|
| 1 | +# Spacecraft TypeScript Guidelines — Full Reference |
| 2 | + |
| 3 | +**Version:** 1.0 |
| 4 | +**Date:** 2026-07-12 |
| 5 | +**Author:** Mohamed Hammad & Spacecraft Software |
| 6 | +**Compatibility:** Claude 3.5+, Claude 4, Grok, and all advanced reasoning models |
| 7 | + |
| 8 | +This document expands on the `SKILL.md` for TypeScript 7.0+ (Go native compiler) systems programming. It provides complete, compile-checked configurations and skeletons for monorepo project references, Piscina worker threadpools, Zod boundary validation, and unit tests. |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## 1. Project References & Incremental Configuration (TS 7.0) |
| 13 | + |
| 14 | +To scale type-checking with the Go compiler, divide the project into references. The compiler compiles upstream projects and caches output declaration files. |
| 15 | + |
| 16 | +### Root Configuration (`tsconfig.json`) |
| 17 | +```json |
| 18 | +{ |
| 19 | + "compilerOptions": { |
| 20 | + "target": "ES2022", |
| 21 | + "module": "NodeNext", |
| 22 | + "moduleResolution": "NodeNext", |
| 23 | + "strict": true, |
| 24 | + "composite": true, |
| 25 | + "incremental": true, |
| 26 | + "declaration": true, |
| 27 | + "declarationMap": true, |
| 28 | + "noEmitOnError": true, |
| 29 | + "skipLibCheck": true |
| 30 | + }, |
| 31 | + "files": [], |
| 32 | + "references": [ |
| 33 | + { "path": "./packages/core" }, |
| 34 | + { "path": "./packages/api" } |
| 35 | + ] |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +### Core Package Configuration (`packages/core/tsconfig.json`) |
| 40 | +```json |
| 41 | +{ |
| 42 | + "extends": "../../tsconfig.json", |
| 43 | + "compilerOptions": { |
| 44 | + "outDir": "./dist", |
| 45 | + "tsBuildInfoFile": "./dist/.tsbuildinfo" |
| 46 | + }, |
| 47 | + "include": ["src/**/*"] |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +### API Package Configuration (`packages/api/tsconfig.json`) |
| 52 | +```json |
| 53 | +{ |
| 54 | + "extends": "../../tsconfig.json", |
| 55 | + "compilerOptions": { |
| 56 | + "outDir": "./dist", |
| 57 | + "tsBuildInfoFile": "./dist/.tsbuildinfo" |
| 58 | + }, |
| 59 | + "include": ["src/**/*"], |
| 60 | + "references": [ |
| 61 | + { "path": "../core" } |
| 62 | + ] |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +--- |
| 67 | + |
| 68 | +## 2. Piscina Worker Threads Skeleton (CPU Parallelism) |
| 69 | + |
| 70 | +Do not block the single-threaded event loop with expensive calculations. Offload them to background worker threads using `piscina`. |
| 71 | + |
| 72 | +### Parent Service (`src/telemetry-service.ts`) |
| 73 | +```typescript |
| 74 | +import { Piscina } from 'piscina'; |
| 75 | +import * as path from 'path'; |
| 76 | + |
| 77 | +export interface ComputeRequest { |
| 78 | + data: Float64Array; |
| 79 | +} |
| 80 | + |
| 81 | +export interface ComputeResponse { |
| 82 | + sum: number; |
| 83 | +} |
| 84 | + |
| 85 | +const workerPool = new Piscina({ |
| 86 | + filename: path.resolve(__dirname, 'worker.js'), |
| 87 | + minThreads: 2, |
| 88 | + maxThreads: 8 |
| 89 | +}); |
| 90 | + |
| 91 | +export async function processTelemetryParallel(data: Float64Array): Promise<number> { |
| 92 | + if (data.length < 5000) { |
| 93 | + // Fall back to serial sum to avoid thread communication overhead |
| 94 | + return data.reduce((acc, val) => acc + val, 0); |
| 95 | + } |
| 96 | + |
| 97 | + const request: ComputeRequest = { data }; |
| 98 | + const response: ComputeResponse = await workerPool.run(request); |
| 99 | + return response.sum; |
| 100 | +} |
| 101 | +``` |
| 102 | + |
| 103 | +### Worker Script (`src/worker.ts`) |
| 104 | +```typescript |
| 105 | +import { ComputeRequest, ComputeResponse } from './telemetry-service'; |
| 106 | + |
| 107 | +export default function (request: ComputeRequest): ComputeResponse { |
| 108 | + const { data } = request; |
| 109 | + let sum = 0; |
| 110 | + for (let i = 0; i < data.length; i++) { |
| 111 | + sum += data[i]!; |
| 112 | + } |
| 113 | + return { sum }; |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +## 3. Zod Boundary Schema Validation |
| 120 | + |
| 121 | +Validate all untrusted external data (network payloads, filesystem inputs) before casting it to internal TypeScript interfaces. |
| 122 | + |
| 123 | +```typescript |
| 124 | +import { z } from 'zod'; |
| 125 | + |
| 126 | +// Define schema (contains runtime check logic) |
| 127 | +export const SensorReadingSchema = z.object({ |
| 128 | + sensorId: z.string().min(1), |
| 129 | + temperature: z.number(), |
| 130 | + timestamp: z.string().datetime(), // ISO 8601 UTC |
| 131 | + status: z.union([z.literal('ok'), z.literal('error')]) |
| 132 | +}); |
| 133 | + |
| 134 | +// Infer TypeScript interface automatically |
| 135 | +export type SensorReading = z.infer<typeof SensorReadingSchema>; |
| 136 | + |
| 137 | +export function parseIncomingTelemetry(payload: string): Result<SensorReading, Error> { |
| 138 | + try { |
| 139 | + const rawData = JSON.parse(payload); |
| 140 | + // parse returns the validated object or throws an error |
| 141 | + const validated = SensorReadingSchema.parse(rawData); |
| 142 | + return { success: true, data: validated }; |
| 143 | + } catch (err) { |
| 144 | + return { success: false, error: err as Error }; |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | +type Result<T, E> = |
| 149 | + | { success: true; data: T } |
| 150 | + | { success: false; error: E }; |
| 151 | +``` |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## 4. Discriminated Union Exhaustiveness (never assertion) |
| 156 | + |
| 157 | +Ensure that all code paths are handled completely. If a new variant is added to a union, the compiler must fail to build. |
| 158 | + |
| 159 | +```typescript |
| 160 | +export type Command = |
| 161 | + | { type: 'start'; delay: number } |
| 162 | + | { type: 'stop' } |
| 163 | + | { type: 'restart'; clean: boolean }; |
| 164 | + |
| 165 | +export function assertNever(x: never): never { |
| 166 | + throw new Error(`Exhaustiveness check failed: unexpected object ${JSON.stringify(x)}`); |
| 167 | +} |
| 168 | + |
| 169 | +export function executeCommand(cmd: Command): void { |
| 170 | + switch (cmd.type) { |
| 171 | + case 'start': |
| 172 | + console.log(`Starting telemetry, delay: ${cmd.delay}`); |
| 173 | + break; |
| 174 | + case 'stop': |
| 175 | + console.log('Stopping telemetry'); |
| 176 | + break; |
| 177 | + case 'restart': |
| 178 | + console.log(`Restarting telemetry, clean: ${cmd.clean}`); |
| 179 | + break; |
| 180 | + default: |
| 181 | + // If a new variant is added and not handled in the switch, |
| 182 | + // the compiler will throw a type error because the type of 'cmd' is not 'never' |
| 183 | + assertNever(cmd); |
| 184 | + } |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +## 5. Testing: Vitest & fast-check |
| 191 | + |
| 192 | +Test suites must check happy paths, errors, and invariants. Use `fast-check` to execute property-based tests. |
| 193 | + |
| 194 | +```typescript |
| 195 | +// test/telemetry.test.ts |
| 196 | +import { describe, it, expect } from 'vitest'; |
| 197 | +import * as fc from 'fast-check'; |
| 198 | +import { executeCommand, Command } from '../src/commands'; |
| 199 | + |
| 200 | +describe('Telemetry Commands', () => { |
| 201 | + it('should parse and process commands cleanly', () => { |
| 202 | + const cmd: Command = { type: 'stop' }; |
| 203 | + expect(() => executeCommand(cmd)).not.toThrow(); |
| 204 | + }); |
| 205 | + |
| 206 | + it('should satisfy list-reversing invariants', () => { |
| 207 | + fc.assert( |
| 208 | + fc.property(fc.array(fc.integer()), (arr) => { |
| 209 | + const rev = [...arr].reverse(); |
| 210 | + const doubleRev = [...rev].reverse(); |
| 211 | + expect(doubleRev).toEqual(arr); |
| 212 | + }) |
| 213 | + ); |
| 214 | + }); |
| 215 | +}); |
| 216 | +``` |
| 217 | + |
| 218 | +--- |
| 219 | + |
| 220 | +## 6. Common Pitfalls & Troubleshooting |
| 221 | + |
| 222 | +| Pitfall | Symptom | Corrective Action | |
| 223 | +| :--- | :--- | :--- | |
| 224 | +| **`any` / `as any` overrides** | Runtime type errors and crashes | Enable `strict: true` and replace `any` with `unknown` type guards. | |
| 225 | +| **Dynamic property assignments** | V8 engine de-optimization (slow loop) | Initialize all properties inside the class constructor or object literal up front. | |
| 226 | +| **Blocking main event loop** | Lagging request handlers or web socket drops | Offload heavy CPU-bound algorithms to a `Piscina` worker pool. | |
| 227 | +| **Programmatic tsc API calls** | Webpack or ESLint errors on TS 7.0 | Install `@typescript/typescript6` and utilize its shims for programmatic tooling. | |
| 228 | +| **Raw worker thread loops** | Out of memory / process limits | Use persistent worker threadpools (`Piscina`) instead of spawning raw workers on-demand. | |
| 229 | +| **Silent parser errors** | Missing type fields crash downstream code | Parse network JSON inputs immediately using `Zod` schema models. | |
| 230 | + |
| 231 | +--- |
| 232 | + |
| 233 | +## 7. Code Review Compliance Gate |
| 234 | + |
| 235 | +Before merging TypeScript code, verify: |
| 236 | +1. `strict: true` and strict type checks are enabled in `tsconfig.json`. |
| 237 | +2. No `any` casting or `!` assertions are present in production code paths. |
| 238 | +3. Every external data ingress point is bound by a Zod schema validation. |
| 239 | +4. Hot objects maintain stable hidden classes (no dynamic property additions/deletions). |
| 240 | +5. Monorepo dependencies are referenced cleanly with `composite: true`. |
0 commit comments