From 93e4674a2d148fe37fb0e4a60695e58e8e142d76 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 26 Jul 2026 23:54:09 -0300 Subject: [PATCH 1/8] fix create method --- packages/simulator/src/factory/createSimulator.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/simulator/src/factory/createSimulator.ts b/packages/simulator/src/factory/createSimulator.ts index 415a739..67ffddf 100644 --- a/packages/simulator/src/factory/createSimulator.ts +++ b/packages/simulator/src/factory/createSimulator.ts @@ -199,13 +199,18 @@ export function createSimulator< * @param options - Backend selection, witnesses, private state, live world. * @returns The constructed simulator (subclass-aware via `this`). */ - static async create( + static async create( this: new ( deps: BackendDeps, - ) => T, - contractArgs: TArgs = [] as unknown as TArgs, - options: SimulatorOptions = {}, - ): Promise { + ) => Simulator, + // Permissive so subclasses can override `create` with contract-specific + // params/returns without tripping TS's static-side `extends` check. + ...args: unknown[] + ): Promise { + const [ + contractArgs = [] as unknown as TArgs, + options = {} as SimulatorOptions, + ] = args as [TArgs?, SimulatorOptions?]; const deps = await prepareBackend(contractArgs, options); return new this(deps); } From 9d4281ec1ae658930eb369b8c44e77475fd30bc8 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 26 Jul 2026 23:56:24 -0300 Subject: [PATCH 2/8] move emit to build.json, fold test sims into the types check --- packages/simulator/package.json | 2 +- .../test/integration/Witness.test.ts | 5 +++- packages/simulator/tsconfig.build.json | 20 ++++++++++++++++ packages/simulator/tsconfig.json | 24 ++++++++----------- 4 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 packages/simulator/tsconfig.build.json diff --git a/packages/simulator/package.json b/packages/simulator/package.json index d6e502c..f3fc056 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -34,7 +34,7 @@ "node": ">=22" }, "scripts": { - "build": "tsc -p .", + "build": "tsc -p tsconfig.build.json", "types": "tsc -p tsconfig.json --noEmit", "test": "MIDNIGHT_BACKEND=dry vitest run", "test:live": "MIDNIGHT_BACKEND=live vitest run", diff --git a/packages/simulator/test/integration/Witness.test.ts b/packages/simulator/test/integration/Witness.test.ts index 7696a2f..b65e975 100644 --- a/packages/simulator/test/integration/Witness.test.ts +++ b/packages/simulator/test/integration/Witness.test.ts @@ -11,7 +11,10 @@ const BYTES_OVERRIDE = new Uint8Array(32).fill(1); const FIELD_OVERRIDE = 222n; const UINT_OVERRIDE = 333n; -const overrideWitnesses = (): IWitnessWitnesses => ({ +const overrideWitnesses = (): IWitnessWitnesses< + unknown, + WitnessPrivateState +> => ({ wit_secretBytes(ctx) { return [ctx.privateState, BYTES_OVERRIDE]; }, diff --git a/packages/simulator/tsconfig.build.json b/packages/simulator/tsconfig.build.json new file mode 100644 index 0000000..8b471ce --- /dev/null +++ b/packages/simulator/tsconfig.build.json @@ -0,0 +1,20 @@ +{ + // Emit-only config for the published package. Extends the nodenext base + // (NOT tsconfig.json, which is bundler/noEmit) so `src` is compiled and + // typechecked under the same ESM resolution consumers get. This is what + // enforces correct `.js` import extensions. `yarn build` runs this; the + // comprehensive typecheck lives in `tsconfig.json`. + "extends": "@tsconfig/node24/tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "skipLibCheck": true, + "sourceMap": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/simulator/tsconfig.json b/packages/simulator/tsconfig.json index 6439b36..32fe051 100644 --- a/packages/simulator/tsconfig.json +++ b/packages/simulator/tsconfig.json @@ -1,21 +1,17 @@ { + // Comprehensive typecheck. Uses bundler resolution to match how the + // tests actually resolve under vitest. Never emits: `tsconfig.build.json` owns + // emit and enforces nodenext/ESM correctness on the published `src`. "extends": "@tsconfig/node24/tsconfig.json", "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist", - "declaration": true, + "noEmit": true, + "declaration": false, + "module": "preserve", + "moduleResolution": "bundler", "skipLibCheck": true, - "sourceMap": true, - "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist", - "tests" - ] -} \ No newline at end of file + "include": ["src/**/*", "test/**/*"], + "exclude": ["node_modules", "dist"] +} From ed07aec8ca5fe2f8d9bea4821ab26659eb546e27 Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 27 Jul 2026 23:05:31 -0300 Subject: [PATCH 3/8] revert typecheck refactor --- packages/simulator/package.json | 2 +- packages/simulator/tsconfig.build.json | 20 -------------------- packages/simulator/tsconfig.json | 22 +++++++++++++--------- 3 files changed, 14 insertions(+), 30 deletions(-) delete mode 100644 packages/simulator/tsconfig.build.json diff --git a/packages/simulator/package.json b/packages/simulator/package.json index f3fc056..d6e502c 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -34,7 +34,7 @@ "node": ">=22" }, "scripts": { - "build": "tsc -p tsconfig.build.json", + "build": "tsc -p .", "types": "tsc -p tsconfig.json --noEmit", "test": "MIDNIGHT_BACKEND=dry vitest run", "test:live": "MIDNIGHT_BACKEND=live vitest run", diff --git a/packages/simulator/tsconfig.build.json b/packages/simulator/tsconfig.build.json deleted file mode 100644 index 8b471ce..0000000 --- a/packages/simulator/tsconfig.build.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - // Emit-only config for the published package. Extends the nodenext base - // (NOT tsconfig.json, which is bundler/noEmit) so `src` is compiled and - // typechecked under the same ESM resolution consumers get. This is what - // enforces correct `.js` import extensions. `yarn build` runs this; the - // comprehensive typecheck lives in `tsconfig.json`. - "extends": "@tsconfig/node24/tsconfig.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist", - "declaration": true, - "skipLibCheck": true, - "sourceMap": true, - "rewriteRelativeImportExtensions": true, - "erasableSyntaxOnly": true, - "verbatimModuleSyntax": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/simulator/tsconfig.json b/packages/simulator/tsconfig.json index 32fe051..861ed59 100644 --- a/packages/simulator/tsconfig.json +++ b/packages/simulator/tsconfig.json @@ -1,17 +1,21 @@ { - // Comprehensive typecheck. Uses bundler resolution to match how the - // tests actually resolve under vitest. Never emits: `tsconfig.build.json` owns - // emit and enforces nodenext/ESM correctness on the published `src`. "extends": "@tsconfig/node24/tsconfig.json", "compilerOptions": { - "noEmit": true, - "declaration": false, - "module": "preserve", - "moduleResolution": "bundler", + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, "skipLibCheck": true, + "sourceMap": true, + "rewriteRelativeImportExtensions": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true }, - "include": ["src/**/*", "test/**/*"], - "exclude": ["node_modules", "dist"] + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "tests" + ] } From 1740ef418fc18afb40b67206976f716954d99d4b Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Jul 2026 14:09:01 -0300 Subject: [PATCH 4/8] add _create type sibling --- .../simulator/src/factory/createSimulator.ts | 45 ++++++++++++++----- .../integration/SampleZOwnableSimulator.ts | 4 +- .../test/integration/SimpleSimulator.ts | 4 +- .../test/integration/WitnessSimulator.ts | 4 +- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/packages/simulator/src/factory/createSimulator.ts b/packages/simulator/src/factory/createSimulator.ts index 67ffddf..ea274ea 100644 --- a/packages/simulator/src/factory/createSimulator.ts +++ b/packages/simulator/src/factory/createSimulator.ts @@ -191,26 +191,49 @@ export function createSimulator< } /** - * Constructs a simulator. In dry, deploys from `contractArgs` to fresh - * in-memory state. In live, the caller already deployed; the args seed only - * the local pure-eval context, never an on-chain deploy. + * Public entry point. Permissive params so subclasses can override `create` + * with contract-specific signatures without tripping TS's static-side + * `extends` check. Subclass overrides should assemble the typed args tuple + * and call {@link _create} (`super._create([...args], options)`) so the + * tuple is checked against `TArgs` — delegating back to this permissive + * `create` would silently skip that check. + * + * @param args - `[contractArgs?, options?]`, validated by `_create`. + * @returns The constructed simulator. + */ + static async create( + this: new ( + deps: BackendDeps, + ) => Simulator, + ...args: unknown[] + ): Promise { + const [contractArgs, options] = args as [TArgs?, SimulatorOptions?]; + return (Simulator as unknown as typeof Simulator)._create.call( + Simulator, + contractArgs, + options, + ); + } + + /** + * Typed construction path. In dry, deploys from `contractArgs` to fresh + * in-memory state; in live the caller already deployed and the args seed + * only the local pure-eval context. Subclass `create` overrides call this + * (`super._create([...typedArgs], options)`) so their args tuple is checked + * against `TArgs`. Underscore-public to match the `_backend`/`_signers` + * convention for declaration emit. * * @param contractArgs - Constructor args for the contract. * @param options - Backend selection, witnesses, private state, live world. * @returns The constructed simulator (subclass-aware via `this`). */ - static async create( + static async _create( this: new ( deps: BackendDeps, ) => Simulator, - // Permissive so subclasses can override `create` with contract-specific - // params/returns without tripping TS's static-side `extends` check. - ...args: unknown[] + contractArgs: TArgs = [] as unknown as TArgs, + options: SimulatorOptions = {}, ): Promise { - const [ - contractArgs = [] as unknown as TArgs, - options = {} as SimulatorOptions, - ] = args as [TArgs?, SimulatorOptions?]; const deps = await prepareBackend(contractArgs, options); return new this(deps); } diff --git a/packages/simulator/test/integration/SampleZOwnableSimulator.ts b/packages/simulator/test/integration/SampleZOwnableSimulator.ts index 94be4e0..8cf2589 100644 --- a/packages/simulator/test/integration/SampleZOwnableSimulator.ts +++ b/packages/simulator/test/integration/SampleZOwnableSimulator.ts @@ -52,8 +52,8 @@ export class SampleZOwnableSimulator extends SampleZOwnableSimulatorBase { ReturnType > = {}, ): Promise { - // biome-ignore lint/complexity/noThisInStatic: super.create must keep the subclass `this` - return super.create( + // biome-ignore lint/complexity/noThisInStatic: super._create must keep the subclass `this` + return super._create( [ownerId, instanceSalt], options, ) as Promise; diff --git a/packages/simulator/test/integration/SimpleSimulator.ts b/packages/simulator/test/integration/SimpleSimulator.ts index 6a2bf4c..5c464f1 100644 --- a/packages/simulator/test/integration/SimpleSimulator.ts +++ b/packages/simulator/test/integration/SimpleSimulator.ts @@ -35,8 +35,8 @@ export class SimpleSimulator extends SimpleSimulatorBase { ReturnType > = {}, ): Promise { - // biome-ignore lint/complexity/noThisInStatic: super.create must keep the subclass `this` - return super.create([], options) as Promise; + // biome-ignore lint/complexity/noThisInStatic: super._create must keep the subclass `this` + return super._create([], options) as Promise; } public setVal(n: bigint): Promise<[]> { diff --git a/packages/simulator/test/integration/WitnessSimulator.ts b/packages/simulator/test/integration/WitnessSimulator.ts index 914fbcd..f45b38d 100644 --- a/packages/simulator/test/integration/WitnessSimulator.ts +++ b/packages/simulator/test/integration/WitnessSimulator.ts @@ -44,8 +44,8 @@ export class WitnessSimulator extends WitnessSimulatorBase { ReturnType > = {}, ): Promise { - // biome-ignore lint/complexity/noThisInStatic: super.create must keep the subclass `this` - return super.create([], options) as Promise; + // biome-ignore lint/complexity/noThisInStatic: super._create must keep the subclass `this` + return super._create([], options) as Promise; } public setBytes(): Promise<[]> { From d9fea4fe1e5528fa1cebafb79a1222c983e433cc Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Jul 2026 15:01:37 -0300 Subject: [PATCH 5/8] improve doc on overriding create --- packages/simulator/README.md | 38 ++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/simulator/README.md b/packages/simulator/README.md index c846d79..f289632 100644 --- a/packages/simulator/README.md +++ b/packages/simulator/README.md @@ -83,37 +83,49 @@ This is required because the simulator API expects a factory function for consis ### 2. Extending the Base Simulator -Create your simulator class with a user-friendly API: +Create your simulator class with a user-friendly API by overriding the static +`create`: ```typescript export class MyContractSimulator extends MyContractSimulatorBase { - constructor( + // Override `create` and return your own type. The base `create` returns the + // base `Simulator` (it can't be generic without breaking these overrides), so + // without this a `MyContractSimulator.create(...)` call would resolve to + // `Simulator` and lose the methods below. Delegate to `super._create`, which is + // typed against the contract's constructor args, so a wrong tuple is caught. + static async create( arg1: bigint, arg2: string, - options: BaseSimulatorOptions< + options: SimulatorOptions< MyContractPrivateState, ReturnType - > = {} - ) { - // Bundle args into tuple for parent class - super([arg1, arg2], options); + > = {}, + ): Promise { + return super._create([arg1, arg2], options) as Promise; } - // Wrap contract's circuits with callable methods - public getValue(): bigint { + // Wrap the contract's circuits with callable methods. Every circuit call is + // async. It returns a promise, so callers `await` it. + public getValue(): Promise { return this.circuits.impure.getValue(); } - public setValue(val: bigint): void { - this.circuits.impure.setValue(val); + public setValue(val: bigint): Promise<[]> { + return this.circuits.impure.setValue(val); } - public transfer(to: Uint8Array, amount: bigint): void { - this.circuits.impure.transfer(to, amount); + public transfer(to: Uint8Array, amount: bigint): Promise<[]> { + return this.circuits.impure.transfer(to, amount); } } ``` +> **Every subclass must override the static `create`** and return its own type +> (e.g. `Promise`), delegating to +> `super._create([...args], options)`. This applies even to subclasses that only +> add circuit methods without the override, `MyContractSimulator.create(...)` +> resolves to the base `Simulator` type and callers lose the subclass's methods. + ### 3. Circuit Types #### Pure Circuits From 734256d0ee6630a2bc08af239574b119f21ad693 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Jul 2026 15:27:48 -0300 Subject: [PATCH 6/8] fix return doc in create --- packages/simulator/src/factory/createSimulator.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/simulator/src/factory/createSimulator.ts b/packages/simulator/src/factory/createSimulator.ts index ea274ea..ee83bc9 100644 --- a/packages/simulator/src/factory/createSimulator.ts +++ b/packages/simulator/src/factory/createSimulator.ts @@ -199,7 +199,8 @@ export function createSimulator< * `create` would silently skip that check. * * @param args - `[contractArgs?, options?]`, validated by `_create`. - * @returns The constructed simulator. + * @returns The constructed simulator, typed as the base `Simulator`; subclass + * `create` overrides narrow the return to their own type. */ static async create( this: new ( @@ -208,8 +209,8 @@ export function createSimulator< ...args: unknown[] ): Promise { const [contractArgs, options] = args as [TArgs?, SimulatorOptions?]; - return (Simulator as unknown as typeof Simulator)._create.call( - Simulator, + // biome-ignore lint/complexity/noThisInStatic: keep the caller's `this` so a non-overriding subclass constructs its own type, not the base `Simulator` (the autofix rewrites `this`->`Simulator`, breaking that at runtime). + return (this as unknown as typeof Simulator)._create( contractArgs, options, ); @@ -225,7 +226,9 @@ export function createSimulator< * * @param contractArgs - Constructor args for the contract. * @param options - Backend selection, witnesses, private state, live world. - * @returns The constructed simulator (subclass-aware via `this`). + * @returns The constructed simulator, typed as the base `Simulator`; subclass + * `create` overrides narrow the return to their own type. The runtime + * instance is the caller's class (constructed via `this`). */ static async _create( this: new ( From ac8152b149326b0e2bae936eb514d72f43dabf94 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Jul 2026 15:33:13 -0300 Subject: [PATCH 7/8] add named alias (so it looks intentional) --- .../sample-contracts/witnesses/WitnessWitnesses.ts | 9 +++++++++ packages/simulator/test/integration/Witness.test.ts | 7 ++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts b/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts index 47c8009..1527c9c 100644 --- a/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts +++ b/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts @@ -70,3 +70,12 @@ export const WitnessWitnesses = (): IWitnessWitnesses< ]; }, }); + +/** + * The witness set with the ledger type param `L` erased to `unknown` which is exactly + * what `ReturnType` yields, and what a simulator built + * from this factory uses as its witnesses type. Prefer this alias over spelling + * out `IWitnessWitnesses`, so the `unknown` reads + * as intentional (the erased `L`) rather than arbitrary. + */ +export type WitnessWitnessSet = ReturnType; diff --git a/packages/simulator/test/integration/Witness.test.ts b/packages/simulator/test/integration/Witness.test.ts index b65e975..5f9a1cd 100644 --- a/packages/simulator/test/integration/Witness.test.ts +++ b/packages/simulator/test/integration/Witness.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import type { - IWitnessWitnesses, WitnessPrivateState, + WitnessWitnessSet, } from '../fixtures/sample-contracts/witnesses/WitnessWitnesses'; import { WitnessSimulator } from './WitnessSimulator'; @@ -11,10 +11,7 @@ const BYTES_OVERRIDE = new Uint8Array(32).fill(1); const FIELD_OVERRIDE = 222n; const UINT_OVERRIDE = 333n; -const overrideWitnesses = (): IWitnessWitnesses< - unknown, - WitnessPrivateState -> => ({ +const overrideWitnesses = (): WitnessWitnessSet => ({ wit_secretBytes(ctx) { return [ctx.privateState, BYTES_OVERRIDE]; }, From 636322fae60e7a37aedc96878517c0a45fc5525c Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Jul 2026 16:36:33 -0300 Subject: [PATCH 8/8] add regression guard --- packages/simulator/package.json | 2 +- .../src/create-contract.type-test.ts | 71 +++++++++++++++++++ packages/simulator/tsconfig.json | 3 +- packages/simulator/tsconfig.types.json | 14 ++++ turbo.json | 3 +- 5 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 packages/simulator/src/create-contract.type-test.ts create mode 100644 packages/simulator/tsconfig.types.json diff --git a/packages/simulator/package.json b/packages/simulator/package.json index d6e502c..56e8260 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -35,7 +35,7 @@ }, "scripts": { "build": "tsc -p .", - "types": "tsc -p tsconfig.json --noEmit", + "types": "tsc -p tsconfig.types.json", "test": "MIDNIGHT_BACKEND=dry vitest run", "test:live": "MIDNIGHT_BACKEND=live vitest run", "clean": "git clean -fXd" diff --git a/packages/simulator/src/create-contract.type-test.ts b/packages/simulator/src/create-contract.type-test.ts new file mode 100644 index 0000000..5fc138a --- /dev/null +++ b/packages/simulator/src/create-contract.type-test.ts @@ -0,0 +1,71 @@ +/** + * Type-level regression guard for the static `create` / `_create` contract. + * + * `yarn types` compiles `src/**` but NOT `test/**` (the sample simulators import + * generated, gitignored contract artifacts), and `vitest` strips types without + * checking them — so a regression in the `create` / `_create` typing would + * otherwise pass CI. This file reproduces the subclass-override pattern against a + * synthetic contract type, so `yarn types` fails on a regression. + * + * It exports nothing and is imported by nothing (inert at runtime). The build + * config (`tsconfig.json`) excludes `*.type-test.ts` from emit, so it never + * reaches `dist`; the type check runs it via `tsconfig.types.json`. + * + * Typechecking the real test simulators (which need the generated artifacts) is a + * separate, larger effort; this is the minimal guard for the contract that + * actually regressed. + */ +import { createSimulator } from './factory/createSimulator.js'; +import type { SimulatorConfig } from './factory/SimulatorConfig.js'; +import type { IMinimalContract } from './types/Contract.js'; + +type GuardPrivateState = { readonly value: number }; +type GuardArgs = readonly [a: number, b: string]; + +// A synthetic config — never executed, only used to instantiate the factory's +// generics for the type-level checks below. +const config = {} as unknown as SimulatorConfig< + GuardPrivateState, + unknown, + unknown, + IMinimalContract, + GuardArgs +>; + +class Guard extends createSimulator(config) { + // A concrete `Promise` return must stay assignable to the base static + // side. If `create`'s return goes generic again, this fails the static-side + // `extends` check (TS2417). + static async create(a: number, b: string, options = {}): Promise { + // biome-ignore lint/complexity/noThisInStatic: super._create must keep the subclass `this` + return super._create([a, b], options) as Promise; + } + + // `_create` must type its args tuple against `GuardArgs`. If that check + // regresses (e.g. `_create` widens to `...args: unknown[]`), the wrong-arity + // call below stops erroring and the `@ts-expect-error` becomes unused → CI red. + static async _argCheck(): Promise { + // Call via the class name (not `super`) so this negative assertion needs + // only the `@ts-expect-error` below — no `noThisInStatic` ignore to stack. + // @ts-expect-error a 1-element tuple must not satisfy `[number, string]`. + return Guard._create([1], {}); + } + + // A subclass-only member. Without it, `Guard`'s instance type would equal the + // base `Simulator`'s and the call-site check below would pass even if `create` + // stopped narrowing to the subclass. This mirrors real subclasses, which add + // circuit methods that a base-typed return would hide. + public marker(): string { + return 'guard'; + } +} + +// The override must narrow the call-site return to the subclass, not widen to +// the base `Simulator` (which lacks `marker`). +async function _callSiteSubtype(): Promise { + const instance: Guard = await Guard.create(1, 'x'); + void instance.marker(); +} + +void Guard; +void _callSiteSubtype; diff --git a/packages/simulator/tsconfig.json b/packages/simulator/tsconfig.json index 861ed59..dc6a948 100644 --- a/packages/simulator/tsconfig.json +++ b/packages/simulator/tsconfig.json @@ -16,6 +16,7 @@ "exclude": [ "node_modules", "dist", - "tests" + "tests", + "src/**/*.type-test.ts" ] } diff --git a/packages/simulator/tsconfig.types.json b/packages/simulator/tsconfig.types.json new file mode 100644 index 0000000..2728f6e --- /dev/null +++ b/packages/simulator/tsconfig.types.json @@ -0,0 +1,14 @@ +{ + // Typecheck-only project used by `yarn types`. + // + // Identical to the build config (`tsconfig.json`) except it (a) never emits and + // (b) re-includes the `*.type-test.ts` regression guards, which the build config + // excludes from emit so they don't ship in `dist`. Keeping them in the type + // check is the whole point: they fail CI if the guarded contracts regress. + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"] +} diff --git a/turbo.json b/turbo.json index 08dd369..aac4177 100644 --- a/turbo.json +++ b/turbo.json @@ -19,6 +19,7 @@ "inputs": [ "src/**/*.ts", "!src/**/*.test.ts", + "!src/**/*.type-test.ts", "tsconfig.json", "tsconfig.build.json", ".env" @@ -27,7 +28,7 @@ }, "types": { "dependsOn": ["^build"], - "inputs": ["src/**/*.ts", "tsconfig.json"], + "inputs": ["src/**/*.ts", "tsconfig.json", "tsconfig.types.json"], "outputs": [] }, "lint": {},