mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
210 lines
6.7 KiB
Markdown
210 lines
6.7 KiB
Markdown
# Contributing to Start SDK
|
|
|
|
This guide covers developing the SDK itself. If you're building a service package *using* the SDK, see the [packaging docs](https://docs.start9.com/packaging).
|
|
|
|
For contributing to the broader StartOS project, see the root [CONTRIBUTING.md](../CONTRIBUTING.md).
|
|
|
|
## Prerequisites
|
|
|
|
- **Node.js v22+** (via [nvm](https://github.com/nvm-sh/nvm) recommended)
|
|
- **npm** (ships with Node.js)
|
|
- **GNU Make**
|
|
|
|
Verify your setup:
|
|
|
|
```bash
|
|
node --version # v22.x or higher
|
|
npm --version
|
|
make --version
|
|
```
|
|
|
|
## Repository Layout
|
|
|
|
```
|
|
sdk/
|
|
├── base/ # @start9labs/start-sdk-base (core types, ABI, effects)
|
|
│ ├── lib/ # TypeScript source
|
|
│ ├── package.json
|
|
│ ├── tsconfig.json
|
|
│ └── jest.config.js
|
|
├── package/ # @start9labs/start-sdk (full developer-facing SDK)
|
|
│ ├── lib/ # TypeScript source
|
|
│ ├── package.json
|
|
│ ├── tsconfig.json
|
|
│ └── jest.config.js
|
|
├── baseDist/ # Build output for base (generated)
|
|
├── dist/ # Build output for package (generated, published to npm)
|
|
├── Makefile # Build orchestration
|
|
├── README.md
|
|
├── ARCHITECTURE.md
|
|
└── CLAUDE.md
|
|
```
|
|
|
|
## Getting Started
|
|
|
|
Install dependencies for both sub-packages:
|
|
|
|
```bash
|
|
cd sdk
|
|
make node_modules
|
|
```
|
|
|
|
This runs `npm ci` in both `base/` and `package/`.
|
|
|
|
## Building
|
|
|
|
### Full Build
|
|
|
|
```bash
|
|
make bundle
|
|
```
|
|
|
|
This runs the complete pipeline: TypeScript compilation, hand-written pair copying, node_modules bundling, formatting, and tests. Outputs land in `baseDist/` (base) and `dist/` (package).
|
|
|
|
### Individual Targets
|
|
|
|
| Target | Description |
|
|
|--------|-------------|
|
|
| `make bundle` | Full build: compile + format + test |
|
|
| `make baseDist` | Compile base package only |
|
|
| `make dist` | Compile full package (depends on base) |
|
|
| `make fmt` | Run Prettier on all `.ts` files |
|
|
| `make check` | Type-check without emitting (both packages) |
|
|
| `make clean` | Remove all build artifacts and node_modules |
|
|
|
|
### What the Build Does
|
|
|
|
1. **Peggy parser generation** — `base/lib/exver/exver.pegjs` is compiled to `exver.ts` (the ExVer version parser)
|
|
2. **TypeScript compilation** — Strict mode, CommonJS output, declaration files
|
|
- `base/` compiles to `baseDist/`
|
|
- `package/` compiles to `dist/`
|
|
3. **Hand-written pair copying** — `.js`/`.d.ts` files without a corresponding `.ts` source are copied into the output directories. These are manually maintained JavaScript files with hand-written type declarations.
|
|
4. **Dependency bundling** — `node_modules/` is rsynced into both output directories so the published package is self-contained
|
|
5. **Formatting** — Prettier formats all TypeScript source
|
|
6. **Testing** — Jest runs both test suites
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
# Run all tests (base + package)
|
|
make test
|
|
|
|
# Run base tests only
|
|
make base/test
|
|
|
|
# Run package tests only
|
|
make package/test
|
|
|
|
# Run a specific test file directly
|
|
cd base && npx jest --testPathPattern=exver
|
|
cd package && npx jest --testPathPattern=host
|
|
```
|
|
|
|
Tests use [Jest](https://jestjs.io/) with [ts-jest](https://kulshekhar.github.io/ts-jest/) for TypeScript support. Configuration is in each sub-package's `jest.config.js`.
|
|
|
|
### Test Files
|
|
|
|
Tests live alongside their subjects or in dedicated `test/` directories:
|
|
|
|
- `base/lib/test/` — ExVer parsing, input spec types, deep merge, graph utilities, type validation
|
|
- `base/lib/util/inMs.test.ts` — Time conversion utility
|
|
- `package/lib/test/` — Health checks, host binding, input spec builder
|
|
|
|
Test files use the `.test.ts` extension and are excluded from compilation via `tsconfig.json`.
|
|
|
|
## Formatting
|
|
|
|
```bash
|
|
make fmt
|
|
```
|
|
|
|
Runs Prettier with the project config (single quotes, no semicolons, trailing commas, 2-space indent). The Prettier config lives in each sub-package's `package.json`:
|
|
|
|
```json
|
|
{
|
|
"trailingComma": "all",
|
|
"tabWidth": 2,
|
|
"semi": false,
|
|
"singleQuote": true
|
|
}
|
|
```
|
|
|
|
## Type Checking
|
|
|
|
To check types without building:
|
|
|
|
```bash
|
|
make check
|
|
```
|
|
|
|
Or directly per package:
|
|
|
|
```bash
|
|
cd base && npm run check
|
|
cd package && npm run check
|
|
```
|
|
|
|
Both packages use strict TypeScript (`"strict": true`) targeting ES2021 with CommonJS module output.
|
|
|
|
## Local Development with a Service Package
|
|
|
|
To test SDK changes against a local service package without publishing to npm:
|
|
|
|
```bash
|
|
# Build and create a local npm link
|
|
make link
|
|
|
|
# In your service package directory
|
|
npm link @start9labs/start-sdk
|
|
```
|
|
|
|
This symlinks the built `dist/` into your global node_modules so your service package picks up local changes.
|
|
|
|
## Publishing
|
|
|
|
```bash
|
|
make publish
|
|
```
|
|
|
|
This builds the full bundle, then runs `npm publish --access=public --tag=latest` from `dist/`. The published package is `@start9labs/start-sdk`.
|
|
|
|
Only the `dist/` directory is published — it contains the compiled JavaScript, declaration files, bundled dependencies, and package metadata.
|
|
|
|
## Adding New Features
|
|
|
|
### Base vs Package
|
|
|
|
Decide where new code belongs:
|
|
|
|
- **`base/`** — Types, interfaces, ABI contracts, OS bindings, and low-level builders that have no dependency on the package layer. Code here should be usable independently.
|
|
- **`package/`** — Developer-facing API, convenience wrappers, runtime helpers (daemons, health checks, backups, file helpers, subcontainers). Code here imports from base and adds higher-level abstractions.
|
|
|
|
### Key Conventions
|
|
|
|
- **Builder pattern** — Most APIs use immutable builder chains (`.addDaemon()`, `.mountVolume()`, `.addAction()`). Each call returns a new type that accumulates configuration.
|
|
- **Effects boundary** — All runtime interactions go through the `Effects` interface. Never call system APIs directly.
|
|
- **Manifest type threading** — The manifest type flows through generics so that volume names, image IDs, and dependency IDs are type-constrained.
|
|
- **Re-export from package** — If you add a new export to base, also re-export it from `package/lib/index.ts` (or expose it through `StartSdk.build()`).
|
|
|
|
### Adding OS Bindings
|
|
|
|
Types in `base/lib/osBindings/` mirror Rust types from the StartOS core. When Rust types change, the corresponding TypeScript bindings need updating. These are re-exported through `base/lib/osBindings/index.ts`.
|
|
|
|
### Writing Tests
|
|
|
|
- Place test files next to the code they test, or in the `test/` directory
|
|
- Use the `.test.ts` extension
|
|
- Tests run in Node.js with ts-jest — no browser environment
|
|
|
|
## Commit Messages
|
|
|
|
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
|
|
```
|
|
feat(sdk): add WebSocket health check
|
|
fix(sdk): correct ExVer range parsing for pre-release versions
|
|
test(sdk): add coverage for MultiHost port binding
|
|
```
|
|
|
|
See the root [CONTRIBUTING.md](../CONTRIBUTING.md#commit-messages) for the full convention.
|