Files
start-os/sdk/CONTRIBUTING.md

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.