@jterrazz/test 10.1.0 → 11.0.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
@@ -10,12 +10,14 @@ import { DeepMockProxy } from "vitest-mock-extended";
10
10
  * (`expected/*.txt`). One grammar, one vocabulary (CONVENTIONS D4).
11
11
  */
12
12
  /** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
13
- type MatcherKind = 'any' | 'base64' | 'date' | 'duration' | 'email' | 'float' | 'hex' | 'int' | 'ip' | 'iso8601' | 'number' | 'path' | 'port' | 'ref' | 'regex' | 'semver' | 'sha' | 'string' | 'time' | 'ulid' | 'url' | 'uuid' | 'workdir';
13
+ 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';
14
14
  /**
15
15
  * A dynamic-value matcher. Created via the {@link match} factories — never
16
16
  * constructed directly by user code.
17
17
  */
18
18
  declare class Matcher {
19
+ /** For kind 'includes': the substring the actual value must contain. */
20
+ readonly includes?: string;
19
21
  readonly kind: MatcherKind;
20
22
  /** For kind 'ref': the capture to assert inequality against. */
21
23
  readonly notRef?: string;
@@ -24,6 +26,7 @@ declare class Matcher {
24
26
  /** For kind 'regex': the pattern the actual value must match. */
25
27
  readonly regex?: RegExp;
26
28
  constructor(kind: MatcherKind, options?: {
29
+ includes?: string;
27
30
  notRef?: string;
28
31
  refName?: string;
29
32
  regex?: RegExp;
@@ -61,6 +64,13 @@ declare const match: {
61
64
  float: () => Matcher;
62
65
  /** Matches a hexadecimal string. */
63
66
  hex: () => Matcher;
67
+ /**
68
+ * Matches a string CONTAINING the given substring. Code-only, like
69
+ * {@link match.regex} — the `{{token}}` fixture vocabulary does not grow.
70
+ * The explicit escape hatch now that contract string filters
71
+ * (`openai.chat({ user })`) mean exact equality.
72
+ */
73
+ includes: (substring: string) => Matcher;
64
74
  /** Matches an integer (or an integer string in text contexts). */
65
75
  int: () => Matcher;
66
76
  /** Matches an IPv4 address. */
@@ -389,43 +399,48 @@ interface CliPort {
389
399
  //#endregion
390
400
  //#region src/core/contracts/types.d.ts
391
401
  /**
392
- * The observed outgoing request, reduced to what trigger matchers inspect.
393
- * Built once per request by the MSW integration and handed to
394
- * {@link InterceptTrigger.match}.
402
+ * The observed outgoing request, reduced to what contract matchers inspect.
403
+ * Built once per request by the engine (MSW on api/jobs, the stub backend on
404
+ * website/mobile) and handed to {@link ContractRequest.match} and to a
405
+ * {@link ContractResponder}.
395
406
  */
396
407
  interface MatchableRequest {
397
408
  /** Parsed JSON body when the payload is JSON, the raw text otherwise, or `null` when absent. */
398
409
  body: unknown;
399
410
  /** Request headers, keyed by lowercased header name. */
400
411
  headers: Record<string, string>;
401
- /** The fully-qualified request URL (including any query string). */
412
+ /** Uppercased HTTP method of the observed request. */
413
+ method: string;
414
+ /** The request URL — fully-qualified, or origin-relative for the stub backend. */
402
415
  url: string;
403
416
  }
404
417
  /**
405
- * An intercept trigger describes which HTTP request to match.
418
+ * The request half of a contract: which outgoing call it speaks for.
419
+ *
420
+ * `url` is either an absolute URL (string or RegExp), or a PATH FORM starting
421
+ * with `/` (`/articles/{{uuid}}`) which matches any origin — the shape a
422
+ * website/mobile app's own backend calls take. `{{token}}` segments compare
423
+ * structurally, declared query params are a subset of the observed ones.
406
424
  */
407
- interface InterceptTrigger {
408
- /** Adapter name - must match the folder prefix in file-based intercepts. */
425
+ interface ContractRequest {
426
+ /** Adapter name `http` | `openai` | `anthropic`. */
409
427
  adapter: string;
410
- /** HTTP method to match. */
428
+ /** HTTP method to match. `*` matches any method. */
411
429
  method: string;
412
- /** URL pattern to match (string for exact prefix, RegExp for pattern). */
430
+ /** Absolute URL (string | RegExp) or an any-origin path form (`/articles/{{uuid}}`). */
413
431
  url: RegExp | string;
414
- /** Optional request matcher - the handler only fires if this returns true. */
432
+ /** Optional request matcher the contract only fires if this returns true. */
415
433
  match?: (request: MatchableRequest) => boolean;
416
- /**
417
- * Transform raw JSON data into a provider-specific response envelope.
418
- * Called when .intercept(trigger, 'adapter/file.json') loads a file.
419
- */
420
- wrap: (data: unknown) => InterceptResponse;
434
+ /** Transform raw data into a provider-specific response envelope. */
435
+ wrap: (data: unknown) => ContractResponse;
421
436
  }
422
437
  /**
423
- * An intercept response describes what to return when the trigger matches.
438
+ * The response half of a contract: what to reply when the request matches.
424
439
  */
425
- interface InterceptResponse {
440
+ interface ContractResponse {
426
441
  /** HTTP status code (default: 200). */
427
442
  status?: number;
428
- /** Response body (will be JSON.stringified). */
443
+ /** Response body an object is JSON, a string is text, `null`/`undefined` is empty. */
429
444
  body: unknown;
430
445
  /** Response headers. */
431
446
  headers?: Record<string, string>;
@@ -434,67 +449,101 @@ interface InterceptResponse {
434
449
  }
435
450
  /**
436
451
  * A dynamic response: computed from the observed request at the moment the
437
- * intercept is consumed, rather than fixed ahead of time. Handed the same
438
- * {@link MatchableRequest} the trigger matched on, so the reply can echo or
439
- * derive from the request body/headers/url.
452
+ * contract is served, rather than fixed ahead of time. Handed the same
453
+ * {@link MatchableRequest} the request half matched on, so the reply can echo
454
+ * or derive from the body/headers/url.
440
455
  */
441
- type InterceptResponder = (request: MatchableRequest) => InterceptResponse;
456
+ type ContractResponder = (request: MatchableRequest) => ContractResponse;
442
457
  /**
443
- * What an intercept replies with: either a fixed {@link InterceptResponse} or
444
- * an {@link InterceptResponder} evaluated per consumed request.
458
+ * What a contract replies with: either a fixed {@link ContractResponse} or a
459
+ * {@link ContractResponder} evaluated per served request.
445
460
  */
446
- type InterceptResponseValue = InterceptResponder | InterceptResponse;
447
- /**
448
- * A fully resolved intercept entry ready to be registered with MSW.
449
- */
450
- interface InterceptEntry {
451
- trigger: InterceptTrigger;
452
- response: InterceptResponseValue;
453
- }
461
+ type ContractResponseValue = ContractResponder | ContractResponse;
454
462
  //#endregion
455
463
  //#region src/core/contracts/contract.d.ts
456
464
  /**
457
- * A declared external interaction: what to match and what to reply, together
458
- * in one named artifact. Contracts live in flat TypeScript files under
459
- * `contracts/` next to the tests that use them `<name>.<provider>.ts` with
460
- * `provider { openai, anthropic, http }` (CONVENTIONS C4) so the business
461
- * payload (prompts, JSON responses) is visible at a glance while the real
462
- * HTTP call stays mocked underneath (MSW).
465
+ * A declared external interaction: what to match (`request`) and what to reply
466
+ * (`response`), together in one named artifact. Contracts live in TypeScript
467
+ * files under `contracts/` next to the tests that use them, so the business
468
+ * payload (prompts, JSON responses) is visible at a glance while the real HTTP
469
+ * call stays mocked underneath.
463
470
  */
464
- interface InterceptContract {
465
- trigger: InterceptTrigger;
471
+ interface Contract {
472
+ /** Which outgoing call this contract speaks for. */
473
+ request: ContractRequest;
466
474
  /**
467
- * The reply — a fixed {@link InterceptResponse}, or a function
468
- * `(request) => InterceptResponse` evaluated per consumed request when the
475
+ * The reply — a fixed {@link ContractResponse}, or a function
476
+ * `(request) => ContractResponse` evaluated per served request when the
469
477
  * response must derive from the incoming payload.
470
478
  */
471
- response: InterceptResponseValue;
479
+ response: ContractResponseValue;
480
+ /**
481
+ * How many times this contract may serve. Omitted = unlimited (a
482
+ * re-render or a retry replays it). `n` = exhausted after n serves, so an
483
+ * ordered sequence is a finite contract followed by an unlimited tail.
484
+ */
485
+ times?: number;
486
+ /**
487
+ * When true, the chain FAILS unless the contract was actually requested:
488
+ * at least once, or exactly `times` times when `times` is set. Turns a
489
+ * silently-unused declaration into a spec failure.
490
+ */
491
+ required?: boolean;
492
+ }
493
+ /**
494
+ * A composite of contracts — the unit tests import. Flat, ordered, immutable;
495
+ * `.with()` derives a variant without touching the original.
496
+ */
497
+ interface Contracts {
498
+ /** The flattened contracts, in selection order. */
499
+ readonly contracts: readonly Contract[];
500
+ /**
501
+ * Derive a new composite: every base contract sharing a route with an
502
+ * override is REPLACED, and the overrides are PREPENDED — so a
503
+ * more-specific override wins first-match selection over a generic base
504
+ * route it does not replace.
505
+ */
506
+ with: (...overrides: (Contract | Contract[] | Contracts)[]) => Contracts;
472
507
  }
508
+ /** Any accepted contract input: one, a list, or a composite. */
509
+ type ContractInput = Contract | Contract[] | Contracts;
473
510
  /**
474
- * Declare an intercept contract. Identity function — its value is the
475
- * enforced shape and the naming convention:
511
+ * Declare one contract. Identity function — its value is the enforced shape
512
+ * and the naming convention:
476
513
  *
477
514
  * @example
478
- * // specs/api/reports/contracts/classify-article.openai.ts
515
+ * // specs/api/reports/contracts/openai/classify-article.ts
479
516
  * import { defineContract, openai } from '@jterrazz/test';
480
517
  *
481
518
  * export default defineContract({
482
- * trigger: openai.responses({ user: /Report Ingestion/, tools: ['classify'] }),
519
+ * request: openai.responses({ user: PROMPT, tools: ['classify'] }),
483
520
  * response: openai.reply({ categories: ['TECH'] }),
484
521
  * });
485
522
  *
486
523
  * // Dynamic — the response is computed from the observed request:
487
524
  * export default defineContract({
488
- * trigger: http.post('https://api.example.com/echo'),
525
+ * request: http.post('https://api.example.com/echo'),
489
526
  * response: (request) => http.json({ received: request.body }),
490
527
  * });
528
+ */
529
+ declare function defineContract(contract: Contract): Contract;
530
+ /**
531
+ * Compose contracts into the artifact a test imports — it's contracts all the
532
+ * way down: a composite may extend contracts, lists, and other composites,
533
+ * recursively, order preserved.
491
534
  *
492
- * // specs/api/reports/reports.test.ts
493
- * import classifyArticle from '../../spec/intercept/contracts/classify-article.openai.js';
535
+ * `.with(...)` derives a scenario: contracts whose route the overrides claim
536
+ * are removed from the base, and the overrides are prepended. Under
537
+ * first-match selection a more specific override (`/articles/gone-1`) also
538
+ * wins over a generic base route (`/articles/{{uuid}}`) it does not replace.
494
539
  *
495
- * const result = await jobs.intercept(classifyArticle).trigger('report-ingestion');
540
+ * @example
541
+ * // contracts/newsroom.contracts.ts
542
+ * export default defineContracts(events, articleById);
543
+ * export const withArticleGone = (id: string) =>
544
+ * newsroom.with(articleGone(id));
496
545
  */
497
- declare function defineContract(contract: InterceptContract): InterceptContract;
546
+ declare function defineContracts(...items: ContractInput[]): Contracts;
498
547
  //#endregion
499
548
  //#region src/core/ports/browser.port.d.ts
500
549
  /**
@@ -975,30 +1024,6 @@ declare class PageResult extends BaseResult {
975
1024
  get url(): string;
976
1025
  }
977
1026
  //#endregion
978
- //#region src/core/http-files/intercept-file.d.ts
979
- /** The declared request half of an exchange — the matching trigger. */
980
- interface ExchangeRequest {
981
- /** Declared header subset — names case-insensitive, values may carry `{{token}}`s. */
982
- headers: Record<string, string>;
983
- method: string;
984
- /** Path as written, query string included (`/articles?event_id=…`). */
985
- path: string;
986
- }
987
- /** The declared response half of an exchange — the stubbed reply. */
988
- interface ExchangeResponse {
989
- /** Parsed JSON body — or the raw text when the body is not valid JSON. */
990
- body: unknown;
991
- /** True when a body section is present. */
992
- hasBody: boolean;
993
- headers: Record<string, string>;
994
- status: number;
995
- }
996
- /** One declared request/response pair of an `intercepts/*.http` file. */
997
- interface InterceptExchange {
998
- request: ExchangeRequest;
999
- response: ExchangeResponse;
1000
- }
1001
- //#endregion
1002
1027
  //#region src/core/specification/shared/stub-backend.d.ts
1003
1028
  /** Options for the stub backend server. */
1004
1029
  interface StubBackendOptions {
@@ -1009,11 +1034,10 @@ interface StubBackendOptions {
1009
1034
  port?: number;
1010
1035
  }
1011
1036
  declare class StubBackend {
1012
- private consumed;
1013
- private entries;
1014
- /** Guarded once the current chain declared at least one intercept. */
1037
+ /** Guarded once the current chain declared at least one contract. */
1015
1038
  private guarded;
1016
1039
  private readonly options;
1040
+ private queue;
1017
1041
  private server;
1018
1042
  private readonly unmatchedRequests;
1019
1043
  private url;
@@ -1023,28 +1047,23 @@ declare class StubBackend {
1023
1047
  /** Stop the server (idempotent). */
1024
1048
  stop(): Promise<void>;
1025
1049
  /**
1026
- * Arm the stub for one chain: the declared exchanges replace the previous
1027
- * chain's wholesale, consumption restarts, and the unmatched log clears —
1050
+ * Arm the stub for one chain: the declared contracts replace the previous
1051
+ * chain's wholesale, the queue restarts, and the unmatched log clears —
1028
1052
  * the reset-between-chains databases already follow.
1029
1053
  */
1030
- beginChain(exchanges: InterceptExchange[]): void;
1054
+ beginChain(contracts: readonly Contract[]): void;
1031
1055
  /**
1032
- * The strict failure for the chain, or null — non-null when the chain
1033
- * declared at least one intercept AND an unmatched request was recorded.
1034
- * Enumerates every unmatched request (method, path, count) plus the
1035
- * declared routes, so the missing exchange writes itself.
1056
+ * The strict failure for the chain, or null. Non-null when the chain
1057
+ * declared at least one contract AND either an unmatched request was
1058
+ * recorded, or a `required` contract was never satisfied. Enumerates every
1059
+ * unmatched request (method, path, count) plus the declared routes, so the
1060
+ * missing contract writes itself.
1036
1061
  */
1037
1062
  violation(): Error | null;
1038
1063
  private corsHeaders;
1039
1064
  private describeRoutes;
1040
1065
  private handle;
1041
1066
  private record;
1042
- /**
1043
- * FIFO with a sticky tail: the first unconsumed matching exchange is
1044
- * consumed; when every matching exchange is spent, the LAST one keeps
1045
- * replying.
1046
- */
1047
- private take;
1048
1067
  }
1049
1068
  //#endregion
1050
1069
  //#region src/core/specification/shared/builder.d.ts
@@ -1068,7 +1087,7 @@ interface DockerSpecConfig {
1068
1087
  interface SpecificationConfig {
1069
1088
  /**
1070
1089
  * The declared stub backend (website/mobile facets) — armed with the
1071
- * chain's `.http` intercept exchanges before every terminal action.
1090
+ * chain's contracts before every terminal action.
1072
1091
  */
1073
1092
  backend?: StubBackend;
1074
1093
  /**
@@ -1136,6 +1155,11 @@ interface SpecificationConfig {
1136
1155
  */
1137
1156
  transform?: (text: string) => string;
1138
1157
  }
1158
+ /**
1159
+ * The `.intercept()` overload set, identical on every facet: one contract, a
1160
+ * list, a composite — or the inline `(request, response)` pair for a one-off.
1161
+ */
1162
+ type InterceptMethod<T> = ((contracts: ContractInput) => T) & ((request: ContractRequest, response: ContractResponder | ContractResponse) => T);
1139
1163
  /**
1140
1164
  * The `api` facet — HTTP chain entry handed out by `specification.api()`.
1141
1165
  * Setup methods chain; action methods are terminal: they execute the spec
@@ -1147,8 +1171,8 @@ interface SpecificationConfig {
1147
1171
  interface ApiSpecification<DatabaseKey extends string = string> {
1148
1172
  /** Set HTTP headers for the request. Multiple calls merge. */
1149
1173
  headers: (headers: Record<string, string>) => ApiSpecification<DatabaseKey>;
1150
- /** Intercept an outgoing HTTP call — a contract, an array of contracts, an `intercepts/*.http` file, or a trigger + response pair. */
1151
- intercept: ((contractOrContracts: InterceptContract | InterceptContract[] | string) => ApiSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => ApiSpecification<DatabaseKey>);
1174
+ /** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
1175
+ intercept: InterceptMethod<ApiSpecification<DatabaseKey>>;
1152
1176
  /** Queue a SQL seed file from `seeds/` to run before the action. */
1153
1177
  seed: (file: string, options?: {
1154
1178
  database?: DatabaseKey;
@@ -1169,8 +1193,8 @@ interface ApiSpecification<DatabaseKey extends string = string> {
1169
1193
  * Jobs run in-process by definition (CONVENTIONS A5/A8).
1170
1194
  */
1171
1195
  interface JobsSpecification<DatabaseKey extends string = string> {
1172
- /** Intercept an outgoing HTTP call — a contract, an array of contracts, an `intercepts/*.http` file, or a trigger + response pair. */
1173
- intercept: ((contractOrContracts: InterceptContract | InterceptContract[] | string) => JobsSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => JobsSpecification<DatabaseKey>);
1196
+ /** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
1197
+ intercept: InterceptMethod<JobsSpecification<DatabaseKey>>;
1174
1198
  /** Queue a SQL seed file from `seeds/` to run before the action. */
1175
1199
  seed: (file: string, options?: {
1176
1200
  database?: DatabaseKey;
@@ -1217,11 +1241,10 @@ interface WebsiteSpecification {
1217
1241
  /** Set HTTP headers for the exchange (incl. User-Agent overrides). Multiple calls merge. */
1218
1242
  headers: (headers: Record<string, string>) => WebsiteSpecification;
1219
1243
  /**
1220
- * Declare the chain's backend exchanges from an `intercepts/<name>.http`
1221
- * file — served by the declared stub backend (requires the runner's
1222
- * `backend` option). Multiple calls layer in order.
1244
+ * Declare the chain's backend contracts served by the declared stub
1245
+ * backend (requires the runner's `backend` option). Multiple calls append.
1223
1246
  */
1224
- intercept: (file: string) => WebsiteSpecification;
1247
+ intercept: InterceptMethod<WebsiteSpecification>;
1225
1248
  /** Perform one raw HTTP GET — redirects surface as 3xx results, never followed. */
1226
1249
  fetch: (path: string) => Promise<FetchResult>;
1227
1250
  /**
@@ -1239,11 +1262,10 @@ interface WebsiteSpecification {
1239
1262
  */
1240
1263
  interface MobileSpecification {
1241
1264
  /**
1242
- * Declare the chain's backend exchanges from an `intercepts/<name>.http`
1243
- * file — served by the declared stub backend (requires the runner's
1244
- * `backend` option). Multiple calls layer in order.
1265
+ * Declare the chain's backend contracts served by the declared stub
1266
+ * backend (requires the runner's `backend` option). Multiple calls append.
1245
1267
  */
1246
- intercept: (file: string) => MobileSpecification;
1268
+ intercept: InterceptMethod<MobileSpecification>;
1247
1269
  /**
1248
1270
  * Relaunch the app and resolve with the captured screen. With a deep
1249
1271
  * link, the app opens on it; with a scenario, the visitor interacts
@@ -1626,8 +1648,8 @@ interface MobileSpecificationOptions {
1626
1648
  bundleId: string;
1627
1649
  };
1628
1650
  /**
1629
- * Declared stub backend: started with the runner, serving the exchanges
1630
- * each chain declares via `.intercept('<name>.http')`. The handle gains
1651
+ * Declared stub backend: started with the runner, serving the contracts
1652
+ * each chain declares via `.intercept(...)`. The handle gains
1631
1653
  * `backendUrl` — inject it into your bundler env yourself.
1632
1654
  */
1633
1655
  backend?: MobileBackendOptions;
@@ -1685,8 +1707,8 @@ interface ServeOptions {
1685
1707
  /**
1686
1708
  * The declared stub backend behind the site under test — started before the
1687
1709
  * server command, torn down with the runner. Its URL is injected into the
1688
- * server child's environment under `env`; the chain's
1689
- * `.intercept('<name>.http')` exchanges are what it serves.
1710
+ * server child's environment under `env`; the contracts each chain declares
1711
+ * via `.intercept(...)` are what it serves.
1690
1712
  */
1691
1713
  interface WebsiteBackendOptions {
1692
1714
  /** Env var receiving the stub's URL in the server child (e.g. `'API_URL'`). */
@@ -1718,7 +1740,7 @@ type WebsiteSpecificationOptions = {
1718
1740
  /**
1719
1741
  * Declared stub backend: started BEFORE the server command, its
1720
1742
  * URL injected into the child env under `backend.env`. Serves the
1721
- * exchanges each chain declares via `.intercept('<name>.http')`.
1743
+ * contracts each chain declares via `.intercept(...)`.
1722
1744
  */
1723
1745
  backend?: WebsiteBackendOptions;
1724
1746
  /**
@@ -2040,20 +2062,38 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
2040
2062
  */
2041
2063
  declare function sqlite(options?: SqliteOptions): SqliteHandle;
2042
2064
  //#endregion
2065
+ //#region src/core/contracts/filters.d.ts
2066
+ /**
2067
+ * A text filter on a provider request builder (`openai.chat({ user })`,
2068
+ * `anthropic.messages({ system })`, …).
2069
+ *
2070
+ * A **string is EXACT**. That is deliberate: a substring filter silently
2071
+ * cross-matches — two prompts sharing a preamble both satisfy the first
2072
+ * contract, and the spec goes green on the wrong reply. Looser matching is
2073
+ * explicit, and says which looseness it means:
2074
+ *
2075
+ * - a `RegExp` — pattern matching;
2076
+ * - `match.includes('…')` — containment, the code-only matcher (it never joins
2077
+ * the `{{token}}` file vocabulary, CONVENTIONS D4);
2078
+ * - any other `match.*` matcher, evaluated by the same structural engine the
2079
+ * assertions use.
2080
+ */
2081
+ type TextFilter = Matcher | RegExp | string;
2082
+ //#endregion
2043
2083
  //#region src/integrations/anthropic/anthropic.d.ts
2044
2084
  interface AnthropicMessagesFilter {
2045
- model?: RegExp | string;
2046
- system?: RegExp | string;
2047
- user?: RegExp | string;
2085
+ model?: TextFilter;
2086
+ system?: TextFilter;
2087
+ user?: TextFilter;
2048
2088
  tools?: string[];
2049
2089
  }
2050
- declare function buildReply(data: unknown): InterceptResponse;
2090
+ declare function buildReply(data: unknown): ContractResponse;
2051
2091
  /**
2052
2092
  * Anthropic API intercept helpers.
2053
2093
  */
2054
2094
  declare const anthropic: {
2055
2095
  /**
2056
- * Trigger: match Messages API requests, optionally routed through a
2096
+ * Request: match Messages API calls, optionally routed through a
2057
2097
  * custom gateway URL. When used with a JSON fixture file, the data is
2058
2098
  * returned as-is (no wrapping) because Anthropic fixtures are typically
2059
2099
  * already in the Messages API response shape.
@@ -2061,15 +2101,16 @@ declare const anthropic: {
2061
2101
  * @example
2062
2102
  * anthropic.messages()
2063
2103
  * anthropic.messages({ system: /classify/ })
2104
+ * anthropic.messages({ user: buildPrompt() }) // string = EXACT equality
2064
2105
  * anthropic.messages({ user: /classify/ }, GATEWAY)
2065
2106
  */
2066
- messages(filter?: AnthropicMessagesFilter, url?: string): InterceptTrigger;
2107
+ messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
2067
2108
  /** Response: wrap data in Anthropic messages format. */
2068
2109
  reply: typeof buildReply;
2069
2110
  /** Response: return an Anthropic error. */
2070
- error(status: number, message?: string): InterceptResponse;
2111
+ error(status: number, message?: string): ContractResponse;
2071
2112
  /** Response: simulate a timeout. */
2072
- timeout(): InterceptResponse;
2113
+ timeout(): ContractResponse;
2073
2114
  };
2074
2115
  //#endregion
2075
2116
  //#region src/core/contracts/http.d.ts
@@ -2077,7 +2118,7 @@ declare const anthropic: {
2077
2118
  * Request filters for the generic HTTP provider. Every field is a subset
2078
2119
  * constraint — a request matches when all provided fields match.
2079
2120
  */
2080
- interface HttpInterceptFilter {
2121
+ interface HttpContractFilter {
2081
2122
  /**
2082
2123
  * Body constraint. An object is a deep SUBSET match (toMatchObject-style)
2083
2124
  * whose leaf values may be `match.*` matchers; a string is a containment
@@ -2089,58 +2130,78 @@ interface HttpInterceptFilter {
2089
2130
  /** Query-param subset. string = exact value, RegExp = `test()`. */
2090
2131
  query?: Record<string, RegExp | string>;
2091
2132
  }
2133
+ /** Init options shared by the response builders. */
2134
+ interface HttpResponseInit {
2135
+ /** Delay in ms before responding (for timeout testing). */
2136
+ delay?: number;
2137
+ /** Response headers, merged over the builder's own. */
2138
+ headers?: Record<string, string>;
2139
+ /** HTTP status code. */
2140
+ status?: number;
2141
+ }
2092
2142
  /**
2093
- * Generic HTTP intercept helpers for any URL. An optional {@link
2094
- * HttpInterceptFilter} narrows matching by request body, headers, or query
2095
- * a request that hits the URL/method but fails the filter counts as unmatched
2096
- * (strict intercepts, CONVENTIONS D7).
2143
+ * Generic HTTP contract helpers for any URL. The url is absolute (string or
2144
+ * RegExp), or a PATH FORM starting with `/` `http.get('/articles/{{uuid}}')`
2145
+ * matches that path on ANY origin, which is what an app calling its own
2146
+ * backend needs. An optional {@link HttpContractFilter} narrows matching by
2147
+ * body, headers, or query — a request that hits the URL/method but fails the
2148
+ * filter counts as unmatched (strict contracts, CONVENTIONS D7).
2097
2149
  *
2098
2150
  * @example
2099
- * .intercept(http.get('https://api.example.com/data'), 'http/response.json')
2100
- * .intercept(http.post(URL, { body: { user: 'alice' } }), http.json({ ok: true }))
2151
+ * defineContract({ request: http.get('/articles/{{uuid}}'), response: http.json(article) })
2152
+ * defineContract({ request: http.post(URL, { body: { user: 'alice' } }), response: http.empty() })
2101
2153
  */
2102
2154
  declare const http: {
2103
- get(url: RegExp | string, filter?: HttpInterceptFilter): InterceptTrigger;
2104
- post(url: RegExp | string, filter?: HttpInterceptFilter): InterceptTrigger;
2105
- put(url: RegExp | string, filter?: HttpInterceptFilter): InterceptTrigger;
2106
- delete(url: RegExp | string, filter?: HttpInterceptFilter): InterceptTrigger;
2107
- any(url: RegExp | string, filter?: HttpInterceptFilter): InterceptTrigger;
2108
- /** Response: simple JSON success. */
2109
- json(data: unknown, status?: number): InterceptResponse;
2110
- /** Response: error with message. */
2111
- error(status: number, message?: string): InterceptResponse;
2155
+ any(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2156
+ delete(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2157
+ get(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2158
+ patch(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2159
+ post(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2160
+ put(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
2161
+ /** Response: a body-less reply (204 by default). */
2162
+ empty(status?: number): ContractResponse;
2163
+ /** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
2164
+ error(status: number, body?: unknown): ContractResponse;
2165
+ /** Response: a JSON body (200 by default). */
2166
+ json(body: unknown, init?: HttpResponseInit): ContractResponse;
2167
+ /** Response: a text body, served as `text/plain` (200 by default). */
2168
+ text(body: string, init?: HttpResponseInit): ContractResponse;
2112
2169
  };
2113
2170
  //#endregion
2114
2171
  //#region src/integrations/openai/openai.d.ts
2115
2172
  interface OpenAIChatFilter {
2116
- model?: RegExp | string;
2117
- system?: RegExp | string;
2118
- user?: RegExp | string;
2173
+ model?: TextFilter;
2174
+ system?: TextFilter;
2175
+ user?: TextFilter;
2119
2176
  tools?: string[];
2120
2177
  temperature?: number;
2121
2178
  }
2122
2179
  interface OpenAIResponsesFilter {
2123
- model?: RegExp | string;
2124
- system?: RegExp | string;
2125
- user?: RegExp | string;
2180
+ model?: TextFilter;
2181
+ system?: TextFilter;
2182
+ user?: TextFilter;
2126
2183
  tools?: string[];
2127
2184
  }
2128
- declare function buildChatReply(data: unknown): InterceptResponse;
2185
+ declare function buildChatReply(data: unknown): ContractResponse;
2129
2186
  /**
2130
2187
  * OpenAI API intercept helpers.
2131
2188
  */
2132
2189
  declare const openai: {
2133
2190
  /**
2134
- * Trigger: match Chat Completions API requests.
2191
+ * Request: match Chat Completions API calls. STRING filters mean EXACT
2192
+ * equality (pass the app's own prompt builder); loosen deliberately with a
2193
+ * RegExp or `match.includes(...)`.
2135
2194
  *
2136
2195
  * @example
2137
- * openai.chat() // any chat call
2138
- * openai.chat({ model: 'gpt-4o' }) // specific model
2139
- * openai.chat({ system: /classify/ }) // system prompt match
2196
+ * openai.chat() // any chat call
2197
+ * openai.chat({ model: 'gpt-4o' }) // exact model
2198
+ * openai.chat({ system: buildPrompt() }) // exact system prompt
2199
+ * openai.chat({ system: /classify/ }) // pattern
2140
2200
  */
2141
- chat(filter?: OpenAIChatFilter): InterceptTrigger;
2201
+ chat(filter?: OpenAIChatFilter): ContractRequest;
2142
2202
  /**
2143
- * Trigger: match Responses API requests (AI SDK v5+) with auto-wrapping.
2203
+ * Request: match Responses API calls (AI SDK v5+) with auto-wrapping.
2204
+ * String filters mean EXACT equality (see {@link openai.chat}).
2144
2205
  * When used with a JSON file, the data is automatically wrapped in the
2145
2206
  * Responses API envelope.
2146
2207
  *
@@ -2150,7 +2211,7 @@ declare const openai: {
2150
2211
  * @example
2151
2212
  * openai.responses({ user: /Report Ingestion/ }, GATEWAY)
2152
2213
  */
2153
- responses(filter?: OpenAIResponsesFilter, url?: string): InterceptTrigger;
2214
+ responses(filter?: OpenAIResponsesFilter, url?: string): ContractRequest;
2154
2215
  /**
2155
2216
  * Response: wrap data in Chat Completions format.
2156
2217
  *
@@ -2159,11 +2220,11 @@ declare const openai: {
2159
2220
  */
2160
2221
  reply: typeof buildChatReply;
2161
2222
  /** Response: return an OpenAI error. */
2162
- error(status: number, message?: string): InterceptResponse;
2223
+ error(status: number, message?: string): ContractResponse;
2163
2224
  /** Response: return malformed content. */
2164
- malformed(content: string): InterceptResponse;
2225
+ malformed(content: string): ContractResponse;
2165
2226
  /** Response: simulate a timeout. */
2166
- timeout(): InterceptResponse;
2227
+ timeout(): ContractResponse;
2167
2228
  };
2168
2229
  //#endregion
2169
2230
  //#region src/vitest/mock-of.d.ts
@@ -2229,4 +2290,4 @@ declare module 'vitest' {
2229
2290
  }
2230
2291
  }
2231
2292
  //#endregion
2232
- 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 CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, ContainerAccessor, type ContainerPort, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, DirectoryAccessor, type DockerSpecConfig, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, HttpResult, type InterceptContract, type InterceptEntry, type InterceptResponder, type InterceptResponse, type InterceptResponseValue, type InterceptTrigger, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, 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 SpecificationConfig, type SpecificationMode, type SqliteOptions, TableAccessor, TextAccessor, type VisitScenario, type Visitor, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, complementary, content, contentinfo, defineContract, field, findContainersByLabel, form, heading, http, inspectContainer, link, main, match, mockOf, mockOfDate, navigation, openai, postgres, redis, region, removeContainers, search, specification, sqlite, testId, text, within };
2293
+ 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 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, 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 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 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 };