@jterrazz/test 15.1.0 → 15.2.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,5 +1,7 @@
1
1
  import { Scalar } from "yaml";
2
2
  import { DeepMockProxy } from "vitest-mock-extended";
3
+ import { ReactNode as ComponentUi } from "react";
4
+ import "vitest/browser";
3
5
  //#region src/specification/matching/match.d.ts
4
6
  /**
5
7
  * Code-side dynamic-value matchers (`match.*`) and the capture scope shared
@@ -161,6 +163,16 @@ type ContractResponse = {
161
163
  headers?: Record<string, string> | undefined;
162
164
  /** Delay in ms before responding (for timeout testing). */
163
165
  delay?: number | undefined;
166
+ /**
167
+ * A transport failure instead of a reply: the request never reached a
168
+ * server, so `fetch` rejects. `status`, `body` and `headers` say nothing
169
+ * here — there is no response to carry them.
170
+ *
171
+ * The one thing a status code cannot express. A component's "the server did
172
+ * not answer, check that it is running" branch is reached by a rejected
173
+ * `fetch`, and a 503 stand-in tests the other branch entirely.
174
+ */
175
+ transport?: 'network-error' | undefined;
164
176
  };
165
177
  /**
166
178
  * A dynamic response: computed from the observed request at the moment the
@@ -267,8 +279,15 @@ declare function defineContracts(...items: ContractInput[]): Contracts;
267
279
  * so `within()` never becomes a second selector language.
268
280
  */
269
281
  type LandmarkKind = 'banner' | 'complementary' | 'contentinfo' | 'form' | 'main' | 'navigation' | 'region' | 'search';
270
- /** The interactive//textual element kinds — what a visitor actually acts on. */
271
- type ElementKind = 'button' | 'field' | 'heading' | 'link' | 'testId' | 'text';
282
+ /**
283
+ * The interactive/textual element kinds — what a visitor actually acts on.
284
+ *
285
+ * `dialog`, `status`, `table`, `row`, `listitem` and `option` are ARIA roles
286
+ * like the landmarks, but they are not CONTAINERS of a page: they are things a
287
+ * visitor reads and acts on, and they are the roles the estate's UI specs were
288
+ * already reaching for through `getByRole` or, worse, through `.first()`.
289
+ */
290
+ type ElementKind = 'button' | 'dialog' | 'field' | 'heading' | 'link' | 'listitem' | 'option' | 'row' | 'status' | 'table' | 'testId' | 'text';
272
291
  /**
273
292
  * A user-facing element descriptor — pure data, built by the element
274
293
  * vocabulary (`button()`, `link()`, `field()`, …) and translated into
@@ -285,9 +304,37 @@ type ElementRef = {
285
304
  * "Read Articles".
286
305
  */
287
306
  exact?: boolean;
307
+ /**
308
+ * Assert about the element's FOCUS rather than its presence — built by
309
+ * `focused(element)`. The accessibility tree carries no focus state, so
310
+ * where the keyboard is cannot be goldened; it is asserted by a verb.
311
+ */
312
+ focused?: boolean;
313
+ /**
314
+ * Narrows to "and it is (not) accepting input" — built by `disabled(x)` /
315
+ * `enabled(x)`. Like `focused`, a STATE the vocabulary can already name the
316
+ * element of; unlike it, one the ARIA tree carries too (`[disabled]`), so
317
+ * this modifier is what names the ONE control a test is about, and it
318
+ * reaches the direction a behavioural substitute cannot — clicking a
319
+ * disabled control is a timeout, never an answer.
320
+ */
321
+ disabled?: boolean;
288
322
  kind: ElementKind | LandmarkKind;
289
323
  /** Landmarks may be anonymous (`main()`, `banner()`); everything else is named. */
290
324
  name?: string;
325
+ /**
326
+ * Narrows to "and it is (not) the chosen one" — built by `selected(x)`.
327
+ * An option of a COLLAPSED select is in the tree and in the document, and
328
+ * it has no box on the screen, so this facet of it is read from the node
329
+ * (`:checked`) rather than from what a visitor could point at.
330
+ */
331
+ selected?: boolean;
332
+ /**
333
+ * The value the field must hold — built by `valued(field('Title'), '…')`.
334
+ * A field's value is a PROPERTY of the node: a controlled input's `value`
335
+ * attribute never moves, so nothing but the live value answers for it.
336
+ */
337
+ value?: string;
291
338
  /**
292
339
  * Restrict the search to the elements of another descriptor — built by
293
340
  * `within(scope, target)`. Chains: a scope may itself carry a scope.
@@ -300,6 +347,12 @@ type ElementRef = {
300
347
  * without opening a browser.
301
348
  */
302
349
  type ElementMatch = {
350
+ /**
351
+ * The accessible name, when it differs from the text — an `aria-label` is
352
+ * what a role descriptor MATCHED on, and printing the text alone sends the
353
+ * author to disambiguate a name they cannot see.
354
+ */
355
+ accessibleName?: string | undefined;
303
356
  /** Nearest landmark ancestor (`nav`, `footer`, `main`…), when there is one. */
304
357
  context?: string | undefined;
305
358
  /** The attribute that disambiguates most — `href` for links, `name` for fields. */
@@ -340,6 +393,12 @@ type Visitor = {
340
393
  click: (element: ElementRef) => Promise<void>;
341
394
  /** Fill a form field with a value. */
342
395
  fill: (element: ElementRef, value: string) => Promise<void>;
396
+ /**
397
+ * Wait until the element is GONE — hidden, or never in the document. The
398
+ * absence primitive: `see()` cannot answer "the dialog closed", because an
399
+ * element that never appears and one that disappeared read the same to it.
400
+ */
401
+ gone: (element: ElementRef) => Promise<void>;
343
402
  /** Navigate to a path of the site under test. */
344
403
  goto: (path: string) => Promise<void>;
345
404
  /** Hover the element. */
@@ -396,6 +455,8 @@ type BrowserPage = {
396
455
  status: number;
397
456
  /** Rendered `document.body.innerText`. */
398
457
  text: string;
458
+ /** The ARIA snapshot of the rendered `<body>` — the outline a golden pins. */
459
+ tree: string;
399
460
  /** `document.title` after rendering. */
400
461
  title: string;
401
462
  /** Final URL after redirects and scenario navigation. */
@@ -813,24 +874,6 @@ declare class TextAccessor {
813
874
  toString(): string;
814
875
  valueOf(): string;
815
876
  }
816
- /**
817
- * Wrap an arbitrary string into a {@link TextAccessor} anchored on the calling
818
- * test's directory — the same caller-detection the builders use.
819
- *
820
- * The product surface of a test framework is its own error messages, checker
821
- * output, and reports; those deserve the same goldening as any other output.
822
- * `text()` makes an ad-hoc string a first-class snapshot subject:
823
- *
824
- * ```typescript
825
- * const message = await catchMessage(() => expect(result.response).toMatch('wrong-body.http'));
826
- * expect(text(message)).toMatch('wrong-body-error.txt'); // resolves to _expected/
827
- * ```
828
- *
829
- * ANSI is stripped before every comparison (the raw form stays on `.text`),
830
- * the `{{token}}` grammar applies to the fixture, and `.grep()` composition
831
- * works exactly as on any stream accessor.
832
- */
833
- declare function text(value: string): TextAccessor;
834
877
  //#endregion
835
878
  //#region src/specification/facets/cli/result.d.ts
836
879
  /**
@@ -1027,6 +1070,13 @@ declare class PageResult extends BaseResult {
1027
1070
  get status(): number;
1028
1071
  /** Rendered body text as a text accessor. */
1029
1072
  get content(): TextAccessor;
1073
+ /**
1074
+ * The ARIA snapshot of the rendered `<body>` — the page's outline, as the
1075
+ * accessibility tree draws it, and the one golden a rendered surface pins.
1076
+ * Deterministic where a screenshot is not, and the SAME dialect a component
1077
+ * spec asserts, so a page and the components inside it read alike.
1078
+ */
1079
+ get tree(): TextAccessor;
1030
1080
  /** The document title as a text accessor. */
1031
1081
  get title(): TextAccessor;
1032
1082
  /** Final URL after redirects. */
@@ -1441,7 +1491,16 @@ declare class ContainerAccessor {
1441
1491
  private containerExec;
1442
1492
  }
1443
1493
  //#endregion
1444
- //#region src/vitest/matchers.d.ts
1494
+ //#region src/specification/facets/_common/result/match-options.d.ts
1495
+ /**
1496
+ * The per-call option both `toMatch` implementations honour.
1497
+ *
1498
+ * A golden is read from disk under node and through a server command from
1499
+ * inside a page, and BOTH builds must answer `{ frozen: true }` the same way —
1500
+ * so the option is declared here, where the node matchers and the page's
1501
+ * matchers can each reach it, rather than in the runner's own folder, which a
1502
+ * seam may not import (rule I1).
1503
+ */
1445
1504
  /**
1446
1505
  * Per-call options for the fixture-file `toMatch` subjects. `frozen` opts a
1447
1506
  * single fixture OUT of update-mode rewriting: a frozen fixture is NEVER
@@ -1456,194 +1515,707 @@ type MatchFixtureOptions = {
1456
1515
  frozen?: boolean;
1457
1516
  };
1458
1517
  //#endregion
1459
- //#region src/specification/facets/_common/orchestrator.d.ts
1460
- type OrchestratorOptions = {
1461
- /**
1462
- * Named infrastructure record. Keys become the database vocabulary of the
1463
- * spec (`.seed()` / `.table()` `database` option) and drive the compose
1464
- * binding: a handle with no explicit `composeService` links to the compose
1465
- * service named exactly like its key, else the kebab-case conversion of the
1466
- * key (`analyticsDb` → `analytics-db`) — see {@link resolveComposeBinding}.
1467
- */
1468
- services: Record<string, ServiceHandle>;
1469
- mode: 'e2e' | 'integration';
1470
- /**
1471
- * The project root, already resolved. REQUIRED: the constructors derive it
1472
- * by walking up from the calling specification file (CONVENTIONS A9), and
1473
- * the orchestrator is handed the answer. Defaulting to `process.cwd()` here
1474
- * meant one silent second opinion about what the project is — the runner
1475
- * resolving from the caller, this from wherever the runner happened to be
1476
- * launched, which in a workspace is rarely the same directory.
1477
- */
1478
- root: string;
1479
- /** Compose project name — used for per-worker stack isolation. */
1480
- projectName?: string;
1481
- };
1518
+ //#region src/integrations/vitest-browser/ui.d.ts
1482
1519
  /**
1483
- * Orchestrator for test infrastructure.
1484
- * Integration: starts services via testcontainers.
1485
- * E2E: runs full docker compose up.
1520
+ * A vanilla DOM subject: a function handed the container it must fill. Its
1521
+ * optional return is the teardown, the way a DOM library states one.
1486
1522
  */
1487
- declare class Orchestrator {
1488
- private readonly services;
1489
- private readonly mode;
1490
- private readonly root;
1491
- private readonly projectName;
1492
- private running;
1493
- private composeStack;
1494
- private composeHandles;
1495
- private started;
1496
- constructor(options: OrchestratorOptions);
1497
- /**
1498
- * Bind each declared handle to a compose service (CONVENTIONS A6): a record
1499
- * key resolves to the service named exactly like it, else the kebab-case
1500
- * conversion of the key; an explicit `composeService` wins. Throws on an
1501
- * ambiguous binding (both names present). Runs once compose config is known
1502
- * so the two-step resolution can consult the real service list.
1503
- */
1504
- private resolveBindings;
1505
- /**
1506
- * Start declared services via testcontainers (integration mode).
1507
- * Phase 1: start all containers in parallel (the slow part).
1508
- * Phase 2: wire connections, healthcheck, and init sequentially (fast).
1509
- */
1510
- start(): Promise<void>;
1511
- /**
1512
- * Stop testcontainers (integration mode).
1513
- */
1514
- stop(): Promise<void>;
1515
- /**
1516
- * Start full docker compose stack (e2e mode).
1517
- * Auto-detects infra services and creates handles for them.
1518
- */
1519
- startCompose(): Promise<void>;
1520
- /**
1521
- * Stop docker compose stack (e2e mode).
1522
- */
1523
- stopCompose(): Promise<void>;
1524
- /**
1525
- * Get the default database — the first declared handle that is one.
1526
- */
1527
- getDatabase(): DatabasePort | null;
1528
- /**
1529
- * Get all database services keyed by their record key (declared services)
1530
- * or compose service name (stack-detected services).
1531
- */
1532
- getDatabases(): Map<string, DatabasePort>;
1533
- private allHandles;
1534
- /**
1535
- * Get app URL from compose (e2e mode).
1536
- */
1537
- getAppUrl(): null | string;
1523
+ type DomMount = (container: HTMLElement) => (() => void) | void;
1524
+ /**
1525
+ * The `server.commands` this seam registers, declared in the module every part
1526
+ * of the seam already imports.
1527
+ *
1528
+ * A `declare module` merges only as an interface, and only from a file that is
1529
+ * itself a module and part of the program — a lone `.d.ts` would have to be
1530
+ * pulled in by an empty import, and its single `export {}` is exactly what a
1531
+ * formatter is free to drop, turning the augmentation into an ambient
1532
+ * declaration that SHADOWS the module it meant to extend.
1533
+ */
1534
+ declare module 'vitest/browser' {
1535
+ interface BrowserCommands {
1536
+ ariaTree: () => Promise<string>;
1537
+ goldenRead: (name: string) => Promise<null | string>;
1538
+ goldenWrite: (name: string, content: string) => Promise<string>;
1539
+ }
1538
1540
  }
1539
1541
  //#endregion
1540
- //#region src/specification/facets/_common/services.d.ts
1542
+ //#region src/integrations/vitest-browser/vitest-browser.adapter.d.ts
1543
+ /** What a mounted subject offers the chain, React or vanilla DOM alike. */
1544
+ type MountedSurface = {
1545
+ /** The element the subject was mounted into. */
1546
+ container: HTMLElement;
1547
+ /** Replace the rendered tree — a prop change, as a parent makes one. */
1548
+ rerender: (ui: ComponentUi) => Promise<void>;
1549
+ /** Take it off the screen, as a parent does. */
1550
+ unmount: () => Promise<void>;
1551
+ };
1552
+ /** The verbs a rendered surface shares with a page, plus the two a parent owns. */
1553
+ declare function componentVerbs(surface: MountedSurface): {
1554
+ check: (element: ElementRef) => Promise<void>;
1555
+ click: (element: ElementRef) => Promise<void>;
1556
+ fill: (element: ElementRef, value: string) => Promise<void>;
1557
+ gone: (element: ElementRef) => Promise<void>;
1558
+ hover: (element: ElementRef) => Promise<void>;
1559
+ press: (key: string) => Promise<void>;
1560
+ rerender: (ui: ComponentUi) => Promise<void>;
1561
+ see: (element: ElementRef) => Promise<void>;
1562
+ select: (element: ElementRef, option: string) => Promise<void>;
1563
+ unmount: () => Promise<void>;
1564
+ };
1565
+ //#endregion
1566
+ //#region src/specification/facets/component/rendered-text.d.ts
1541
1567
  /**
1542
- * Infrastructure services declared as a named record. Keys become the typed
1543
- * vocabulary of the whole spec: the server factory receives the same record,
1544
- * and `.seed()` / `.table()` target databases by key.
1545
- */
1546
- type ServiceRecord = Record<string, ServiceHandle>;
1547
- /** Keys of a services record whose handles are databases. */
1548
- type DatabaseKeys<Services extends ServiceRecord> = { [K in keyof Services]: Services[K] extends DatabasePort ? K & string : never; }[keyof Services];
1568
+ * A stream captured INSIDE the page.
1569
+ *
1570
+ * Structurally identical to any other {@link TextAccessor} — the same
1571
+ * `{{token}}` grammar, the same ANSI stripping, the same `.grep()` — and
1572
+ * marked so the matcher augmentation can say the one thing that differs: its
1573
+ * golden lives on the server, so `toMatch` is awaited here where it is
1574
+ * synchronous everywhere else.
1575
+ */
1576
+ declare class RenderedText extends TextAccessor {
1577
+ /** The marker the `toMatch` overload reads. Never asserted on directly. */
1578
+ readonly capturedInPage: true;
1579
+ }
1549
1580
  //#endregion
1550
- //#region src/specification/facets/api/start-api.d.ts
1551
- /** Any object with a request method compatible with Hono's app.request(). */
1552
- type HonoApp = {
1553
- request: (path: string, init?: RequestInit) => Promise<Response> | Response;
1554
- };
1555
- /** Execution mode — exists ONLY on `specification.api()` (CONVENTIONS A5). */
1556
- type SpecificationMode = 'compose' | 'node';
1557
- /** Options for {@link startApi | specification.api}. */
1558
- type ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
1559
- /**
1560
- * Execution mode override. Resolution: `options.mode` >
1561
- * `process.env.TEST_MODE` > `'node'`. Never hardcode this in a
1562
- * specification file when `server` is defined — set it per vitest
1563
- * project via `env: { TEST_MODE: 'compose' }` (CONVENTIONS A5).
1564
- */
1565
- mode?: SpecificationMode;
1581
+ //#region src/specification/facets/component/component.result.d.ts
1582
+ /**
1583
+ * What a render resolves to — the component as the page saw it, after the
1584
+ * scenario ran.
1585
+ *
1586
+ * Five accessors and no more: the ARIA outline a golden pins, the two
1587
+ * renderings a probe reads, and the two streams a spec wants silent. Nothing
1588
+ * here reaches back into React — a spec that needs the component's internals
1589
+ * is asserting the implementation, not the surface a user meets.
1590
+ */
1591
+ declare class RenderResult {
1592
+ /** The browser console as a stream — one `[type] text` line per message. */
1593
+ readonly console: RenderedText;
1594
+ /** Rendered text of the whole document body (`innerText`). */
1595
+ readonly content: RenderedText;
1596
+ /** Console messages of type `error` only, plus any uncaught page error. */
1597
+ readonly errors: RenderedText;
1598
+ /** The mounted markup, serialised — the escape hatch for a class or an attribute. */
1599
+ readonly html: RenderedText;
1600
+ /** The ARIA snapshot of the rendered body — the outline a golden pins. */
1601
+ readonly tree: RenderedText;
1602
+ constructor(streams: {
1603
+ console: string;
1604
+ content: string;
1605
+ errors: string;
1606
+ html: string;
1607
+ tree: string;
1608
+ });
1609
+ }
1610
+ //#endregion
1611
+ //#region src/specification/facets/component/component.types.d.ts
1612
+ /**
1613
+ * The component facet's shape, stated where BOTH runtimes can read it.
1614
+ *
1615
+ * The page gets the working chain; node gets a stub under the same name and
1616
+ * the same type, so a component test that ran in the wrong project fails on
1617
+ * the line that rendered rather than on a missing export. The published types
1618
+ * are the node build's, so this file is the one description of the facet.
1619
+ */
1620
+ /** The interaction vocabulary a render scenario is handed — the When. */
1621
+ type ComponentVisitor = ReturnType<typeof componentVerbs>;
1622
+ /** The behaviour of a render; assertions stay in the Then, on the result (W1). */
1623
+ type ComponentScenario = (visitor: ComponentVisitor) => Promise<void>;
1624
+ /**
1625
+ * What `.render()` mounts. A React tree is an element (`<PostTable />`); a
1626
+ * vanilla DOM subject is the FUNCTION that fills a container, which is how a
1627
+ * DOM-only module is called in production too.
1628
+ */
1629
+ type RenderSubject = ComponentUi | DomMount;
1630
+ /**
1631
+ * The chain. Every setup returns a new one, so the handle a spec imports never
1632
+ * carries the previous test's contracts, wrapper or clock.
1633
+ */
1634
+ type ComponentChain = {
1635
+ /** Pin the page's `Date` for this render — the clock the component reads. */
1636
+ clock: (iso: string) => ComponentChain;
1566
1637
  /**
1567
- * Project root override for compose detection and init scripts. When
1568
- * absent, the root is auto-discovered by walking up from the calling
1569
- * specification file to the first directory containing
1570
- * `docker/compose.test.yaml`, else the first containing `package.json`
1571
- * (CONVENTIONS A9).
1638
+ * Declare what the network replies — the same contracts, the same queue
1639
+ * and the same strictness as every other facet (D7), served by msw's
1640
+ * worker instead of its node interceptor.
1572
1641
  */
1573
- root?: string;
1642
+ intercept: {
1643
+ (contracts: ContractInput): ComponentChain;
1644
+ (request: ContractRequest, response: ContractResponder | ContractResponse): ComponentChain;
1645
+ };
1646
+ /** Mount the subject, run the scenario, and capture what the page shows. */
1647
+ render: (subject: RenderSubject, scenario?: ComponentScenario) => Promise<RenderResult>;
1574
1648
  /**
1575
- * The app factory — receives the started services record (fully typed)
1576
- * and returns the Hono app. Required in node mode, ignored in compose
1577
- * mode (the app runs as a compose service there).
1649
+ * The page size THIS render gets — the Given of a component that reads
1650
+ * `matchMedia` or a container query. The project's size is restored when
1651
+ * the test ends, so a narrow render never leaks into the next one.
1578
1652
  */
1579
- server?: (services: Services) => HonoApp;
1653
+ viewport: (size: {
1654
+ height: number;
1655
+ width: number;
1656
+ }) => ComponentChain;
1580
1657
  /**
1581
- * Named infrastructure record. Keys become the `database` vocabulary of
1582
- * `.seed()` / `.table()` and drive the compose binding: a handle with no
1583
- * `composeService` option links to the compose service named exactly like
1584
- * its key, else the kebab-case conversion of the key (CONVENTIONS A6).
1658
+ * Wrap every render of this chain — the test-local Given a router stub or
1659
+ * a `<StrictMode>` is. The project's own `wrap` stays outermost: it is the
1660
+ * app's frame, and one test does not get to sit outside it.
1585
1661
  */
1586
- services?: Services;
1662
+ wrap: (wrapper: (ui: ComponentUi) => ComponentUi) => ComponentChain;
1587
1663
  };
1664
+ //#endregion
1665
+ //#region src/specification/facets/website/elements.d.ts
1588
1666
  /**
1589
- * The record returned by {@link startApi | specification.api}. Destructure
1590
- * with the canonical names (CONVENTIONS A3):
1667
+ * The element vocabulary — user-facing descriptors for visit scenarios.
1668
+ * Reads like English (`click(link('Articles'))`), translates to
1669
+ * accessibility-first locators in the browser integration. CSS/XPath is
1670
+ * deliberately not expressible; `testId()` is the single escape hatch.
1591
1671
  *
1592
- * const { api, cleanup, docker } = await specification.api(…);
1672
+ * A descriptor must designate exactly ONE element (CONVENTIONS W3). Two knobs
1673
+ * narrow it, in this order of preference:
1674
+ *
1675
+ * 1. `within(scope, target)` — search inside a landmark, the way a person
1676
+ * would say "the Articles link *in the nav*";
1677
+ * 2. `{ exact: true }` — match the accessible name whole rather than as a
1678
+ * substring, when two names genuinely overlap.
1593
1679
  */
1594
- type ApiHandle<DatabaseKey extends string = string> = {
1595
- api: ApiSpecification<DatabaseKey>;
1596
- /** Stop all infrastructure started by this specification. */
1597
- cleanup: () => Promise<void>;
1680
+ /** Options accepted by every named descriptor. */
1681
+ type ElementOptions = {
1598
1682
  /**
1599
- * Read a running container by id — returns a {@link ContainerAccessor}
1600
- * usable with `await expect(...).toBeRunning()` and read accessors.
1683
+ * Match the accessible name as a whole string. Default is substring —
1684
+ * `link('Articles')` also matches "Read Articles".
1601
1685
  */
1602
- docker: (containerId: string) => ContainerAccessor;
1603
- /** The orchestrator managing the test infrastructure lifecycle. */
1604
- orchestrator: Orchestrator;
1686
+ exact?: boolean;
1605
1687
  };
1606
- declare function startApi<Services extends ServiceRecord>(options: ApiSpecificationOptions<Services>): Promise<ApiHandle<DatabaseKeys<Services>>>;
1607
- //#endregion
1608
- //#region src/specification/facets/cli/start-cli.d.ts
1609
- /** Options for {@link startCli | specification.cli}. */
1610
- type CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
1611
- /**
1612
- * Opt-in Docker awareness. When set, every spec generates a unique
1613
- * test-run id, injects it into the child process env under `envVar`,
1614
- * and exposes `.container(name)` accessors on the result that lazily
1615
- * query Docker. Always declare results with `await using` so leaked
1616
- * containers get force-removed at scope exit (CONVENTIONS B5).
1617
- */
1618
- docker?: DockerSpecConfig;
1619
- /**
1620
- * Named environment SETS for `<case>.spec.yaml` documents. An `env:` entry
1621
- * `frozen` applies the whole `frozen` record; `$WORKDIR` expands and `null`
1622
- * unsets, exactly as in `.env()`. Declared once per app so a document
1623
- * states WHICH ground it stands on, not how to build it.
1624
- */
1625
- env?: Record<string, CliEnv>;
1626
- /**
1627
- * Named servers a `<case>.spec.yaml` document may start (`serve: [mcp]`, or
1628
- * `- mcp: { KEY: value }` to add env to that one). Each entry names the
1629
- * shell `command`, the `ready` regex whose capture group holds the port the
1630
- * server announces, the `url(port)` builder, and the `env` variable that
1631
- * URL is bound to in every run's child.
1632
- */
1633
- serve?: Record<string, LiterateServeRegistration>;
1634
- /**
1635
- * Project-root override (CONVENTIONS A9) — the single meaning of `root`:
1636
- * it anchors compose detection and local-bin resolution for the tested
1637
- * binary. It is NOT a fixtures root; `.fixture()` resolves feature-local
1638
- * or `$FIXTURES/` paths on its own.
1639
- */
1640
- root?: string;
1641
- /**
1642
- * Named infrastructure record started via testcontainers. Connection
1643
- * URLs are injected automatically into the child env: `<KEY>_URL` per
1644
- * service, plus `DATABASE_URL` / `REDIS_URL` when unambiguous
1645
- * (CONVENTIONS B6). `.env()` overrides.
1646
- */
1688
+ /** A user-facing element factory — what every name in this module is. */
1689
+ type ElementFactory = (name: string, options?: ElementOptions) => ElementRef;
1690
+ /** A button (or element with the button role), by accessible name. */
1691
+ declare const button: ElementFactory;
1692
+ /** A form field, by label. */
1693
+ declare const field: ElementFactory;
1694
+ /** A heading, by accessible name. */
1695
+ declare const heading: ElementFactory;
1696
+ /** A link, by accessible name. */
1697
+ declare const link: ElementFactory;
1698
+ /** An element containing the given text. */
1699
+ declare const content: ElementFactory;
1700
+ /** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
1701
+ declare const testId: (id: string) => ElementRef;
1702
+ /** A landmark factory — the optionally-named containers `within()` accepts. */
1703
+ type LandmarkFactory = (name?: string, options?: ElementOptions) => ElementRef;
1704
+ /** The `banner` landmark — the page header. */
1705
+ declare const banner: LandmarkFactory;
1706
+ /** The `complementary` landmark — an `<aside>`, a sidebar. */
1707
+ declare const complementary: LandmarkFactory;
1708
+ /** The `contentinfo` landmark — the page footer. */
1709
+ declare const contentinfo: LandmarkFactory;
1710
+ /** The `form` landmark — a form carrying an accessible name. */
1711
+ declare const form: LandmarkFactory;
1712
+ /** The `main` landmark — the primary content of the document. */
1713
+ declare const main: LandmarkFactory;
1714
+ /** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
1715
+ declare const navigation: LandmarkFactory;
1716
+ /** The `region` landmark — a `<section>` carrying an accessible name. */
1717
+ declare const region: LandmarkFactory;
1718
+ /** The `search` landmark. */
1719
+ declare const search: LandmarkFactory;
1720
+ /**
1721
+ * Restrict a descriptor to the inside of another — the answer to ambiguity,
1722
+ * and the one the framework prefers over a test id.
1723
+ *
1724
+ * click(within(navigation(), link('Articles')))
1725
+ *
1726
+ * Composes outside-in: the scope may itself be scoped, so a deeply nested
1727
+ * target reads `within(main(), within(region('Series'), link('Part 2')))`.
1728
+ * Any descriptor works as a scope, including `testId()` when a container has
1729
+ * no landmark role to stand on.
1730
+ */
1731
+ declare const within: (scope: ElementRef, target: ElementRef) => ElementRef;
1732
+ /** A modal or non-modal `<dialog>`, by accessible name. A closed one is absent. */
1733
+ declare const dialog: LandmarkFactory;
1734
+ /** A live region announcing a result — `role="status"`, an `<output>`. */
1735
+ declare const status: LandmarkFactory;
1736
+ /** A table, by its caption or accessible name. */
1737
+ declare const table: LandmarkFactory;
1738
+ /** A row of a table or grid, by the text of its cells. */
1739
+ declare const row: LandmarkFactory;
1740
+ /** An item of a list, by its text. Retires the `.first()` of an unnamed `<li>`. */
1741
+ declare const listitem: LandmarkFactory;
1742
+ /**
1743
+ * An option of a select or a listbox, by its label — named inside the field
1744
+ * that holds it: `within(field('Channel'), option('LinkedIn'))`.
1745
+ *
1746
+ * Asserted by PRESENCE, not by visibility: the options of a collapsed
1747
+ * `<select>` are in the document and in the accessibility tree, and none of
1748
+ * them has a box on the screen until it is opened. `see(option('LinkedIn'))`
1749
+ * therefore asks "is it offered", and `see(selected(option('LinkedIn')))`
1750
+ * asks which one the field is on.
1751
+ */
1752
+ declare const option: ElementFactory;
1753
+ /**
1754
+ * Where the keyboard is: `see(focused(button('Open')))` asserts the element has
1755
+ * focus, `gone(focused(x))` that it does not.
1756
+ *
1757
+ * A modifier rather than a descriptor of its own, because focus is a STATE of
1758
+ * an element the vocabulary can already name. It is a verb's business and never
1759
+ * a golden's: the accessibility tree carries no focus, so a tree snapshot of a
1760
+ * dialog that took the keyboard and one that did not are byte-identical.
1761
+ */
1762
+ declare const focused: (element: ElementRef) => ElementRef;
1763
+ /**
1764
+ * Whether the element accepts input: `see(disabled(button('Delete')))` asserts
1765
+ * it refuses, `see(enabled(button('Delete')))` that it takes it.
1766
+ *
1767
+ * Modifiers rather than descriptors, for the same reason as `focused`: being
1768
+ * disabled is a state of an element the vocabulary already names. Unlike focus,
1769
+ * the ARIA tree carries it, so a golden pins a whole outline's enablement and
1770
+ * these name the ONE control a test is about. They also buy the direction a
1771
+ * behavioural substitute cannot reach — clicking a disabled control is a
1772
+ * timeout, never an assertion.
1773
+ */
1774
+ declare const disabled: (element: ElementRef) => ElementRef;
1775
+ /** The other direction of {@link disabled} — the element takes input. */
1776
+ declare const enabled: (element: ElementRef) => ElementRef;
1777
+ /**
1778
+ * Which option the field is on: `see(selected(option('LinkedIn')))`, and
1779
+ * `gone(selected(option('X')))` for the one it left.
1780
+ *
1781
+ * A modifier for the same reason as {@link disabled}: being chosen is a state
1782
+ * of an element the vocabulary already names. No matcher answers for it — a
1783
+ * `<option>` is not a checkbox — so both adapters read the `:checked` the CSS
1784
+ * spec gives a selected option.
1785
+ */
1786
+ declare const selected: (element: ElementRef) => ElementRef;
1787
+ /**
1788
+ * What the field holds: `see(valued(field('Title'), 'Launch teaser'))`, and
1789
+ * `gone(valued(field('Title'), '…'))` for a value it no longer holds.
1790
+ *
1791
+ * A modifier for the same reason as {@link disabled}: what a field holds is a
1792
+ * state of an element the vocabulary already names, and this is how the ONE
1793
+ * field a test is about is waited for — a golden pins a whole outline instead.
1794
+ * Read from the node's live value, never from the `value` ATTRIBUTE, which a
1795
+ * controlled input never moves.
1796
+ */
1797
+ declare const valued: (element: ElementRef, value: string) => ElementRef;
1798
+ //#endregion
1799
+ //#region src/specification/contracts/filters.d.ts
1800
+ /**
1801
+ * A text filter on a provider request builder (`openai.chat({ user })`,
1802
+ * `anthropic.messages({ system })`, …).
1803
+ *
1804
+ * A **string is EXACT**. That is deliberate: a substring filter silently
1805
+ * cross-matches — two prompts sharing a preamble both satisfy the first
1806
+ * contract, and the spec goes green on the wrong reply. Looser matching is
1807
+ * explicit, and says which looseness it means:
1808
+ *
1809
+ * - a `RegExp` — pattern matching;
1810
+ * - `match.includes('…')` — containment, the code-only matcher (it never joins
1811
+ * the `{{token}}` file vocabulary, CONVENTIONS D4);
1812
+ * - any other `match.*` matcher, evaluated by the same structural engine the
1813
+ * assertions use.
1814
+ */
1815
+ type TextFilter = Matcher | RegExp | string;
1816
+ //#endregion
1817
+ //#region src/integrations/anthropic/anthropic.d.ts
1818
+ type AnthropicMessagesFilter = {
1819
+ model?: TextFilter;
1820
+ system?: TextFilter;
1821
+ user?: TextFilter;
1822
+ tools?: string[];
1823
+ };
1824
+ /**
1825
+ * Anthropic API intercept helpers.
1826
+ */
1827
+ declare const anthropic: {
1828
+ /**
1829
+ * Request: match Messages API calls, optionally routed through a
1830
+ * custom gateway URL. When used with a JSON fixture file, the data is
1831
+ * returned as-is (no wrapping) because Anthropic fixtures are typically
1832
+ * already in the Messages API response shape.
1833
+ *
1834
+ * @example
1835
+ * anthropic.messages()
1836
+ * anthropic.messages({ system: /classify/ })
1837
+ * anthropic.messages({ user: buildPrompt() }) // string = EXACT equality
1838
+ * anthropic.messages({ user: /classify/ }, GATEWAY)
1839
+ */
1840
+ messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
1841
+ /** Response: wrap data in Anthropic messages format. */
1842
+ reply(data: unknown): ContractResponse;
1843
+ /** Response: return an Anthropic error. */
1844
+ error(status: number, message?: string): ContractResponse;
1845
+ /** Response: simulate a timeout. */
1846
+ timeout(): ContractResponse;
1847
+ };
1848
+ //#endregion
1849
+ //#region src/specification/contracts/http.d.ts
1850
+ /**
1851
+ * Request filters for the generic HTTP provider. Every field is a subset
1852
+ * constraint — a request matches when all provided fields match.
1853
+ */
1854
+ type HttpContractFilter = {
1855
+ /**
1856
+ * Body constraint. An object is a deep SUBSET match (toMatchObject-style)
1857
+ * whose leaf values may be `match.*` matchers; a string is a containment
1858
+ * test and a RegExp a `test()` over the raw text body.
1859
+ */
1860
+ body?: object | RegExp | string;
1861
+ /** Header subset. Names are case-insensitive; string = exact value, RegExp = `test()`. */
1862
+ headers?: Record<string, RegExp | string>;
1863
+ /** Query-param subset. string = exact value, RegExp = `test()`. */
1864
+ query?: Record<string, RegExp | string>;
1865
+ };
1866
+ /** Init options shared by the response builders. */
1867
+ type HttpResponseInit = {
1868
+ /** Delay in ms before responding (for timeout testing). */
1869
+ delay?: number;
1870
+ /** Response headers, merged over the builder's own. */
1871
+ headers?: Record<string, string>;
1872
+ /** HTTP status code. */
1873
+ status?: number;
1874
+ };
1875
+ /**
1876
+ * Generic HTTP contract helpers for any URL. The url is absolute (string or
1877
+ * RegExp), or a PATH FORM starting with `/` — `http.get('/articles/{{uuid}}')`
1878
+ * matches that path on ANY origin, which is what an app calling its own
1879
+ * backend needs. An optional {@link HttpContractFilter} narrows matching by
1880
+ * body, headers, or query — a request that hits the URL/method but fails the
1881
+ * filter counts as unmatched (strict contracts, CONVENTIONS D7).
1882
+ *
1883
+ * @example
1884
+ * defineContract({ request: http.get('/articles/{{uuid}}'), response: http.json(article) })
1885
+ * defineContract({ request: http.post(URL, { body: { user: 'alice' } }), response: http.empty() })
1886
+ */
1887
+ declare const http: {
1888
+ any(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1889
+ delete(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1890
+ get(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1891
+ patch(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1892
+ post(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1893
+ put(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
1894
+ /** Response: a body-less reply (204 by default). */
1895
+ empty(status?: number): ContractResponse;
1896
+ /** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
1897
+ error(status: number, body?: unknown): ContractResponse;
1898
+ /** Response: a JSON body (200 by default). */
1899
+ json(body: unknown, init?: HttpResponseInit): ContractResponse;
1900
+ /**
1901
+ * Response: no response at all — the request fails in transport, the way a
1902
+ * server that is not running fails it. `fetch` rejects; no status is served.
1903
+ */
1904
+ unreachable(): ContractResponse;
1905
+ /** Response: a text body, served as `text/plain` (200 by default). */
1906
+ text(body: string, init?: HttpResponseInit): ContractResponse;
1907
+ };
1908
+ //#endregion
1909
+ //#region src/integrations/openai/openai.d.ts
1910
+ type OpenAIChatFilter = {
1911
+ model?: TextFilter;
1912
+ system?: TextFilter;
1913
+ user?: TextFilter;
1914
+ tools?: string[];
1915
+ temperature?: number;
1916
+ };
1917
+ type OpenAIResponsesFilter = {
1918
+ model?: TextFilter;
1919
+ system?: TextFilter;
1920
+ user?: TextFilter;
1921
+ tools?: string[];
1922
+ };
1923
+ /**
1924
+ * OpenAI API intercept helpers.
1925
+ */
1926
+ declare const openai: {
1927
+ /**
1928
+ * Request: match Chat Completions API calls. STRING filters mean EXACT
1929
+ * equality (pass the app's own prompt builder); loosen deliberately with a
1930
+ * RegExp or `match.includes(...)`.
1931
+ *
1932
+ * @example
1933
+ * openai.chat() // any chat call
1934
+ * openai.chat({ model: 'gpt-4o' }) // exact model
1935
+ * openai.chat({ system: buildPrompt() }) // exact system prompt
1936
+ * openai.chat({ system: /classify/ }) // pattern
1937
+ */
1938
+ chat(filter?: OpenAIChatFilter): ContractRequest;
1939
+ /**
1940
+ * Request: match Responses API calls (AI SDK v5+) with auto-wrapping.
1941
+ * String filters mean EXACT equality (see {@link openai.chat}).
1942
+ * When used with a JSON file, the data is automatically wrapped in the
1943
+ * Responses API envelope.
1944
+ *
1945
+ * @param filter - Optional body filters.
1946
+ * @param url - Custom gateway URL (default: api.openai.com).
1947
+ *
1948
+ * @example
1949
+ * openai.responses({ user: /Report Ingestion/ }, GATEWAY)
1950
+ */
1951
+ responses(filter?: OpenAIResponsesFilter, url?: string): ContractRequest;
1952
+ /**
1953
+ * Response: wrap data in Chat Completions format.
1954
+ *
1955
+ * @example
1956
+ * openai.reply({ categories: ['TECH'] })
1957
+ */
1958
+ reply(data: unknown): ContractResponse;
1959
+ /** Response: return an OpenAI error. */
1960
+ error(status: number, message?: string): ContractResponse;
1961
+ /** Response: return malformed content. */
1962
+ malformed(content: string): ContractResponse;
1963
+ /** Response: simulate a timeout. */
1964
+ timeout(): ContractResponse;
1965
+ };
1966
+ //#endregion
1967
+ //#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>;
1970
+ /**
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;
1975
+ //#endregion
1976
+ //#region src/vitest/mock-of-date.d.ts
1977
+ /** Interface for freezing and resetting the global Date in tests. */
1978
+ type MockDatePort = {
1979
+ /** Restore the real Date object. */
1980
+ reset: () => void;
1981
+ /** Freeze `Date.now()` and `new Date()` to the given value. */
1982
+ set: (date: Date | number | string) => void;
1983
+ };
1984
+ /**
1985
+ * Freeze or reset the global Date for deterministic time-dependent tests.
1986
+ * Wraps the `mockdate` package.
1987
+ */
1988
+ declare const mockOfDate: MockDatePort;
1989
+ //#endregion
1990
+ //#region src/surface.d.ts
1991
+ declare module 'vitest' {
1992
+ interface Assertion<T = any> {
1993
+ /**
1994
+ * Assert the subject is empty — zero rows for a table (async), an
1995
+ * empty stream for a text accessor (console, errors, stdout).
1996
+ */
1997
+ toBeEmpty: T extends TableAccessor ? () => Promise<void> : T extends {
1998
+ readonly comparableText: string;
1999
+ } ? () => Promise<void> : never;
2000
+ /** Assert the container is running. Async — docker-backed subject. */
2001
+ toBeRunning: T extends ContainerAccessor ? () => Promise<void> : never;
2002
+ /**
2003
+ * On `@jterrazz/test` accessors: assert the subject matches a fixture
2004
+ * file under `_expected/<name>` (flat — a slash creates a subfolder;
2005
+ * `.http` format for `result.response`). Async for filesystem/directory
2006
+ * subjects (tree compare on disk), and for a subject captured INSIDE a
2007
+ * page (`result.tree`, `result.html`, … — the file crosses the browser
2008
+ * seam through a server command).
2009
+ *
2010
+ * Pass `{ frozen: true }` to opt a single fixture OUT of update-mode
2011
+ * rewriting: a frozen fixture is never overwritten under `TEST_UPDATE=1`
2012
+ * (or vitest `-u`), and a frozen mismatch/missing fixture still throws.
2013
+ * Use it for a deliberately-wrong fixture whose diff/error rendering is
2014
+ * the behaviour under test.
2015
+ */
2016
+ toMatch: T extends DirectoryAccessor | FilesystemAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends {
2017
+ readonly capturedInPage: true;
2018
+ } ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends JsonAccessor | ResponseAccessor | TextAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => void : (expected: RegExp | string, options?: MatchFixtureOptions) => void;
2019
+ /**
2020
+ * Assert the table contains exactly the given rows for the given
2021
+ * columns. Cells accept `match.*` dynamic-value matchers. Async —
2022
+ * queries the database.
2023
+ */
2024
+ toMatchRows: T extends TableAccessor ? (expected: {
2025
+ columns: string[];
2026
+ rows: readonly (readonly unknown[])[];
2027
+ }) => Promise<void> : never;
2028
+ }
2029
+ }
2030
+ //#endregion
2031
+ //#region src/specification/facets/_common/orchestrator.d.ts
2032
+ type OrchestratorOptions = {
2033
+ /**
2034
+ * Named infrastructure record. Keys become the database vocabulary of the
2035
+ * spec (`.seed()` / `.table()` `database` option) and drive the compose
2036
+ * binding: a handle with no explicit `composeService` links to the compose
2037
+ * service named exactly like its key, else the kebab-case conversion of the
2038
+ * key (`analyticsDb` → `analytics-db`) — see {@link resolveComposeBinding}.
2039
+ */
2040
+ services: Record<string, ServiceHandle>;
2041
+ mode: 'e2e' | 'integration';
2042
+ /**
2043
+ * The project root, already resolved. REQUIRED: the constructors derive it
2044
+ * by walking up from the calling specification file (CONVENTIONS A9), and
2045
+ * the orchestrator is handed the answer. Defaulting to `process.cwd()` here
2046
+ * meant one silent second opinion about what the project is — the runner
2047
+ * resolving from the caller, this from wherever the runner happened to be
2048
+ * launched, which in a workspace is rarely the same directory.
2049
+ */
2050
+ root: string;
2051
+ /** Compose project name — used for per-worker stack isolation. */
2052
+ projectName?: string;
2053
+ };
2054
+ /**
2055
+ * Orchestrator for test infrastructure.
2056
+ * Integration: starts services via testcontainers.
2057
+ * E2E: runs full docker compose up.
2058
+ */
2059
+ declare class Orchestrator {
2060
+ private readonly services;
2061
+ private readonly mode;
2062
+ private readonly root;
2063
+ private readonly projectName;
2064
+ private running;
2065
+ private composeStack;
2066
+ private composeHandles;
2067
+ private started;
2068
+ constructor(options: OrchestratorOptions);
2069
+ /**
2070
+ * Bind each declared handle to a compose service (CONVENTIONS A6): a record
2071
+ * key resolves to the service named exactly like it, else the kebab-case
2072
+ * conversion of the key; an explicit `composeService` wins. Throws on an
2073
+ * ambiguous binding (both names present). Runs once compose config is known
2074
+ * so the two-step resolution can consult the real service list.
2075
+ */
2076
+ private resolveBindings;
2077
+ /**
2078
+ * Start declared services via testcontainers (integration mode).
2079
+ * Phase 1: start all containers in parallel (the slow part).
2080
+ * Phase 2: wire connections, healthcheck, and init sequentially (fast).
2081
+ */
2082
+ start(): Promise<void>;
2083
+ /**
2084
+ * Stop testcontainers (integration mode).
2085
+ */
2086
+ stop(): Promise<void>;
2087
+ /**
2088
+ * Start full docker compose stack (e2e mode).
2089
+ * Auto-detects infra services and creates handles for them.
2090
+ */
2091
+ startCompose(): Promise<void>;
2092
+ /**
2093
+ * Stop docker compose stack (e2e mode).
2094
+ */
2095
+ stopCompose(): Promise<void>;
2096
+ /**
2097
+ * Get the default database — the first declared handle that is one.
2098
+ */
2099
+ getDatabase(): DatabasePort | null;
2100
+ /**
2101
+ * Get all database services keyed by their record key (declared services)
2102
+ * or compose service name (stack-detected services).
2103
+ */
2104
+ getDatabases(): Map<string, DatabasePort>;
2105
+ private allHandles;
2106
+ /**
2107
+ * Get app URL from compose (e2e mode).
2108
+ */
2109
+ getAppUrl(): null | string;
2110
+ }
2111
+ //#endregion
2112
+ //#region src/specification/facets/_common/services.d.ts
2113
+ /**
2114
+ * Infrastructure services declared as a named record. Keys become the typed
2115
+ * vocabulary of the whole spec: the server factory receives the same record,
2116
+ * and `.seed()` / `.table()` target databases by key.
2117
+ */
2118
+ type ServiceRecord = Record<string, ServiceHandle>;
2119
+ /** Keys of a services record whose handles are databases. */
2120
+ type DatabaseKeys<Services extends ServiceRecord> = { [K in keyof Services]: Services[K] extends DatabasePort ? K & string : never; }[keyof Services];
2121
+ //#endregion
2122
+ //#region src/specification/facets/api/start-api.d.ts
2123
+ /** Any object with a request method compatible with Hono's app.request(). */
2124
+ type HonoApp = {
2125
+ request: (path: string, init?: RequestInit) => Promise<Response> | Response;
2126
+ };
2127
+ /** Execution mode — exists ONLY on `specification.api()` (CONVENTIONS A5). */
2128
+ type SpecificationMode = 'compose' | 'node';
2129
+ /** Options for {@link startApi | specification.api}. */
2130
+ type ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2131
+ /**
2132
+ * Execution mode override. Resolution: `options.mode` >
2133
+ * `process.env.TEST_MODE` > `'node'`. Never hardcode this in a
2134
+ * specification file when `server` is defined — set it per vitest
2135
+ * project via `env: { TEST_MODE: 'compose' }` (CONVENTIONS A5).
2136
+ */
2137
+ mode?: SpecificationMode;
2138
+ /**
2139
+ * Project root override for compose detection and init scripts. When
2140
+ * absent, the root is auto-discovered by walking up from the calling
2141
+ * specification file to the first directory containing
2142
+ * `docker/compose.test.yaml`, else the first containing `package.json`
2143
+ * (CONVENTIONS A9).
2144
+ */
2145
+ root?: string;
2146
+ /**
2147
+ * The app factory — receives the started services record (fully typed)
2148
+ * and returns the Hono app. Required in node mode, ignored in compose
2149
+ * mode (the app runs as a compose service there).
2150
+ */
2151
+ server?: (services: Services) => HonoApp;
2152
+ /**
2153
+ * Named infrastructure record. Keys become the `database` vocabulary of
2154
+ * `.seed()` / `.table()` and drive the compose binding: a handle with no
2155
+ * `composeService` option links to the compose service named exactly like
2156
+ * its key, else the kebab-case conversion of the key (CONVENTIONS A6).
2157
+ */
2158
+ services?: Services;
2159
+ };
2160
+ /**
2161
+ * The record returned by {@link startApi | specification.api}. Destructure
2162
+ * with the canonical names (CONVENTIONS A3):
2163
+ *
2164
+ * const { api, cleanup, docker } = await specification.api(…);
2165
+ */
2166
+ type ApiHandle<DatabaseKey extends string = string> = {
2167
+ api: ApiSpecification<DatabaseKey>;
2168
+ /** Stop all infrastructure started by this specification. */
2169
+ cleanup: () => Promise<void>;
2170
+ /**
2171
+ * Read a running container by id — returns a {@link ContainerAccessor}
2172
+ * usable with `await expect(...).toBeRunning()` and read accessors.
2173
+ */
2174
+ docker: (containerId: string) => ContainerAccessor;
2175
+ /** The orchestrator managing the test infrastructure lifecycle. */
2176
+ orchestrator: Orchestrator;
2177
+ };
2178
+ declare function startApi<Services extends ServiceRecord>(options: ApiSpecificationOptions<Services>): Promise<ApiHandle<DatabaseKeys<Services>>>;
2179
+ //#endregion
2180
+ //#region src/specification/facets/cli/start-cli.d.ts
2181
+ /** Options for {@link startCli | specification.cli}. */
2182
+ type CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
2183
+ /**
2184
+ * Opt-in Docker awareness. When set, every spec generates a unique
2185
+ * test-run id, injects it into the child process env under `envVar`,
2186
+ * and exposes `.container(name)` accessors on the result that lazily
2187
+ * query Docker. Always declare results with `await using` so leaked
2188
+ * containers get force-removed at scope exit (CONVENTIONS B5).
2189
+ */
2190
+ docker?: DockerSpecConfig;
2191
+ /**
2192
+ * Named environment SETS for `<case>.spec.yaml` documents. An `env:` entry
2193
+ * `frozen` applies the whole `frozen` record; `$WORKDIR` expands and `null`
2194
+ * unsets, exactly as in `.env()`. Declared once per app so a document
2195
+ * states WHICH ground it stands on, not how to build it.
2196
+ */
2197
+ env?: Record<string, CliEnv>;
2198
+ /**
2199
+ * Named servers a `<case>.spec.yaml` document may start (`serve: [mcp]`, or
2200
+ * `- mcp: { KEY: value }` to add env to that one). Each entry names the
2201
+ * shell `command`, the `ready` regex whose capture group holds the port the
2202
+ * server announces, the `url(port)` builder, and the `env` variable that
2203
+ * URL is bound to in every run's child.
2204
+ */
2205
+ serve?: Record<string, LiterateServeRegistration>;
2206
+ /**
2207
+ * Project-root override (CONVENTIONS A9) — the single meaning of `root`:
2208
+ * it anchors compose detection and local-bin resolution for the tested
2209
+ * binary. It is NOT a fixtures root; `.fixture()` resolves feature-local
2210
+ * or `$FIXTURES/` paths on its own.
2211
+ */
2212
+ root?: string;
2213
+ /**
2214
+ * Named infrastructure record started via testcontainers. Connection
2215
+ * URLs are injected automatically into the child env: `<KEY>_URL` per
2216
+ * service, plus `DATABASE_URL` / `REDIS_URL` when unambiguous
2217
+ * (CONVENTIONS B6). `.env()` overrides.
2218
+ */
1647
2219
  services?: Services;
1648
2220
  /**
1649
2221
  * Escape hatch: normaliser applied to result.stdout / result.stderr
@@ -1900,6 +2472,10 @@ type Specification = {
1900
2472
  };
1901
2473
  declare const specification: Specification;
1902
2474
  //#endregion
2475
+ //#region src/specification/facets/component/component.node.d.ts
2476
+ /** Present, typed, and refusing — every setup and the terminal action alike. */
2477
+ declare const component: ComponentChain;
2478
+ //#endregion
1903
2479
  //#region src/specification/literate/spec-document.d.ts
1904
2480
  /**
1905
2481
  * The facets a document may describe. `cli` is the only one implemented; the
@@ -1988,93 +2564,51 @@ declare function inspectContainer(id: string): unknown;
1988
2564
  /** Force-remove the given container IDs in a single call. Errors are swallowed. */
1989
2565
  declare function removeContainers(ids: string[]): void;
1990
2566
  //#endregion
1991
- //#region src/specification/ports/container.port.d.ts
1992
- /**
1993
- * Abstract container interface.
1994
- * Represents a running service (database, cache, etc.)
1995
- */
1996
- type ContainerPort = {
1997
- /** Start the container and wait until ready. */
1998
- start: () => Promise<void>;
1999
- /** Stop and remove the container. */
2000
- stop: () => Promise<void>;
2001
- /** Get the mapped host port for a container port. */
2002
- getMappedPort: (containerPort: number) => number;
2003
- /** Get the host to connect to. */
2004
- getHost: () => string;
2005
- /** Get a full connection string for this service. */
2006
- getConnectionString: () => string;
2007
- /** Get container logs (stdout + stderr). */
2008
- getLogs: () => Promise<string>;
2009
- };
2010
- //#endregion
2011
- //#region src/specification/facets/website/elements.d.ts
2567
+ //#region src/specification/facets/_common/result/text-subject.d.ts
2012
2568
  /**
2013
- * The element vocabulary — user-facing descriptors for visit scenarios.
2014
- * Reads like English (`click(link('Articles'))`), translates to
2015
- * accessibility-first locators in the browser integration. CSS/XPath is
2016
- * deliberately not expressible; `testId()` is the single escape hatch.
2017
- *
2018
- * A descriptor must designate exactly ONE element (CONVENTIONS W3). Two knobs
2019
- * narrow it, in this order of preference:
2569
+ * Wrap an arbitrary string into a {@link TextAccessor} anchored on the calling
2570
+ * test's directory — the same caller-detection the builders use.
2020
2571
  *
2021
- * 1. `within(scope, target)` — search inside a landmark, the way a person
2022
- * would say "the Articles link *in the nav*";
2023
- * 2. `{ exact: true }` — match the accessible name whole rather than as a
2024
- * substring, when two names genuinely overlap.
2025
- */
2026
- /** Options accepted by every named descriptor. */
2027
- type ElementOptions = {
2028
- /**
2029
- * Match the accessible name as a whole string. Default is substring —
2030
- * `link('Articles')` also matches "Read Articles".
2031
- */
2032
- exact?: boolean;
2033
- };
2034
- /** A user-facing element factory — what every name in this module is. */
2035
- type ElementFactory = (name: string, options?: ElementOptions) => ElementRef;
2036
- /** A button (or element with the button role), by accessible name. */
2037
- declare const button: ElementFactory;
2038
- /** A form field, by label. */
2039
- declare const field: ElementFactory;
2040
- /** A heading, by accessible name. */
2041
- declare const heading: ElementFactory;
2042
- /** A link, by accessible name. */
2043
- declare const link: ElementFactory;
2044
- /** An element containing the given text. */
2045
- declare const content: ElementFactory;
2046
- /** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
2047
- declare const testId: (id: string) => ElementRef;
2048
- /** A landmark factory — the optionally-named containers `within()` accepts. */
2049
- type LandmarkFactory = (name?: string, options?: ElementOptions) => ElementRef;
2050
- /** The `banner` landmark — the page header. */
2051
- declare const banner: LandmarkFactory;
2052
- /** The `complementary` landmark — an `<aside>`, a sidebar. */
2053
- declare const complementary: LandmarkFactory;
2054
- /** The `contentinfo` landmark — the page footer. */
2055
- declare const contentinfo: LandmarkFactory;
2056
- /** The `form` landmark — a form carrying an accessible name. */
2057
- declare const form: LandmarkFactory;
2058
- /** The `main` landmark — the primary content of the document. */
2059
- declare const main: LandmarkFactory;
2060
- /** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
2061
- declare const navigation: LandmarkFactory;
2062
- /** The `region` landmark — a `<section>` carrying an accessible name. */
2063
- declare const region: LandmarkFactory;
2064
- /** The `search` landmark. */
2065
- declare const search: LandmarkFactory;
2066
- /**
2067
- * Restrict a descriptor to the inside of another — the answer to ambiguity,
2068
- * and the one the framework prefers over a test id.
2572
+ * The product surface of a test framework is its own error messages, checker
2573
+ * output, and reports; those deserve the same goldening as any other output.
2574
+ * `text()` makes an ad-hoc string a first-class snapshot subject:
2069
2575
  *
2070
- * click(within(navigation(), link('Articles')))
2576
+ * ```typescript
2577
+ * const message = await catchMessage(() => expect(result.response).toMatch('wrong-body.http'));
2578
+ * expect(text(message)).toMatch('wrong-body-error.txt'); // resolves to _expected/
2579
+ * ```
2071
2580
  *
2072
- * Composes outside-in: the scope may itself be scoped, so a deeply nested
2073
- * target reads `within(main(), within(region('Series'), link('Part 2')))`.
2074
- * Any descriptor works as a scope, including `testId()` when a container has
2075
- * no landmark role to stand on.
2581
+ * ANSI is stripped before every comparison (the raw form stays on `.text`),
2582
+ * the `{{token}}` grammar applies to the fixture, and `.grep()` composition
2583
+ * works exactly as on any stream accessor.
2584
+ *
2585
+ * The ANCHOR is why this sits apart from the accessor it builds: reading the
2586
+ * caller's frame means reading a stack and a real path, which is node's alone.
2587
+ * {@link TextAccessor} itself is a pure projection over a captured string, so
2588
+ * it loads in a page — and the browser entry builds its own `text()`, where
2589
+ * the golden commands resolve `_expected/` from the test's path server-side.
2076
2590
  */
2077
- declare const within: (scope: ElementRef, target: ElementRef) => ElementRef;
2591
+ declare function text(value: string): TextAccessor;
2592
+ //#endregion
2593
+ //#region src/specification/ports/container.port.d.ts
2594
+ /**
2595
+ * Abstract container interface.
2596
+ * Represents a running service (database, cache, etc.)
2597
+ */
2598
+ type ContainerPort = {
2599
+ /** Start the container and wait until ready. */
2600
+ start: () => Promise<void>;
2601
+ /** Stop and remove the container. */
2602
+ stop: () => Promise<void>;
2603
+ /** Get the mapped host port for a container port. */
2604
+ getMappedPort: (containerPort: number) => number;
2605
+ /** Get the host to connect to. */
2606
+ getHost: () => string;
2607
+ /** Get a full connection string for this service. */
2608
+ getConnectionString: () => string;
2609
+ /** Get container logs (stdout + stderr). */
2610
+ getLogs: () => Promise<string>;
2611
+ };
2078
2612
  //#endregion
2079
2613
  //#region src/integrations/postgres/postgres.d.ts
2080
2614
  type PostgresOptions = {
@@ -2240,228 +2774,4 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
2240
2774
  */
2241
2775
  declare function sqlite(options?: SqliteOptions): SqliteHandle;
2242
2776
  //#endregion
2243
- //#region src/specification/contracts/filters.d.ts
2244
- /**
2245
- * A text filter on a provider request builder (`openai.chat({ user })`,
2246
- * `anthropic.messages({ system })`, …).
2247
- *
2248
- * A **string is EXACT**. That is deliberate: a substring filter silently
2249
- * cross-matches — two prompts sharing a preamble both satisfy the first
2250
- * contract, and the spec goes green on the wrong reply. Looser matching is
2251
- * explicit, and says which looseness it means:
2252
- *
2253
- * - a `RegExp` — pattern matching;
2254
- * - `match.includes('…')` — containment, the code-only matcher (it never joins
2255
- * the `{{token}}` file vocabulary, CONVENTIONS D4);
2256
- * - any other `match.*` matcher, evaluated by the same structural engine the
2257
- * assertions use.
2258
- */
2259
- type TextFilter = Matcher | RegExp | string;
2260
- //#endregion
2261
- //#region src/integrations/anthropic/anthropic.d.ts
2262
- type AnthropicMessagesFilter = {
2263
- model?: TextFilter;
2264
- system?: TextFilter;
2265
- user?: TextFilter;
2266
- tools?: string[];
2267
- };
2268
- /**
2269
- * Anthropic API intercept helpers.
2270
- */
2271
- declare const anthropic: {
2272
- /**
2273
- * Request: match Messages API calls, optionally routed through a
2274
- * custom gateway URL. When used with a JSON fixture file, the data is
2275
- * returned as-is (no wrapping) because Anthropic fixtures are typically
2276
- * already in the Messages API response shape.
2277
- *
2278
- * @example
2279
- * anthropic.messages()
2280
- * anthropic.messages({ system: /classify/ })
2281
- * anthropic.messages({ user: buildPrompt() }) // string = EXACT equality
2282
- * anthropic.messages({ user: /classify/ }, GATEWAY)
2283
- */
2284
- messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
2285
- /** Response: wrap data in Anthropic messages format. */
2286
- reply(data: unknown): ContractResponse;
2287
- /** Response: return an Anthropic error. */
2288
- error(status: number, message?: string): ContractResponse;
2289
- /** Response: simulate a timeout. */
2290
- timeout(): ContractResponse;
2291
- };
2292
- //#endregion
2293
- //#region src/specification/contracts/http.d.ts
2294
- /**
2295
- * Request filters for the generic HTTP provider. Every field is a subset
2296
- * constraint — a request matches when all provided fields match.
2297
- */
2298
- type HttpContractFilter = {
2299
- /**
2300
- * Body constraint. An object is a deep SUBSET match (toMatchObject-style)
2301
- * whose leaf values may be `match.*` matchers; a string is a containment
2302
- * test and a RegExp a `test()` over the raw text body.
2303
- */
2304
- body?: object | RegExp | string;
2305
- /** Header subset. Names are case-insensitive; string = exact value, RegExp = `test()`. */
2306
- headers?: Record<string, RegExp | string>;
2307
- /** Query-param subset. string = exact value, RegExp = `test()`. */
2308
- query?: Record<string, RegExp | string>;
2309
- };
2310
- /** Init options shared by the response builders. */
2311
- type HttpResponseInit = {
2312
- /** Delay in ms before responding (for timeout testing). */
2313
- delay?: number;
2314
- /** Response headers, merged over the builder's own. */
2315
- headers?: Record<string, string>;
2316
- /** HTTP status code. */
2317
- status?: number;
2318
- };
2319
- /**
2320
- * Generic HTTP contract helpers for any URL. The url is absolute (string or
2321
- * RegExp), or a PATH FORM starting with `/` — `http.get('/articles/{{uuid}}')`
2322
- * matches that path on ANY origin, which is what an app calling its own
2323
- * backend needs. An optional {@link HttpContractFilter} narrows matching by
2324
- * body, headers, or query — a request that hits the URL/method but fails the
2325
- * filter counts as unmatched (strict contracts, CONVENTIONS D7).
2326
- *
2327
- * @example
2328
- * defineContract({ request: http.get('/articles/{{uuid}}'), response: http.json(article) })
2329
- * defineContract({ request: http.post(URL, { body: { user: 'alice' } }), response: http.empty() })
2330
- */
2331
- declare const http: {
2332
- any(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2333
- delete(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2334
- get(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2335
- patch(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2336
- post(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2337
- put(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2338
- /** Response: a body-less reply (204 by default). */
2339
- empty(status?: number): ContractResponse;
2340
- /** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
2341
- error(status: number, body?: unknown): ContractResponse;
2342
- /** Response: a JSON body (200 by default). */
2343
- json(body: unknown, init?: HttpResponseInit): ContractResponse;
2344
- /** Response: a text body, served as `text/plain` (200 by default). */
2345
- text(body: string, init?: HttpResponseInit): ContractResponse;
2346
- };
2347
- //#endregion
2348
- //#region src/integrations/openai/openai.d.ts
2349
- type OpenAIChatFilter = {
2350
- model?: TextFilter;
2351
- system?: TextFilter;
2352
- user?: TextFilter;
2353
- tools?: string[];
2354
- temperature?: number;
2355
- };
2356
- type OpenAIResponsesFilter = {
2357
- model?: TextFilter;
2358
- system?: TextFilter;
2359
- user?: TextFilter;
2360
- tools?: string[];
2361
- };
2362
- /**
2363
- * OpenAI API intercept helpers.
2364
- */
2365
- declare const openai: {
2366
- /**
2367
- * Request: match Chat Completions API calls. STRING filters mean EXACT
2368
- * equality (pass the app's own prompt builder); loosen deliberately with a
2369
- * RegExp or `match.includes(...)`.
2370
- *
2371
- * @example
2372
- * openai.chat() // any chat call
2373
- * openai.chat({ model: 'gpt-4o' }) // exact model
2374
- * openai.chat({ system: buildPrompt() }) // exact system prompt
2375
- * openai.chat({ system: /classify/ }) // pattern
2376
- */
2377
- chat(filter?: OpenAIChatFilter): ContractRequest;
2378
- /**
2379
- * Request: match Responses API calls (AI SDK v5+) with auto-wrapping.
2380
- * String filters mean EXACT equality (see {@link openai.chat}).
2381
- * When used with a JSON file, the data is automatically wrapped in the
2382
- * Responses API envelope.
2383
- *
2384
- * @param filter - Optional body filters.
2385
- * @param url - Custom gateway URL (default: api.openai.com).
2386
- *
2387
- * @example
2388
- * openai.responses({ user: /Report Ingestion/ }, GATEWAY)
2389
- */
2390
- responses(filter?: OpenAIResponsesFilter, url?: string): ContractRequest;
2391
- /**
2392
- * Response: wrap data in Chat Completions format.
2393
- *
2394
- * @example
2395
- * openai.reply({ categories: ['TECH'] })
2396
- */
2397
- reply(data: unknown): ContractResponse;
2398
- /** Response: return an OpenAI error. */
2399
- error(status: number, message?: string): ContractResponse;
2400
- /** Response: return malformed content. */
2401
- malformed(content: string): ContractResponse;
2402
- /** Response: simulate a timeout. */
2403
- timeout(): ContractResponse;
2404
- };
2405
- //#endregion
2406
- //#region src/vitest/mock-of.d.ts
2407
- /** Factory signature that creates a deep mock proxy for any interface. */
2408
- type MockPort = <T>() => DeepMockProxy<T>;
2409
- /**
2410
- * Create a deep mock proxy for a given type.
2411
- * Wraps `vitest-mock-extended`'s `mockDeep` for convenient port mocking.
2412
- */
2413
- declare const mockOf: MockPort;
2414
- //#endregion
2415
- //#region src/vitest/mock-of-date.d.ts
2416
- /** Interface for freezing and resetting the global Date in tests. */
2417
- type MockDatePort = {
2418
- /** Restore the real Date object. */
2419
- reset: () => void;
2420
- /** Freeze `Date.now()` and `new Date()` to the given value. */
2421
- set: (date: Date | number | string) => void;
2422
- };
2423
- /**
2424
- * Freeze or reset the global Date for deterministic time-dependent tests.
2425
- * Wraps the `mockdate` package.
2426
- */
2427
- declare const mockOfDate: MockDatePort;
2428
- //#endregion
2429
- //#region src/index.d.ts
2430
- declare module 'vitest' {
2431
- interface Assertion<T = any> {
2432
- /**
2433
- * Assert the subject is empty — zero rows for a table (async), an
2434
- * empty stream for a text accessor (console, errors, stdout).
2435
- */
2436
- toBeEmpty: T extends TableAccessor ? () => Promise<void> : T extends {
2437
- readonly comparableText: string;
2438
- } ? () => Promise<void> : never;
2439
- /** Assert the container is running. Async — docker-backed subject. */
2440
- toBeRunning: T extends ContainerAccessor ? () => Promise<void> : never;
2441
- /**
2442
- * On `@jterrazz/test` accessors: assert the subject matches a fixture
2443
- * file under `_expected/<name>` (flat — a slash creates a subfolder;
2444
- * `.http` format for `result.response`). Async for filesystem/directory
2445
- * subjects (tree compare on disk). Other subjects keep vitest-native
2446
- * `toMatch` semantics (string substring / regexp).
2447
- *
2448
- * Pass `{ frozen: true }` to opt a single fixture OUT of update-mode
2449
- * rewriting: a frozen fixture is never overwritten under `TEST_UPDATE=1`
2450
- * (or vitest `-u`), and a frozen mismatch/missing fixture still throws.
2451
- * Use it for a deliberately-wrong fixture whose diff/error rendering is
2452
- * the behaviour under test.
2453
- */
2454
- toMatch: T extends DirectoryAccessor | FilesystemAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends JsonAccessor | ResponseAccessor | TextAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => void : (expected: RegExp | string, options?: MatchFixtureOptions) => void;
2455
- /**
2456
- * Assert the table contains exactly the given rows for the given
2457
- * columns. Cells accept `match.*` dynamic-value matchers. Async —
2458
- * queries the database.
2459
- */
2460
- toMatchRows: T extends TableAccessor ? (expected: {
2461
- columns: string[];
2462
- rows: readonly (readonly unknown[])[];
2463
- }) => Promise<void> : never;
2464
- }
2465
- }
2466
- //#endregion
2467
- 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, 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 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, 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, content, contentinfo, defineContract, defineContracts, field, findContainersByLabel, form, heading, http, inspectContainer, link, main, match, mockOf, mockOfDate, navigation, openai, postgres, redis, region, removeContainers, search, specification, sqlite, testId, text, within };
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 };