Skip to main content
The TypeScript runner | ESSThe files ess writes for a TypeScript conformance package, the Target interface an implementation provides, and how to run it with npm.ESSreferenceessreferenceadopterdevelopertry-spec-driven-developmentreference

The TypeScript runner

ess verify conform synthesize --target typescript writes the suite a specification obliges as an ESM test package that node --test runs. Run your first conformance suite walks through one; this page lists what the package holds and what your implementation provides. It needs Node.js 20 or later.

What ess writes​

From the tasks project of the earlier pages:

$ cd ~/tasks
$ ess verify conform synthesize --path . --target typescript --out typescript
note: every declared actor may invoke `tasks.list.AddTask`, so no actor is refused it and no `tasks.list.AddTask/grant/denied` scenario is owed
note: every declared actor may invoke `tasks.list.CompleteTask`, so no actor is refused it and no `tasks.list.CompleteTask/grant/denied` scenario is owed
6 scenario(s) (0 authored), 0 refusal(s), 15 file(s) written to typescript

The package is essconform, one directory below --out:

README.md
ir.json
package.json
src/coordinate.ts
src/direct_response.ts
src/explore.ts
src/fixtures.ts
src/index.ts
src/one_time_response.ts
src/predicate.ts
src/reading.ts
src/response.ts
src/runtime.ts
suite.json
tsconfig.json
FileWhat it is
suite.jsonthe suite: one scenario per outcome, lifecycle move and refusal
ir.jsonthe compiled specification, which the explorer interprets as its reference model
src/runtime.tsthe runner: run, runWith, the Target interface and its request and result types
src/predicate.ts, src/response.ts, src/reading.ts, src/coordinate.ts, src/fixtures.tswhat the runner asserts with: guards, returned values, clock readings and supplied values
src/direct_response.ts, src/one_time_response.tstyped direct responses and one-time response disclosure checks
src/explore.tsexplore and exploreConcurrent, seeded random command sequences
src/index.tsthe entry point your test imports from
package.json, tsconfig.jsonthe package: npm test compiles src/ into dist/ and runs dist/*.test.js
README.mdthe wiring for this suite, and the report format it requires

Beside the package, typescript/.ess-output/state.json records which files ess wrote there. Nothing in essconform is edited by hand; a file you add, such as src/conformance.test.ts, is kept when you regenerate.

The package has no runtime dependencies. Before you add anything to it, it installs and typechecks without a finding:

$ cd typescript/essconform
$ npm install
…
$ npm run --silent typecheck

What your implementation provides​

One object satisfying Target, exactly as src/runtime.ts declares it. A method marked ? is optional:

export interface Target {
fixtureValues?(
scenario: ScenarioContext,
contract: FixtureContract,
): Answer<Record<string, Node>>;
identity(): Answer<Identity>;
beginScenario(scenario: ScenarioContext): Answer<void>;
endScenario(scenario: ScenarioContext): Answer<void>;
executeCommand(request: CommandRequest): Answer<CommandResult>;
awaitOutcome?(request: AwaitRequest): Answer<AwaitAnswer>;
executeCommandWithoutInput?(request: AbsentInputRequest): Answer<CommandResult>;
queryView(request: ViewRequest): Answer<ViewResult>;
observeEvents(request: EventObservationRequest): Answer<ObservedEvent[]>;
configureExternalOutcome(control: ExternalOutcomeControl): Answer<void>;
configureExternalOutcomeRepeatedly?(
control: ExternalOutcomeControl & { times: number },
): Answer<void>;
redeliverEvent(request: RedeliveryRequest): Answer<void>;
deliverEvent?(request: EventDeliveryRequest): Answer<void>;
observeInvocations(request: InvocationObservationRequest): Answer<Invocation[]>;
}
MethodWhat it answers
identitythe implementation's name and version, for the report
beginScenario, endScenariobracket one scenario; state from one scenario must not satisfy another
executeCommandrun one command; return the outcome taken, the declared error if it refused, and the events it emitted directly
awaitOutcomefor a command every actor of which is declared system: true, which is never sent: the outcome the system reached on the requested instance, the input it issued the command with and what it published, or {reached: false}; hold the system's step until the runner awaits it, and report your clock's reading in elapsedMillis where the request carries within
queryViewread one view; a read_your_writes view must already show the command that just returned
observeEventsthe events seen for one activity, away from the command that caused them
configureExternalOutcomeforce the next answer of an outcome declared external:
redeliverEventdeliver an event a second time, for delivery: at_least_once
observeInvocationsthe commands one binding invoked and what it passed

A method the implementation cannot answer throws ErrUnsupported. Report/2 records that scenario as unsupported, and the default strict run fails. Ordinary target errors are recorded separately as error and make execution inconclusive. A specification that declares backend setup, periodic hosts or clock readings asks for a further interface beside Target (EntitySetupTarget, PeriodicTarget, ClockReadingTarget); the generated README.md names the ones its suite needs.

Run it​

Hand a factory to run from one test file under src/; the runner builds one target per scenario:

import { test } from "node:test";
import { run } from "./index.js";
import type { Target } from "./index.js";

await test("conformance", async (t) => {
await run(t, (): Target => newTarget());
});

Then run npm test with ESS_REPORT_FORMAT=2 set; ESS_REPORT_OUT names a file for the report:

$ ESS_REPORT_FORMAT=2 ESS_REPORT_OUT=report.json npm test

Verify conformance covers authored scenarios, coverage and the report in full.