Files
start-os/sdk/CONTRIBUTING.md

6.7 KiB

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.

For contributing to the broader StartOS project, see the root CONTRIBUTING.md.

Prerequisites

  • Node.js v22+ (via nvm recommended)
  • npm (ships with Node.js)
  • GNU Make

Verify your setup:

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:

cd sdk
make node_modules

This runs npm ci in both base/ and package/.

Building

Full Build

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 generationbase/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 bundlingnode_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

# 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 with 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

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:

{
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true
}

Type Checking

To check types without building:

make check

Or directly per package:

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:

# 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

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:

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 for the full convention.