@jterrazz/test 15.2.0 → 15.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,119 +1,7 @@
1
1
  import { Scalar } from "yaml";
2
- import { DeepMockProxy } from "vitest-mock-extended";
2
+ import { DeepMockProxy, MockProxy } from "vitest-mock-extended";
3
3
  import { ReactNode as ComponentUi } from "react";
4
4
  import "vitest/browser";
5
- //#region src/specification/matching/match.d.ts
6
- /**
7
- * Code-side dynamic-value matchers (`match.*`) and the capture scope shared
8
- * by every assertion of a single spec execution.
9
- *
10
- * Matchers are used inside `toMatchRows` rows and any structural comparison;
11
- * their file-side twins are the `{{placeholder}}` forms in `_expected/*.http`
12
- * fixtures (body AND headers), `_expected/*.json` files, and text snapshots
13
- * (`_expected/*.txt`). One grammar, one vocabulary (CONVENTIONS D4).
14
- */
15
- /** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
16
- type MatcherKind = 'any' | 'base64' | 'date' | 'duration' | 'email' | 'float' | 'hex' | 'includes' | 'int' | 'ip' | 'iso8601' | 'number' | 'path' | 'port' | 'ref' | 'regex' | 'semver' | 'sha' | 'string' | 'time' | 'ulid' | 'url' | 'uuid' | 'workdir';
17
- /** Every kind usable as a `{{token}}` in fixture files (all but ref/regex/includes). */
18
- declare const TOKEN_KINDS: readonly ["any", "base64", "date", "duration", "email", "float", "hex", "int", "ip", "iso8601", "number", "path", "port", "semver", "sha", "string", "time", "ulid", "url", "uuid", "workdir"];
19
- /** A token kind valid inside a fixture `{{...}}` placeholder. */
20
- type TokenKind = (typeof TOKEN_KINDS)[number];
21
- /**
22
- * A dynamic-value matcher. Created via the {@link match} factories — never
23
- * constructed directly by user code.
24
- */
25
- declare class Matcher {
26
- /** For kind 'includes': the substring the actual value must contain. */
27
- readonly includes?: string | undefined;
28
- readonly kind: MatcherKind;
29
- /** For kind 'ref': the capture to assert inequality against. */
30
- readonly notRef?: string | undefined;
31
- /** For kind 'ref': the capture name. */
32
- readonly refName?: string | undefined;
33
- /** For kind 'regex': the pattern the actual value must match. */
34
- readonly regex?: RegExp | undefined;
35
- constructor(kind: MatcherKind, options?: {
36
- includes?: string | undefined;
37
- notRef?: string | undefined;
38
- refName?: string | undefined;
39
- regex?: RegExp | undefined;
40
- });
41
- /** Placeholder-style rendering used in failure diffs and serialization. */
42
- toString(): string;
43
- toJSON(): string;
44
- }
45
- /** A no-argument matcher factory — what every typed entry of `match` is. */
46
- type TypedMatcher = () => Matcher;
47
- /**
48
- * Dynamic-value matchers for structural comparisons — the code-side mirror of
49
- * the `{{token}}` fixture grammar (CONVENTIONS D4).
50
- *
51
- * @example
52
- * await expect(result.table('users', { database: 'db' })).toMatchRows({
53
- * columns: ['id', 'name'],
54
- * rows: [[match.uuid(), 'Alice']],
55
- * });
56
- */
57
- /**
58
- * The matcher roster: one no-argument factory per token kind, plus the three
59
- * code-only forms the `{{token}}` grammar does not carry.
60
- */
61
- type MatchFactories = Record<TokenKind, TypedMatcher> & {
62
- includes: (substring: string) => Matcher;
63
- ref: (name: string, options?: {
64
- not?: string;
65
- }) => Matcher;
66
- regex: (regex: RegExp) => Matcher;
67
- };
68
- declare const match: MatchFactories;
69
- /**
70
- * Named captures recorded by `match.ref()` / `{{type#ref}}` placeholders.
71
- * One scope lives on each spec result — every assertion chained off the same
72
- * result shares it, and a new chain starts fresh.
73
- */
74
- declare class CaptureScope {
75
- private readonly values;
76
- /**
77
- * The exact working directory of the current spec, when the framework
78
- * knows it (cli mode). Drives the `{{workdir}}` token / `match.workdir()`.
79
- */
80
- workdir?: string | undefined;
81
- constructor(workdir?: string);
82
- get(name: string): unknown;
83
- has(name: string): boolean;
84
- set(name: string, value: unknown): void;
85
- }
86
- //#endregion
87
- //#region src/specification/facets/_common/result/json.d.ts
88
- /**
89
- * Read-only accessor for a JSON payload parsed from a text stream (stdout).
90
- *
91
- * Lazily parses the JSON on first use; parse errors are deferred until the
92
- * value is read so that callers can still read the raw stream text without
93
- * triggering a throw.
94
- *
95
- * ANSI escape sequences are stripped by default before `JSON.parse`
96
- * (CONVENTIONS D6). When a `transform` is configured on the spec runner, it
97
- * runs on the stripped text — an escape hatch for application noise not
98
- * covered by the `{{token}}` grammar.
99
- *
100
- * Assertions go through `expect()`: `expect(result.json).toMatch('name.json')`
101
- * (deep-equal against `_expected/<name>`, with `{{placeholder}}` support).
102
- */
103
- declare class JsonAccessor {
104
- /** @internal Ref-capture scope shared by the current spec execution. */
105
- readonly captures: CaptureScope;
106
- /** @internal */
107
- private readonly rawText;
108
- /** @internal Test-file directory — fixture resolution root for matchers. */
109
- readonly testDir: string;
110
- /** @internal */
111
- private readonly transform?;
112
- constructor(rawText: string, testDir: string, transform?: (text: string) => string, captures?: CaptureScope);
113
- /** The parsed JSON value. Throws if the text is not valid JSON. */
114
- get value(): unknown;
115
- }
116
- //#endregion
117
5
  //#region src/specification/contracts/types.d.ts
118
6
  /**
119
7
  * The observed outgoing request, reduced to what contract matchers inspect.
@@ -157,7 +45,11 @@ type ContractRequest = {
157
45
  type ContractResponse = {
158
46
  /** HTTP status code (default: 200). */
159
47
  status?: number;
160
- /** Response body — an object is JSON, a string is text, `null`/`undefined` is empty. */
48
+ /**
49
+ * Response body — an object is JSON, a string is text, `null`/`undefined`
50
+ * is empty, and a {@link StreamBody} (built by `http.stream()` /
51
+ * `http.sse()`) arrives in pieces.
52
+ */
161
53
  body: unknown;
162
54
  /** Response headers. */
163
55
  headers?: Record<string, string> | undefined;
@@ -174,6 +66,31 @@ type ContractResponse = {
174
66
  */
175
67
  transport?: 'network-error' | undefined;
176
68
  };
69
+ /**
70
+ * The marker that tells a STREAMED body from a value that happens to be an
71
+ * object. A body reaches three different engines (msw's server, msw's worker,
72
+ * the `node:http` stub) and each of them serialises "an object" as JSON, so a
73
+ * stream declared as a plain value arrives as `{}`. The tag is what each
74
+ * engine recognises before it reaches for `JSON.stringify`.
75
+ */
76
+ declare const STREAM_BODY = "@jterrazz/test:stream";
77
+ /**
78
+ * A body that arrives in pieces — what `http.stream()` and `http.sse()` build.
79
+ *
80
+ * The chunks are written in order, `delayBetweenChunks` milliseconds apart,
81
+ * and the connection closes after the last one. It is the chunks and their
82
+ * ORDER that a streaming subject is specified against: a client that renders
83
+ * tokens as they land, or one that reconnects on a half-read body.
84
+ */
85
+ type StreamBody = {
86
+ /** The pieces, written in order. */
87
+ chunks: readonly string[];
88
+ /** The `content-type` the stream is served under. */
89
+ contentType: string;
90
+ /** Milliseconds between two chunks. Default 0 — everything at once. */
91
+ delayBetweenChunks: number;
92
+ kind: typeof STREAM_BODY;
93
+ };
177
94
  /**
178
95
  * A dynamic response: computed from the observed request at the moment the
179
96
  * contract is served, rather than fixed ahead of time. Handed the same
@@ -272,6 +189,144 @@ declare function defineContract(contract: Contract): Contract;
272
189
  */
273
190
  declare function defineContracts(...items: ContractInput[]): Contracts;
274
191
  //#endregion
192
+ //#region src/integrations/msw/scope.d.ts
193
+ /**
194
+ * One block's worth of declared network. Disposed at the end of the scope that
195
+ * declared it: the handlers are dropped, and an unmatched request is thrown.
196
+ */
197
+ type InterceptScope = AsyncDisposable;
198
+ /**
199
+ * The module-scope network double. Resolves once the engine is listening —
200
+ * which is why it is a promise, and why the canonical form awaits it:
201
+ *
202
+ * ```typescript
203
+ * await using _ = await intercept(http.get(url), http.json({ ok: true }));
204
+ * ```
205
+ *
206
+ * Without that `await` the subject could reach the real network before the
207
+ * first handler is in place, so the shape refuses the racy spelling: a promise
208
+ * is not an `AsyncDisposable`, and the compiler says so.
209
+ */
210
+ type Intercept = ((contracts: ContractInput) => Promise<InterceptScope>) & ((request: ContractRequest, response: ContractResponder | ContractResponse) => Promise<InterceptScope>);
211
+ //#endregion
212
+ //#region src/specification/matching/match.d.ts
213
+ /**
214
+ * Code-side dynamic-value matchers (`match.*`) and the capture scope shared
215
+ * by every assertion of a single spec execution.
216
+ *
217
+ * Matchers are used inside `toMatchRows` rows and any structural comparison;
218
+ * their file-side twins are the `{{placeholder}}` forms in `_expected/*.http`
219
+ * fixtures (body AND headers), `_expected/*.json` files, and text snapshots
220
+ * (`_expected/*.txt`). One grammar, one vocabulary (CONVENTIONS D4).
221
+ */
222
+ /** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
223
+ type MatcherKind = 'any' | 'base64' | 'date' | 'duration' | 'email' | 'float' | 'hex' | 'includes' | 'int' | 'ip' | 'iso8601' | 'number' | 'path' | 'port' | 'ref' | 'regex' | 'semver' | 'sha' | 'string' | 'time' | 'ulid' | 'url' | 'uuid' | 'workdir';
224
+ /** Every kind usable as a `{{token}}` in fixture files (all but ref/regex/includes). */
225
+ declare const TOKEN_KINDS: readonly ["any", "base64", "date", "duration", "email", "float", "hex", "int", "ip", "iso8601", "number", "path", "port", "semver", "sha", "string", "time", "ulid", "url", "uuid", "workdir"];
226
+ /** A token kind valid inside a fixture `{{...}}` placeholder. */
227
+ type TokenKind = (typeof TOKEN_KINDS)[number];
228
+ /**
229
+ * A dynamic-value matcher. Created via the {@link match} factories — never
230
+ * constructed directly by user code.
231
+ */
232
+ declare class Matcher {
233
+ /** For kind 'includes': the substring the actual value must contain. */
234
+ readonly includes?: string | undefined;
235
+ readonly kind: MatcherKind;
236
+ /** For kind 'ref': the capture to assert inequality against. */
237
+ readonly notRef?: string | undefined;
238
+ /** For kind 'ref': the capture name. */
239
+ readonly refName?: string | undefined;
240
+ /** For kind 'regex': the pattern the actual value must match. */
241
+ readonly regex?: RegExp | undefined;
242
+ constructor(kind: MatcherKind, options?: {
243
+ includes?: string | undefined;
244
+ notRef?: string | undefined;
245
+ refName?: string | undefined;
246
+ regex?: RegExp | undefined;
247
+ });
248
+ /** Placeholder-style rendering used in failure diffs and serialization. */
249
+ toString(): string;
250
+ toJSON(): string;
251
+ }
252
+ /** A no-argument matcher factory — what every typed entry of `match` is. */
253
+ type TypedMatcher = () => Matcher;
254
+ /**
255
+ * Dynamic-value matchers for structural comparisons — the code-side mirror of
256
+ * the `{{token}}` fixture grammar (CONVENTIONS D4).
257
+ *
258
+ * @example
259
+ * await expect(result.table('users', { database: 'db' })).toMatchRows({
260
+ * columns: ['id', 'name'],
261
+ * rows: [[match.uuid(), 'Alice']],
262
+ * });
263
+ */
264
+ /**
265
+ * The matcher roster: one no-argument factory per token kind, plus the three
266
+ * code-only forms the `{{token}}` grammar does not carry.
267
+ */
268
+ type MatchFactories = Record<TokenKind, TypedMatcher> & {
269
+ includes: (substring: string) => Matcher;
270
+ ref: (name: string, options?: {
271
+ not?: string;
272
+ }) => Matcher;
273
+ regex: (regex: RegExp) => Matcher;
274
+ };
275
+ declare const match: MatchFactories;
276
+ /**
277
+ * Named captures recorded by `match.ref()` / `{{type#ref}}` placeholders.
278
+ * One scope lives on each spec result — every assertion chained off the same
279
+ * result shares it, and a new chain starts fresh.
280
+ */
281
+ declare class CaptureScope {
282
+ private readonly values;
283
+ /**
284
+ * The exact working directory of the current spec, when the framework
285
+ * knows it (cli mode). Drives the `{{workdir}}` token / `match.workdir()`.
286
+ */
287
+ workdir?: string | undefined;
288
+ constructor(workdir?: string);
289
+ get(name: string): unknown;
290
+ has(name: string): boolean;
291
+ set(name: string, value: unknown): void;
292
+ }
293
+ //#endregion
294
+ //#region src/specification/facets/_common/result/json.d.ts
295
+ /**
296
+ * Read-only accessor for a JSON payload parsed from a text stream (stdout).
297
+ *
298
+ * Lazily parses the JSON on first use; parse errors are deferred until the
299
+ * value is read so that callers can still read the raw stream text without
300
+ * triggering a throw.
301
+ *
302
+ * ANSI escape sequences are stripped by default before `JSON.parse`
303
+ * (CONVENTIONS D6). When a `transform` is configured on the spec runner, it
304
+ * runs on the stripped text — an escape hatch for application noise not
305
+ * covered by the `{{token}}` grammar.
306
+ *
307
+ * Assertions go through `expect()`: `expect(result.json).toMatch('name.json')`
308
+ * (deep-equal against `_expected/<name>`, with `{{placeholder}}` support).
309
+ */
310
+ declare class JsonAccessor<Value = unknown> {
311
+ /** @internal Ref-capture scope shared by the current spec execution. */
312
+ readonly captures: CaptureScope;
313
+ /** @internal */
314
+ private readonly rawText;
315
+ /** @internal Test-file directory — fixture resolution root for matchers. */
316
+ readonly testDir: string;
317
+ /** @internal */
318
+ private readonly transform?;
319
+ constructor(rawText: string, testDir: string, transform?: (text: string) => string, captures?: CaptureScope);
320
+ /**
321
+ * The parsed JSON value. Throws if the text is not valid JSON.
322
+ *
323
+ * `Value` is `unknown` unless the subject that produced this accessor
324
+ * knows better — `.call<T>()` does, and hands its T straight through so a
325
+ * one-field reading needs no cast in the test.
326
+ */
327
+ get value(): Value;
328
+ }
329
+ //#endregion
275
330
  //#region src/specification/ports/browser.port.d.ts
276
331
  /**
277
332
  * The landmark roles — the standard set of page regions, and the only
@@ -424,6 +479,11 @@ type BrowserOpenOptions = {
424
479
  * and the boundary of the `external` policy.
425
480
  */
426
481
  baseUrl: string;
482
+ /**
483
+ * Pin the page's own `Date` at this instant before the first byte is
484
+ * parsed — the calendar the site's scripts read. `.clock()` on the chain.
485
+ */
486
+ clock?: string | undefined;
427
487
  /**
428
488
  * Cross-origin request policy. `'block'` aborts every request leaving
429
489
  * the site under test (analytics, CDNs) — the browser-side analog of
@@ -978,6 +1038,62 @@ type LiterateRunFlags = {
978
1038
  frozen?: boolean | undefined;
979
1039
  };
980
1040
  //#endregion
1041
+ //#region src/specification/facets/integration/result.d.ts
1042
+ /** What `.call()` produced: the value it returned, or the error it threw. */
1043
+ type CallOutcome = {
1044
+ error: unknown;
1045
+ threw: boolean;
1046
+ value: unknown;
1047
+ };
1048
+ type CallResultOptions = BaseResultOptions & {
1049
+ outcome: CallOutcome;
1050
+ };
1051
+ /**
1052
+ * What `result.value` is, decided by what the call returned: a string reads as
1053
+ * a stream, anything else as JSON. `.call<T>()` already knows T, so the test
1054
+ * reads one field (`result.value.value.ok`) without narrowing a union first.
1055
+ */
1056
+ type CallValue<Returned> = Returned extends string ? TextAccessor : JsonAccessor<Returned>;
1057
+ /**
1058
+ * The result of an in-process call — the integration facet's whole surface.
1059
+ *
1060
+ * Two readings, never both: `value` is what the call RETURNED, `error` is
1061
+ * what it THREW. A module that refuses is specified the same way a module
1062
+ * that answers is, and neither needs a `try`/`catch` in the test — which is
1063
+ * the shape that used to make a refusal spec three times the size of the
1064
+ * happy one, and the shape that quietly passes when nothing throws at all.
1065
+ *
1066
+ * Both are the package's ordinary accessors, so the golden mechanism reaches
1067
+ * them, and `Returned` is what `.call<T>()` returned — `result.value.value` is
1068
+ * that type, not `unknown`, so a one-field reading needs no golden file: `toMatch('<name>.json'|'<name>.txt')`, the `{{token}}` grammar,
1069
+ * `{ frozen }` and `TEST_UPDATE=1` all work here exactly as on an HTTP
1070
+ * response or a command's stdout.
1071
+ */
1072
+ declare class CallResult<Returned = unknown> extends BaseResult {
1073
+ private readonly outcome;
1074
+ constructor(options: CallResultOptions);
1075
+ /**
1076
+ * What the call THREW, as text — the message of an `Error`, the value
1077
+ * itself when something else was thrown, and empty when it returned.
1078
+ *
1079
+ * Empty rather than absent on purpose: `await expect(result.error).toBeEmpty()`
1080
+ * is how a spec says "and it did not refuse" — awaited, because the
1081
+ * matcher answers a promise on every subject it takes, and a dropped one
1082
+ * passes the test while the failure surfaces as an unhandled rejection
1083
+ * (D2 refuses the bare form).
1084
+ */
1085
+ get error(): TextAccessor;
1086
+ /**
1087
+ * What the call RETURNED. A string reads as a stream (`<name>.txt`),
1088
+ * anything else as JSON (`<name>.json`) — the same two subjects every
1089
+ * other facet's results are built from.
1090
+ *
1091
+ * A call that threw returns the empty value: what it produced is
1092
+ * {@link CallResult.error}.
1093
+ */
1094
+ get value(): CallValue<Returned>;
1095
+ }
1096
+ //#endregion
981
1097
  //#region src/specification/facets/mobile/result.d.ts
982
1098
  /** Result from a `.open()` action — the screen as the device saw it, final state. */
983
1099
  declare class ScreenResult extends BaseResult {
@@ -1194,11 +1310,23 @@ type SpecificationConfig = {
1194
1310
  * facet factories auto-populate this when `dockerConfig` is present.
1195
1311
  */
1196
1312
  dockerTestRunId?: string | undefined;
1313
+ /**
1314
+ * Environment applied to every run of the declared binary
1315
+ * (`specification.cli({ defaults })`) — under a chain's own `.env()` and
1316
+ * under a document's `env:`, both of which win.
1317
+ */
1318
+ defaultEnv?: CliEnv | undefined;
1197
1319
  /**
1198
1320
  * Named environment SETS a spec document may name by bare word
1199
1321
  * (`env: frozen`). Declared once per app in `specification.cli()`.
1200
1322
  */
1201
1323
  envSets?: Record<string, CliEnv> | undefined;
1324
+ /**
1325
+ * When set, `.clock()` is unavailable on this runner and throws this
1326
+ * reason immediately (compose mode — the calendar it would pin is this
1327
+ * process's, and the app runs in a container).
1328
+ */
1329
+ clockDisabledReason?: string | undefined;
1202
1330
  /**
1203
1331
  * When set, `.intercept()` is unavailable on this runner and throws this
1204
1332
  * reason immediately (compose mode — MSW is in-process, CONVENTIONS I3).
@@ -1240,6 +1368,8 @@ type InterceptMethod<T> = ((contracts: ContractInput) => T) & ((request: Contrac
1240
1368
  * of the declared services record that are databases.
1241
1369
  */
1242
1370
  type ApiSpecification<DatabaseKey extends string = string> = {
1371
+ /** Pin the app's `Date` at `iso` for this chain — the calendar the app reads. */
1372
+ clock: (iso: string) => ApiSpecification<DatabaseKey>;
1243
1373
  /** Set HTTP headers for the request. Multiple calls merge. */
1244
1374
  headers: (headers: Record<string, string>) => ApiSpecification<DatabaseKey>;
1245
1375
  /** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
@@ -1264,6 +1394,8 @@ type ApiSpecification<DatabaseKey extends string = string> = {
1264
1394
  * Jobs run in-process by definition (CONVENTIONS A5/A8).
1265
1395
  */
1266
1396
  type JobsSpecification<DatabaseKey extends string = string> = {
1397
+ /** Pin the job's `Date` at `iso` for this chain — the calendar the job reads. */
1398
+ clock: (iso: string) => JobsSpecification<DatabaseKey>;
1267
1399
  /** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
1268
1400
  intercept: InterceptMethod<JobsSpecification<DatabaseKey>>;
1269
1401
  /** Queue a SQL seed file from `_seeds/` to run before the action. */
@@ -1273,6 +1405,31 @@ type JobsSpecification<DatabaseKey extends string = string> = {
1273
1405
  /** Execute the named job registered via the `jobs` option and resolve with the result. */
1274
1406
  trigger: (name: string) => Promise<BaseResult>;
1275
1407
  };
1408
+ /**
1409
+ * The `integration` facet — the in-process chain handed out by
1410
+ * `specification.integration()`.
1411
+ *
1412
+ * The subject is a MODULE, not an entry: no HTTP, no binary, no page. What
1413
+ * makes it a spec rather than a module test is what it stands on — real
1414
+ * services, declared contracts, a golden — so it takes the same setups as
1415
+ * every other facet and ends on the one action a module has: being called.
1416
+ */
1417
+ type IntegrationSpecification<Services extends Record<string, unknown> = Record<string, unknown>, DatabaseKey extends string = string> = {
1418
+ /** Pin the module's `Date` at `iso` for this chain. */
1419
+ clock: (iso: string) => IntegrationSpecification<Services, DatabaseKey>;
1420
+ /** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
1421
+ intercept: InterceptMethod<IntegrationSpecification<Services, DatabaseKey>>;
1422
+ /** Queue a SQL seed file from `_seeds/` to run before the call. */
1423
+ seed: (file: string, options?: {
1424
+ database?: DatabaseKey;
1425
+ }) => IntegrationSpecification<Services, DatabaseKey>;
1426
+ /**
1427
+ * Call the module and resolve with what it produced. The started services
1428
+ * record is handed in, so the subject is constructed with the real
1429
+ * connection strings rather than with a double.
1430
+ */
1431
+ call: <T>(subject: (services: Services) => Promise<T> | T) => Promise<CallResult<T>>;
1432
+ };
1276
1433
  /**
1277
1434
  * The `cli` facet — command chain entry handed out by `specification.cli()`.
1278
1435
  * Setup methods chain; `.exec()` is the single terminal action (CONVENTIONS
@@ -1317,6 +1474,11 @@ type CliSpecification<DatabaseKey extends string = string> = {
1317
1474
  * performs one raw HTTP exchange and never follows redirects.
1318
1475
  */
1319
1476
  type WebsiteSpecification = {
1477
+ /**
1478
+ * Pin the PAGE's `Date` at `iso` for this chain — what the site's own
1479
+ * scripts read. A `.fetch()` opens no page and refuses the setup.
1480
+ */
1481
+ clock: (iso: string) => WebsiteSpecification;
1320
1482
  /** Set HTTP headers for the exchange (incl. User-Agent overrides). Multiple calls merge. */
1321
1483
  headers: (headers: Record<string, string>) => WebsiteSpecification;
1322
1484
  /**
@@ -1863,6 +2025,28 @@ type HttpContractFilter = {
1863
2025
  /** Query-param subset. string = exact value, RegExp = `test()`. */
1864
2026
  query?: Record<string, RegExp | string>;
1865
2027
  };
2028
+ /** Init options for a streamed reply. */
2029
+ type HttpStreamInit = {
2030
+ /** The `content-type` the stream is served under. Default `application/octet-stream`. */
2031
+ contentType?: string;
2032
+ /** Milliseconds between two chunks. Default 0 — every chunk at once. */
2033
+ delay?: number;
2034
+ /** Response headers, merged over the builder's own. */
2035
+ headers?: Record<string, string>;
2036
+ /** HTTP status code. Default 200. */
2037
+ status?: number;
2038
+ };
2039
+ /** One server-sent event, as `http.sse()` frames it. */
2040
+ type SseEvent = {
2041
+ /** The `data:` payload. An object is serialised as JSON; a string is sent as-is. */
2042
+ data: unknown;
2043
+ /** The `event:` name. Omitted for an unnamed (default `message`) event. */
2044
+ event?: string;
2045
+ /** The `id:` the client echoes as `Last-Event-ID` when it reconnects. */
2046
+ id?: string;
2047
+ /** The `retry:` hint, in milliseconds. */
2048
+ retry?: number;
2049
+ };
1866
2050
  /** Init options shared by the response builders. */
1867
2051
  type HttpResponseInit = {
1868
2052
  /** Delay in ms before responding (for timeout testing). */
@@ -1902,6 +2086,27 @@ declare const http: {
1902
2086
  * server that is not running fails it. `fetch` rejects; no status is served.
1903
2087
  */
1904
2088
  unreachable(): ContractResponse;
2089
+ /**
2090
+ * Response: a body that arrives in PIECES, written in order.
2091
+ *
2092
+ * A subject that reads a response as it lands — a token-by-token render, a
2093
+ * progress bar, a reconnect on a half-read body — is specified by the
2094
+ * chunks and their order, which a single serialised body cannot state.
2095
+ * Honoured by all three engines: msw's server, msw's worker, and the
2096
+ * `node:http` stub the website and mobile facets serve from.
2097
+ *
2098
+ * @example
2099
+ * http.stream(['Hel', 'lo, ', 'world'], { contentType: 'text/plain', delay: 10 })
2100
+ */
2101
+ stream(chunks: readonly string[], init?: HttpStreamInit): ContractResponse;
2102
+ /**
2103
+ * Response: a server-sent event stream — one chunk per event, framed the
2104
+ * way an `EventSource` reads them, served as `text/event-stream`.
2105
+ *
2106
+ * @example
2107
+ * http.sse([{ data: { token: 'Hel' } }, { data: { token: 'lo' } }, { event: 'done', data: '' }])
2108
+ */
2109
+ sse(events: readonly SseEvent[], init?: Omit<HttpStreamInit, "contentType">): ContractResponse;
1905
2110
  /** Response: a text body, served as `text/plain` (200 by default). */
1906
2111
  text(body: string, init?: HttpResponseInit): ContractResponse;
1907
2112
  };
@@ -1965,13 +2170,35 @@ declare const openai: {
1965
2170
  };
1966
2171
  //#endregion
1967
2172
  //#region src/vitest/mock-of.d.ts
1968
- /** Factory signature that creates a deep mock proxy for any interface. */
1969
- type MockPort = <T>() => DeepMockProxy<T>;
2173
+ /** How much of the port the double answers for. */
2174
+ type MockOfOptions = {
2175
+ /**
2176
+ * Deep (the default): reading a member that was never stubbed hands back
2177
+ * another double, however far down the port nests. `false` gives the flat
2178
+ * proxy — every member of `T` and nothing beneath them, which is what a
2179
+ * port with a nested namespace wants when the test only ever touches the
2180
+ * first level.
2181
+ */
2182
+ deep?: boolean;
2183
+ };
1970
2184
  /**
1971
- * Create a deep mock proxy for a given type.
1972
- * Wraps `vitest-mock-extended`'s `mockDeep` for convenient port mocking.
1973
- */
1974
- declare const mockOf: MockPort;
2185
+ * A typed double for an injected port — the fourth rung of the doubles ladder
2186
+ * (docs/12-conventions.md), and the only one a module test may build itself.
2187
+ *
2188
+ * The type argument is the port: `T` is constrained to `object` and has no
2189
+ * default, so a double is always asked for something. A call with no argument
2190
+ * answers for `object`, which satisfies no port and fails where it is used.
2191
+ *
2192
+ * @example
2193
+ * const gateway = mockOf<PaymentGateway>(); // deep
2194
+ * const clock = mockOf<ClockPort>({ deep: false }); // flat
2195
+ */
2196
+ declare function mockOf<T extends object>(options: {
2197
+ deep: false;
2198
+ }): MockProxy<T>;
2199
+ declare function mockOf<T extends object>(options?: MockOfOptions): DeepMockProxy<T>;
2200
+ /** The factory's own shape — kept as a name so a consumer can annotate with it. */
2201
+ type MockPort = typeof mockOf;
1975
2202
  //#endregion
1976
2203
  //#region src/vitest/mock-of-date.d.ts
1977
2204
  /** Interface for freezing and resetting the global Date in tests. */
@@ -1987,6 +2214,115 @@ type MockDatePort = {
1987
2214
  */
1988
2215
  declare const mockOfDate: MockDatePort;
1989
2216
  //#endregion
2217
+ //#region src/vitest/clock.d.ts
2218
+ /**
2219
+ * The one time primitive.
2220
+ *
2221
+ * Time is the determinism the framework owns (CONVENTIONS D16): a test that
2222
+ * samples `Date.now()` and asserts on what comes back is a test that fails on
2223
+ * the next leap second, and a test that reaches for `vi.useFakeTimers()` owns a
2224
+ * teardown it will one day forget. `clock` is both answers in one name — the
2225
+ * instant is stated, and the scope restores itself.
2226
+ *
2227
+ * ```typescript
2228
+ * using _ = clock.at('2026-03-04T09:30:00Z'); // Date is pinned until the scope ends
2229
+ * ```
2230
+ *
2231
+ * `at()` takes the CALENDAR and leaves the scheduler alone: a UI framework's
2232
+ * own loop, a `setTimeout` inside the subject and every promise still resolve
2233
+ * on their own. `run()` takes the scheduler too, for a subject whose behaviour
2234
+ * IS the passage of time — a debounce, a retry backoff, a poll.
2235
+ */
2236
+ /** A pinned clock — released when the `using` scope that declared it ends. */
2237
+ type PinnedClock = Disposable;
2238
+ declare const clock: {
2239
+ /**
2240
+ * Move the pinned clock forward by `ms` — and, under {@link clock.run},
2241
+ * fire every callback that came due, awaiting what each of them started.
2242
+ *
2243
+ * Only a pinned clock moves: called with no clock taken, it says so rather
2244
+ * than silently doing nothing to the real one.
2245
+ */
2246
+ advance(ms: number): Promise<void>;
2247
+ /**
2248
+ * Pin `Date` at `iso` for the current scope. `Date` ONLY: the scheduler
2249
+ * stays real, so a subject that awaits, renders or polls keeps running.
2250
+ *
2251
+ * @example
2252
+ * using _ = clock.at('2026-03-04T09:30:00Z');
2253
+ * expect(stampedAt()).toBe('2026-03-04T09:30:00.000Z');
2254
+ */
2255
+ at(iso: string): PinnedClock;
2256
+ /**
2257
+ * Take the scheduler as well as the calendar: `setTimeout`, `setInterval`
2258
+ * and their friends queue instead of firing, and {@link clock.advance} is
2259
+ * what makes them due. For a subject whose behaviour IS elapsed time — a
2260
+ * debounce, a retry backoff, a poll — where waiting for real would be the
2261
+ * arbitrary sleep J2 forbids.
2262
+ *
2263
+ * @example
2264
+ * using _ = clock.run('2026-03-04T09:30:00Z');
2265
+ * const pending = retrying(failsTwice);
2266
+ * await clock.advance(5_000);
2267
+ * await expect(pending).resolves.toBe('ok');
2268
+ */
2269
+ run(iso?: string): PinnedClock;
2270
+ };
2271
+ //#endregion
2272
+ //#region src/specification/assertions/required.d.ts
2273
+ /**
2274
+ * The value, or a failure that says what was missing and why it mattered.
2275
+ *
2276
+ * A test reaching into a captured structure — a row, a header, the third
2277
+ * element of a list — meets `T | undefined` and has to do something about it.
2278
+ * The two things it usually does are both wrong: `!` erases the question, and
2279
+ * an `if (!x) throw new Error('missing')` re-states the same three lines in
2280
+ * every repository (the estate had two hand-written copies of exactly this).
2281
+ * `required` is that gesture with the reason attached, so the failure reads as
2282
+ * a sentence rather than as a `TypeError` two frames further down.
2283
+ *
2284
+ * @example
2285
+ * const id = required(result.response.body.id, 'the creation reply carries the new id');
2286
+ */
2287
+ declare function required<T>(value: null | T | undefined, why: string): T;
2288
+ //#endregion
2289
+ //#region src/specification/assertions/wait-until.d.ts
2290
+ /** How long to wait, and how often to look. */
2291
+ type WaitUntilOptions = {
2292
+ /** Milliseconds between two readings. Default 50. */
2293
+ interval?: number;
2294
+ /** How long the condition has to become true. Default 5 000 ms. */
2295
+ timeout?: number;
2296
+ /** What the condition is, named in the failure. Default: the predicate's source. */
2297
+ why?: string;
2298
+ };
2299
+ /**
2300
+ * Wait until a condition holds — the sanctioned answer to "the thing I am
2301
+ * specifying is not ready yet".
2302
+ *
2303
+ * A spec that sleeps for a guessed duration is a spec that is slow on a fast
2304
+ * machine and flaky on a slow one, which is why J2 refuses `setTimeout` under
2305
+ * `specs/`. Every facet that drives something already waits on a CONDITION —
2306
+ * a served page, a banner on stdout, an element the visitor can see — and this
2307
+ * is that primitive where no facet owns the waiting: a background write to
2308
+ * land, a queue to drain, a file to appear.
2309
+ *
2310
+ * The failure names the condition and how long it was given, so a timeout is a
2311
+ * sentence rather than "expected true, got false".
2312
+ *
2313
+ * The budget is measured on `performance.now()`, not on `Date` — `clock.at()`
2314
+ * freezes the calendar, and a deadline read off a frozen calendar never
2315
+ * arrives: the poll would run until vitest killed the test with a sentence
2316
+ * about the file rather than about the condition.
2317
+ *
2318
+ * Not for a subject under `clock.run()`: there the scheduler is the test's, and
2319
+ * `clock.advance(ms)` is what makes time pass.
2320
+ *
2321
+ * @example
2322
+ * await waitUntil(() => outbox.length === 1, { why: 'the job wrote its one message' });
2323
+ */
2324
+ declare function waitUntil(predicate: () => boolean | Promise<boolean>, options?: WaitUntilOptions): Promise<void>;
2325
+ //#endregion
1990
2326
  //#region src/surface.d.ts
1991
2327
  declare module 'vitest' {
1992
2328
  interface Assertion<T = any> {
@@ -2188,6 +2524,14 @@ type CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2188
2524
  * containers get force-removed at scope exit (CONVENTIONS B5).
2189
2525
  */
2190
2526
  docker?: DockerSpecConfig;
2527
+ /**
2528
+ * Environment applied to EVERY run of this binary — the variables the
2529
+ * product needs to be deterministic at all (`TZ`, `NO_COLOR`, `LANG`,
2530
+ * a config home under the spec's own workdir). Stated once per app
2531
+ * instead of repeated on every chain and in every document; a chained
2532
+ * `.env()` and a document's `env:` both win over it.
2533
+ */
2534
+ defaults?: CliEnv;
2191
2535
  /**
2192
2536
  * Named environment SETS for `<case>.spec.yaml` documents. An `env:` entry
2193
2537
  * `frozen` applies the whole `frozen` record; `$WORKDIR` expands and `null`
@@ -2245,6 +2589,42 @@ type CliHandle<DatabaseKey extends string = string> = {
2245
2589
  };
2246
2590
  declare function startCli<Services extends ServiceRecord>(bin: string, options?: CliSpecificationOptions<Services>): Promise<CliHandle<DatabaseKeys<Services>>>;
2247
2591
  //#endregion
2592
+ //#region src/specification/facets/integration/start-integration.d.ts
2593
+ /** Options for {@link startIntegration | specification.integration}. */
2594
+ type IntegrationSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2595
+ /** Project-root override — see the `root` option of `specification.api()`. */
2596
+ root?: string;
2597
+ /**
2598
+ * Named infrastructure the module is specified AGAINST — a real database,
2599
+ * a real cache, a `process()` it talks to. Omit it for the other half of
2600
+ * the facet: a pure module whose oracle is a golden.
2601
+ */
2602
+ services?: Services;
2603
+ };
2604
+ /**
2605
+ * The record returned by {@link startIntegration | specification.integration}.
2606
+ * Destructure with the canonical names (CONVENTIONS A3):
2607
+ *
2608
+ * const { integration, cleanup } = await specification.integration(…);
2609
+ */
2610
+ type IntegrationHandle<Services extends ServiceRecord = ServiceRecord, DatabaseKey extends string = string> = {
2611
+ /** Stop all infrastructure started by this specification. */
2612
+ cleanup: () => Promise<void>;
2613
+ integration: IntegrationSpecification<Services, DatabaseKey>;
2614
+ };
2615
+ /**
2616
+ * Specify a module the way the assembled product is specified: against real
2617
+ * services and declared contracts, with the golden mechanism on what it
2618
+ * produces.
2619
+ *
2620
+ * The fork is the SUBJECT. A module alone is a module test, beside its code,
2621
+ * with no runner at all. A module that needs a real database, or whose oracle
2622
+ * is a golden file, is this — and it used to be neither: a `specs/` folder
2623
+ * with plain vitest, a hand-built sqlite template, a `beforeAll` that started
2624
+ * a container, and a `toMatchSnapshot` where the package has a golden engine.
2625
+ */
2626
+ declare function startIntegration<Services extends ServiceRecord>(options?: IntegrationSpecificationOptions<Services>): Promise<IntegrationHandle<Services, DatabaseKeys<Services>>>;
2627
+ //#endregion
2248
2628
  //#region src/specification/facets/jobs/start-jobs.d.ts
2249
2629
  /** Options for {@link startJobs | specification.jobs}. */
2250
2630
  type JobsSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
@@ -2291,7 +2671,7 @@ type MobileBackendOptions = {
2291
2671
  port?: number;
2292
2672
  };
2293
2673
  /** Options for {@link startMobile | specification.mobile}. */
2294
- type MobileSpecificationOptions = {
2674
+ type MobileSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2295
2675
  /** The app under test — terminated and relaunched by every `.open()`. */
2296
2676
  app: {
2297
2677
  /** Bundle id of the installed app (`com.example.app`). */
@@ -2324,6 +2704,14 @@ type MobileSpecificationOptions = {
2324
2704
  * driving a DEV build states the wait its bundler's cold boot needs
2325
2705
  * (`{ action: 45_000 }`) instead of sleeping inside its scenarios.
2326
2706
  */
2707
+ /**
2708
+ * Named services started with the runner and stopped with it — a
2709
+ * `process()` for the bundler the app loads from, a database a dev build
2710
+ * reads. Metro stops being a `beforeAll` the repository maintains: it is
2711
+ * `process({ command: 'expo start', ready: /Metro waiting on .*:(\\d+)/ })`
2712
+ * like every other external process the framework owns.
2713
+ */
2714
+ services?: Services;
2327
2715
  timeouts?: DeviceTimeouts;
2328
2716
  };
2329
2717
  /**
@@ -2345,13 +2733,35 @@ type MobileHandle = {
2345
2733
  /** The resolved simulator UDID the specs run against. */
2346
2734
  udid: string;
2347
2735
  };
2348
- declare function startMobile(options: MobileSpecificationOptions): Promise<MobileHandle>;
2736
+ declare function startMobile<Services extends ServiceRecord>(options: MobileSpecificationOptions<Services>): Promise<MobileHandle>;
2349
2737
  //#endregion
2350
2738
  //#region src/specification/facets/website/serve.adapter.d.ts
2351
- /** Options for a local server started by the framework. */
2352
- type ServeOptions = {
2353
- /** Shell command that starts the site. Receives the chosen port as `PORT`. */
2739
+ /**
2740
+ * The ONE shape an external process takes — the site under test, a backend a
2741
+ * page talks to, a bundler a simulator loads from. Built by `process()`
2742
+ * (chapter 11) and accepted wherever a facet starts something outside this
2743
+ * process: `website({ server })`, `services: { api: process(…) }`, and the
2744
+ * literate `serve:` registry.
2745
+ */
2746
+ type ProcessOptions = {
2747
+ /**
2748
+ * A one-shot command run ONCE per specification, before the process is
2749
+ * spawned, which must exit 0 — a build, a migration, a fixture load. Its
2750
+ * failure is the specification's, and it says so.
2751
+ */
2752
+ before?: string;
2753
+ /** Shell command that starts the process. Receives the chosen port as `PORT`. */
2354
2754
  command: string;
2755
+ /** Working directory, relative to the project root. Default: the root itself. */
2756
+ cwd?: string;
2757
+ /**
2758
+ * Environment for the child, on top of the runner's own. A function
2759
+ * receives the services already started beside this one, so a process can
2760
+ * be handed a sibling's connection string.
2761
+ */
2762
+ env?: ((services: Record<string, {
2763
+ connectionString: string;
2764
+ }>) => Record<string, string>) | Record<string, string>;
2355
2765
  /** Fixed port. Default: an OS-assigned free port, injected as `PORT`. */
2356
2766
  port?: number;
2357
2767
  /**
@@ -2369,8 +2779,96 @@ type ServeOptions = {
2369
2779
  /** Readiness budget in milliseconds. Default 30 000. */
2370
2780
  timeout?: number;
2371
2781
  };
2782
+ /**
2783
+ * What `ProcessOptions` was called in 15.2, when the only process the
2784
+ * framework owned was a website's server.
2785
+ *
2786
+ * @deprecated Renamed to {@link ProcessOptions} in 15.3 — the shape is the
2787
+ * one external process every facet starts, not a website's alone. The alias
2788
+ * is removed in 16.0.
2789
+ */
2790
+ type ServeOptions = ProcessOptions;
2791
+ //#endregion
2792
+ //#region src/specification/facets/_common/process.d.ts
2793
+ /**
2794
+ * An external process, declared like any other service.
2795
+ *
2796
+ * Before this there were three shapes for the same thing: `website({ server })`
2797
+ * took a `ServeOptions`, a literate document took a `serve:` entry, and a
2798
+ * project that needed a second process — an API behind the site, Metro behind
2799
+ * the simulator — started one by hand in a `beforeAll` and killed it in an
2800
+ * `afterAll` it sometimes forgot. `process()` is the one shape, and the
2801
+ * framework owns the lifetime.
2802
+ *
2803
+ * It sits in a `services` record beside `postgres()`, `redis()` and
2804
+ * `sqlite()`, so it starts after them and can be handed their connection
2805
+ * strings through `env`, and it is stopped with the specification.
2806
+ */
2807
+ declare class ProcessHandle implements ServiceHandle {
2808
+ /** Embedded: nothing to pull, nothing to run in a container. */
2809
+ readonly defaultImage = "";
2810
+ readonly defaultPort = 0;
2811
+ readonly environment: Record<string, string>;
2812
+ readonly type = "process";
2813
+ composeName: null | string;
2814
+ connectionString: string;
2815
+ started: boolean;
2816
+ private adapter;
2817
+ private readonly options;
2818
+ constructor(options: ProcessOptions);
2819
+ buildConnectionString(host: string, port: number): string;
2820
+ createDatabaseAdapter(): DatabasePort | null;
2821
+ healthcheck(): Promise<void>;
2822
+ /**
2823
+ * A process is not a container, so the orchestrator never starts it: the
2824
+ * facet does, once its siblings are up and their connection strings are
2825
+ * readable ({@link startProcessServices}).
2826
+ */
2827
+ initialize(): Promise<void>;
2828
+ /**
2829
+ * Isolation is the RUN's, not the worker's — and a run here is one
2830
+ * EVALUATION of the specification module. Under vitest's default
2831
+ * isolation that is once per test file, so two files that import the same
2832
+ * specification get a child each, with the id the facet minted for it.
2833
+ * Nothing is acquired or reset between tests: the id is what tells two
2834
+ * runs apart, never a label a spec samples.
2835
+ */
2836
+ isolation(): IsolationStrategy;
2837
+ reset(): Promise<void>;
2838
+ /**
2839
+ * Spawn it and wait until it answers. `services` is every handle declared
2840
+ * beside this one, already started — what an `env` function reads.
2841
+ *
2842
+ * @internal
2843
+ */
2844
+ spawnWith(root: string, services: Record<string, {
2845
+ connectionString: string;
2846
+ }>, runId: string): Promise<void>;
2847
+ /** Terminate the process group. Idempotent. */
2848
+ shutdown(): Promise<void>;
2849
+ /** What it was declared as — what a facet reads to start it in a slot of its own. */
2850
+ get spec(): ProcessOptions;
2851
+ /** The base URL it came up on — known only once it is started. */
2852
+ get url(): string;
2853
+ }
2854
+ /**
2855
+ * Declare an external process the framework owns for the life of the
2856
+ * specification.
2857
+ *
2858
+ * @example
2859
+ * const { website, cleanup } = await specification.website({
2860
+ * services: { api: process({ command: 'bin/server web', ready: '/health' }) },
2861
+ * server: ({ api }) => process({
2862
+ * command: 'next dev',
2863
+ * env: { NEXT_PUBLIC_API_URL: api.connectionString },
2864
+ * }),
2865
+ * });
2866
+ */
2867
+ declare function processService(options: ProcessOptions): ProcessHandle;
2372
2868
  //#endregion
2373
2869
  //#region src/specification/facets/website/start-website.d.ts
2870
+ /** What `server` may be handed as, and what it resolves to. */
2871
+ type ServerSpec<Services extends ServiceRecord> = ((services: Services) => ProcessHandle | ProcessOptions) | ProcessHandle | ProcessOptions;
2374
2872
  /**
2375
2873
  * The declared stub backend behind the site under test — started before the
2376
2874
  * server command, torn down with the runner. Its URL is injected into the
@@ -2391,7 +2889,7 @@ type WebsiteBackendOptions = {
2391
2889
  * mode for the same reason: a deployed site cannot be pointed at a local
2392
2890
  * stub.
2393
2891
  */
2394
- type WebsiteSpecificationOptions = {
2892
+ type WebsiteSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2395
2893
  /**
2396
2894
  * Cross-origin request policy for visits. Default: `'block'` with a
2397
2895
  * local `server` (deterministic — analytics and CDNs never leave the
@@ -2403,6 +2901,14 @@ type WebsiteSpecificationOptions = {
2403
2901
  * `server` command. Auto-discovered from the calling file when absent.
2404
2902
  */
2405
2903
  root?: string;
2904
+ /**
2905
+ * Named services started with the runner, before the site is: a database
2906
+ * the site reads, a `process()` backend it calls. They are started in
2907
+ * declaration order and stopped with the specification, and `server` may
2908
+ * be a function of them — which is how a site is handed the URL of the
2909
+ * API it was started beside.
2910
+ */
2911
+ services?: Services;
2406
2912
  } & ({
2407
2913
  /**
2408
2914
  * Declared stub backend: started BEFORE the server command, its
@@ -2411,10 +2917,11 @@ type WebsiteSpecificationOptions = {
2411
2917
  */
2412
2918
  backend?: WebsiteBackendOptions;
2413
2919
  /**
2414
- * Start the site locally: a shell command receiving a free port
2415
- * as `PORT`, polled on `ready` (default `/`) until it answers.
2920
+ * Start the site locally: a `process()` — a shell command receiving
2921
+ * a free port as `PORT`, polled on `ready` (default `/`) until it
2922
+ * answers — or a function of the started services returning one.
2416
2923
  */
2417
- server: ServeOptions;
2924
+ server: ServerSpec<Services>;
2418
2925
  url?: never;
2419
2926
  } | {
2420
2927
  backend?: never;
@@ -2435,11 +2942,11 @@ type WebsiteHandle = {
2435
2942
  url: string;
2436
2943
  website: WebsiteSpecification;
2437
2944
  };
2438
- declare function startWebsite(options: WebsiteSpecificationOptions): Promise<WebsiteHandle>;
2945
+ declare function startWebsite<Services extends ServiceRecord>(options: WebsiteSpecificationOptions<Services>): Promise<WebsiteHandle>;
2439
2946
  //#endregion
2440
2947
  //#region src/specification/facets/_common/specification.d.ts
2441
2948
  /**
2442
- * The five specification constructors (CONVENTIONS A2) — created in a
2949
+ * The six specification constructors (CONVENTIONS A2) — created in a
2443
2950
  * `*.specification.ts` file under `specs/`, destructured with canonical
2444
2951
  * names, and cleaned up via `afterAll(cleanup)` (A1/A3/A4).
2445
2952
  *
@@ -2462,10 +2969,11 @@ declare function startWebsite(options: WebsiteSpecificationOptions): Promise<Web
2462
2969
  * export const { cli, cleanup } = await specification.cli('my-cli');
2463
2970
  * afterAll(cleanup);
2464
2971
  */
2465
- /** The five constructors, and only five — the framework's whole entry surface. */
2972
+ /** The six constructors, and only six — the framework's whole entry surface. */
2466
2973
  type Specification = {
2467
2974
  api: typeof startApi;
2468
2975
  cli: typeof startCli;
2976
+ integration: typeof startIntegration;
2469
2977
  jobs: typeof startJobs;
2470
2978
  mobile: typeof startMobile;
2471
2979
  website: typeof startWebsite;
@@ -2774,4 +3282,11 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
2774
3282
  */
2775
3283
  declare function sqlite(options?: SqliteOptions): SqliteHandle;
2776
3284
  //#endregion
2777
- export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, type ComponentChain, type ComponentScenario, type ComponentUi, type ComponentVisitor, ContainerAccessor, type ContainerPort, type Contract, type ContractInput, type ContractRequest, type ContractResponder, type ContractResponse, type ContractResponseValue, type Contracts, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, DirectoryAccessor, type DockerSpecConfig, type DomMount, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, type HttpContractFilter, type HttpResponseInit, HttpResult, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileElementRef, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockDatePort, type MockPort, Orchestrator, PageResult, type PostgresOptions, type RedisOptions, type RenderResult, type RenderSubject, type RenderedText, ResponseAccessor, type ScreenNode, ScreenResult, type ServeOptions, type ServerPort, type ServerResponse, type ServiceHandle, type ServiceRecord, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecKind, type SpecRun, type SpecServeEntry, type SpecStream, type SpecificationConfig, type SpecificationMode, type SqliteOptions, TableAccessor, TextAccessor, type TextFilter, type VisitScenario, type Visitor, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, complementary, component, content, contentinfo, defineContract, defineContracts, dialog, disabled, enabled, field, findContainersByLabel, focused, form, heading, http, inspectContainer, link, listitem, main, match, mockOf, mockOfDate, navigation, openai, option, postgres, redis, region, removeContainers, row, search, selected, specification, sqlite, status, table, testId, text, valued, within };
3285
+ //#region src/index.d.ts
3286
+ /**
3287
+ * The module-scope network double, on msw's node server — what a test with no
3288
+ * chain to hang contracts on reaches for (chapter 10).
3289
+ */
3290
+ declare const intercept: Intercept;
3291
+ //#endregion
3292
+ export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, CallResult, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, type ComponentChain, type ComponentScenario, type ComponentUi, type ComponentVisitor, ContainerAccessor, type ContainerPort, type Contract, type ContractInput, type ContractRequest, type ContractResponder, type ContractResponse, type ContractResponseValue, type Contracts, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, DirectoryAccessor, type DockerSpecConfig, type DomMount, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, type HttpContractFilter, type HttpResponseInit, HttpResult, type HttpStreamInit, type IntegrationHandle, type IntegrationSpecification, type IntegrationSpecificationOptions, type Intercept, type InterceptScope, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileElementRef, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockDatePort, type MockOfOptions, type MockPort, Orchestrator, PageResult, type PinnedClock, type PostgresOptions, ProcessHandle, type ProcessOptions, type RedisOptions, type RenderResult, type RenderSubject, type RenderedText, ResponseAccessor, type ScreenNode, ScreenResult, type ServeOptions, type ServerPort, type ServerResponse, type ServerSpec, type ServiceHandle, type ServiceRecord, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecKind, type SpecRun, type SpecServeEntry, type SpecStream, type SpecificationConfig, type SpecificationMode, type SqliteOptions, type SseEvent, type StreamBody, TableAccessor, TextAccessor, type TextFilter, type VisitScenario, type Visitor, type WaitUntilOptions, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, clock, complementary, component, content, contentinfo, defineContract, defineContracts, dialog, disabled, enabled, field, findContainersByLabel, focused, form, heading, http, inspectContainer, intercept, link, listitem, main, match, mockOf, mockOfDate, navigation, openai, option, postgres, processService as process, redis, region, removeContainers, required, row, search, selected, specification, sqlite, status, table, testId, text, valued, waitUntil, within };