@jterrazz/test 16.1.1 → 17.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.
Files changed (304) hide show
  1. package/README.md +45 -171
  2. package/dist/browser/index.js +2 -2980
  3. package/dist/browser/services.js +2 -0
  4. package/dist/browser/stubs.js +2 -0
  5. package/dist/checker.d.ts +6 -1
  6. package/dist/checker.js +4534 -2628
  7. package/dist/config.d.ts +40 -0
  8. package/dist/config.js +309 -0
  9. package/dist/in-place.global-setup.d.ts +11 -0
  10. package/dist/in-place.global-setup.js +21 -0
  11. package/dist/index.d.ts +1653 -3004
  12. package/dist/index.js +5226 -3845
  13. package/dist/mobile-session.global-setup.d.ts +14 -0
  14. package/dist/mobile-session.global-setup.js +31 -0
  15. package/dist/oxlint.cjs +6069 -5634
  16. package/dist/oxlint.d.cts +43 -7212
  17. package/dist/oxlint.d.ts +43 -7212
  18. package/dist/oxlint.js +6127 -5693
  19. package/dist/route-coverage.global-setup.d.ts +42 -0
  20. package/dist/route-coverage.global-setup.js +2 -0
  21. package/dist/services.d.ts +3 -0
  22. package/dist/services.global-setup.d.ts +43 -0
  23. package/dist/services.global-setup.js +2 -0
  24. package/dist/services.js +597 -0
  25. package/dist/shared-appium.adapter.js +428 -0
  26. package/dist/shared-artifacts.js +32 -0
  27. package/dist/shared-chapter.js +7 -0
  28. package/dist/shared-container.port.d.ts +31 -0
  29. package/dist/shared-derived-projects.js +58 -0
  30. package/dist/shared-device.port.d.ts +189 -0
  31. package/dist/shared-exchange.js +447 -0
  32. package/dist/shared-in-place.js +172 -0
  33. package/dist/shared-index.browser.js +3191 -0
  34. package/dist/shared-index.services.d.ts +346 -0
  35. package/dist/shared-index.stubs.d.ts +287 -0
  36. package/dist/shared-json-shape.js +17 -0
  37. package/dist/shared-landmarks.js +71 -0
  38. package/dist/shared-ledger.js +68 -0
  39. package/dist/shared-literate-plugin.js +183 -0
  40. package/dist/shared-manifest.js +1321 -0
  41. package/dist/shared-pause.js +11 -0
  42. package/dist/shared-peer.js +88 -0
  43. package/dist/shared-playwright.adapter.js +330 -0
  44. package/dist/shared-process.adapter.js +74 -0
  45. package/dist/shared-refusal.js +35 -0
  46. package/dist/shared-resolve.js +24 -0
  47. package/dist/shared-route-coverage.global-setup.js +187 -0
  48. package/dist/shared-scenario-failure.js +198 -0
  49. package/dist/shared-serve.js +365 -0
  50. package/dist/shared-service-images.js +10 -0
  51. package/dist/shared-services.global-setup.js +179 -0
  52. package/dist/shared-session-ledger.js +301 -0
  53. package/dist/shared-setup-entry.js +12 -0
  54. package/dist/shared-source-scan.js +133 -0
  55. package/dist/shared-spec-document.js +365 -0
  56. package/dist/shared-specifications.js +26 -0
  57. package/dist/shared-stream-body.js +130 -0
  58. package/dist/shared-stub-server.js +190 -0
  59. package/dist/shared-temp-root.global-setup.js +80 -0
  60. package/dist/shared-test-baseline.js +26 -0
  61. package/dist/shared-test-section.js +58 -0
  62. package/dist/shared-testcontainers.adapter.js +203 -0
  63. package/dist/shared-tokens.js +55 -0
  64. package/dist/shared-yaml-document.js +119 -0
  65. package/dist/stubs.d.ts +2 -0
  66. package/dist/stubs.js +657 -0
  67. package/dist/temp-root.global-setup.d.ts +7 -0
  68. package/dist/temp-root.global-setup.js +2 -0
  69. package/dist/vitest.d.ts +97 -163
  70. package/dist/vitest.js +373 -525
  71. package/docs/README.md +82 -36
  72. package/docs/concepts/architecture.md +118 -0
  73. package/docs/concepts/conventions.md +333 -0
  74. package/docs/guides/api-specs.md +374 -0
  75. package/docs/guides/cli-specs.md +564 -0
  76. package/docs/guides/component-specs.md +252 -0
  77. package/docs/guides/develop.md +94 -0
  78. package/docs/guides/integration-specs.md +181 -0
  79. package/docs/guides/job-specs.md +180 -0
  80. package/docs/guides/lint-specs.md +174 -0
  81. package/docs/guides/mobile-specs.md +319 -0
  82. package/docs/guides/module-tests.md +149 -0
  83. package/docs/guides/release.md +43 -0
  84. package/docs/guides/spec-documents.md +189 -0
  85. package/docs/guides/website-specs.md +413 -0
  86. package/docs/reference/assertions.md +345 -0
  87. package/docs/reference/glossary.md +20 -0
  88. package/docs/reference/linting.md +483 -0
  89. package/docs/reference/services.md +236 -0
  90. package/docs/reference/stubs.md +498 -0
  91. package/docs/reference/time-and-doubles.md +137 -0
  92. package/docs/reference/tokens.md +258 -0
  93. package/docs/reference/visitor.md +221 -0
  94. package/docs/start/getting-started.md +187 -0
  95. package/docs/start/set-up-a-project.md +214 -0
  96. package/package.json +50 -30
  97. package/schema/spec.schema.json +43 -17
  98. package/dist/appium.adapter.js +0 -489
  99. package/dist/artifacts.js +0 -32
  100. package/dist/fixtures.js +0 -110
  101. package/dist/literate.js +0 -1747
  102. package/dist/match.js +0 -804
  103. package/dist/playwright.adapter.js +0 -438
  104. package/dist/server.js +0 -160
  105. package/dist/stream.js +0 -321
  106. package/dist/substring-warning.js +0 -282
  107. package/docs/01-architecture.md +0 -95
  108. package/docs/02-developing.md +0 -351
  109. package/docs/03-testing.md +0 -495
  110. package/docs/04-operating.md +0 -101
  111. package/docs/05-module-tests.md +0 -152
  112. package/docs/06-integration.md +0 -180
  113. package/docs/07-component.md +0 -246
  114. package/docs/08-website.md +0 -404
  115. package/docs/09-mobile.md +0 -270
  116. package/docs/10-api.md +0 -264
  117. package/docs/11-jobs.md +0 -157
  118. package/docs/12-cli.md +0 -677
  119. package/docs/13-elements.md +0 -248
  120. package/docs/14-assertions.md +0 -314
  121. package/docs/15-tokens.md +0 -253
  122. package/docs/16-contracts.md +0 -438
  123. package/docs/17-services.md +0 -188
  124. package/docs/18-conventions.md +0 -332
  125. package/docs/19-linting.md +0 -512
  126. package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
  127. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
  128. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
  129. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
  130. package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
  131. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
  132. package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
  133. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
  134. package/docs/decisions/_template.md +0 -22
  135. package/docs/reference/classes/Matcher.md +0 -120
  136. package/docs/reference/classes/ProcessHandle.md +0 -368
  137. package/docs/reference/functions/defineContract.md +0 -39
  138. package/docs/reference/functions/defineContracts.md +0 -36
  139. package/docs/reference/functions/disabled.md +0 -28
  140. package/docs/reference/functions/enabled.md +0 -20
  141. package/docs/reference/functions/focused.md +0 -26
  142. package/docs/reference/functions/mockOf.md +0 -79
  143. package/docs/reference/functions/postgres.md +0 -31
  144. package/docs/reference/functions/process.md +0 -33
  145. package/docs/reference/functions/redis.md +0 -28
  146. package/docs/reference/functions/required.md +0 -41
  147. package/docs/reference/functions/selected.md +0 -26
  148. package/docs/reference/functions/sqlite.md +0 -33
  149. package/docs/reference/functions/testId.md +0 -20
  150. package/docs/reference/functions/text.md +0 -40
  151. package/docs/reference/functions/valued.md +0 -28
  152. package/docs/reference/functions/waitUntil.md +0 -46
  153. package/docs/reference/functions/within.md +0 -29
  154. package/docs/reference/index.md +0 -196
  155. package/docs/reference/interfaces/BaseResult.md +0 -87
  156. package/docs/reference/interfaces/CallResult.md +0 -160
  157. package/docs/reference/interfaces/CaptureScope.md +0 -82
  158. package/docs/reference/interfaces/CliResult.md +0 -253
  159. package/docs/reference/interfaces/ContainerAccessor.md +0 -166
  160. package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
  161. package/docs/reference/interfaces/FetchResult.md +0 -180
  162. package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
  163. package/docs/reference/interfaces/HttpResult.md +0 -126
  164. package/docs/reference/interfaces/JsonAccessor.md +0 -46
  165. package/docs/reference/interfaces/PageResult.md +0 -356
  166. package/docs/reference/interfaces/RenderResult.md +0 -72
  167. package/docs/reference/interfaces/RenderedText.md +0 -110
  168. package/docs/reference/interfaces/ResponseAccessor.md +0 -47
  169. package/docs/reference/interfaces/ScreenResult.md +0 -128
  170. package/docs/reference/interfaces/TableAccessor.md +0 -26
  171. package/docs/reference/interfaces/TextAccessor.md +0 -96
  172. package/docs/reference/type-aliases/ApiHandle.md +0 -68
  173. package/docs/reference/type-aliases/ApiSpecification.md +0 -213
  174. package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
  175. package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
  176. package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
  177. package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
  178. package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
  179. package/docs/reference/type-aliases/BrowserPage.md +0 -132
  180. package/docs/reference/type-aliases/BrowserPort.md +0 -51
  181. package/docs/reference/type-aliases/CliEnv.md +0 -11
  182. package/docs/reference/type-aliases/CliHandle.md +0 -80
  183. package/docs/reference/type-aliases/CliInput.md +0 -36
  184. package/docs/reference/type-aliases/CliOutput.md +0 -46
  185. package/docs/reference/type-aliases/CliPort.md +0 -58
  186. package/docs/reference/type-aliases/CliSpecification.md +0 -144
  187. package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
  188. package/docs/reference/type-aliases/ComponentChain.md +0 -156
  189. package/docs/reference/type-aliases/ComponentScenario.md +0 -20
  190. package/docs/reference/type-aliases/ComponentUi.md +0 -47
  191. package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
  192. package/docs/reference/type-aliases/ContainerPort.md +0 -113
  193. package/docs/reference/type-aliases/Contract.md +0 -68
  194. package/docs/reference/type-aliases/ContractInput.md +0 -13
  195. package/docs/reference/type-aliases/ContractRequest.md +0 -95
  196. package/docs/reference/type-aliases/ContractResponder.md +0 -23
  197. package/docs/reference/type-aliases/ContractResponse.md +0 -78
  198. package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
  199. package/docs/reference/type-aliases/Contracts.md +0 -48
  200. package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
  201. package/docs/reference/type-aliases/DatabasePort.md +0 -72
  202. package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
  203. package/docs/reference/type-aliases/DevicePort.md +0 -51
  204. package/docs/reference/type-aliases/DeviceScreen.md +0 -32
  205. package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
  206. package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
  207. package/docs/reference/type-aliases/DomMount.md +0 -21
  208. package/docs/reference/type-aliases/ElementKind.md +0 -27
  209. package/docs/reference/type-aliases/ElementMatch.md +0 -74
  210. package/docs/reference/type-aliases/ElementOptions.md +0 -29
  211. package/docs/reference/type-aliases/ElementRef.md +0 -128
  212. package/docs/reference/type-aliases/ExecOptions.md +0 -37
  213. package/docs/reference/type-aliases/FileAccessor.md +0 -54
  214. package/docs/reference/type-aliases/HonoApp.md +0 -31
  215. package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
  216. package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
  217. package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
  218. package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
  219. package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
  220. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
  221. package/docs/reference/type-aliases/Intercept.md +0 -19
  222. package/docs/reference/type-aliases/InterceptOptions.md +0 -28
  223. package/docs/reference/type-aliases/InterceptScope.md +0 -11
  224. package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
  225. package/docs/reference/type-aliases/JobHandle.md +0 -34
  226. package/docs/reference/type-aliases/JobsHandle.md +0 -57
  227. package/docs/reference/type-aliases/JobsResult.md +0 -16
  228. package/docs/reference/type-aliases/JobsSpecification.md +0 -94
  229. package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
  230. package/docs/reference/type-aliases/LandmarkKind.md +0 -20
  231. package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
  232. package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
  233. package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
  234. package/docs/reference/type-aliases/MatchableRequest.md +0 -61
  235. package/docs/reference/type-aliases/MatcherKind.md +0 -34
  236. package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
  237. package/docs/reference/type-aliases/MobileElementKind.md +0 -14
  238. package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
  239. package/docs/reference/type-aliases/MobileElementRef.md +0 -20
  240. package/docs/reference/type-aliases/MobileHandle.md +0 -65
  241. package/docs/reference/type-aliases/MobileScenario.md +0 -20
  242. package/docs/reference/type-aliases/MobileSpecification.md +0 -51
  243. package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
  244. package/docs/reference/type-aliases/MobileVisitor.md +0 -81
  245. package/docs/reference/type-aliases/MockOfOptions.md +0 -26
  246. package/docs/reference/type-aliases/MockPort.md +0 -10
  247. package/docs/reference/type-aliases/PinnedClock.md +0 -10
  248. package/docs/reference/type-aliases/PostgresOptions.md +0 -32
  249. package/docs/reference/type-aliases/ProcessOptions.md +0 -112
  250. package/docs/reference/type-aliases/RedisOptions.md +0 -20
  251. package/docs/reference/type-aliases/RenderSubject.md +0 -12
  252. package/docs/reference/type-aliases/ScreenNode.md +0 -67
  253. package/docs/reference/type-aliases/ServerPort.md +0 -36
  254. package/docs/reference/type-aliases/ServerResponse.md +0 -46
  255. package/docs/reference/type-aliases/ServerSpec.md +0 -21
  256. package/docs/reference/type-aliases/ServiceHandle.md +0 -216
  257. package/docs/reference/type-aliases/ServiceRecord.md +0 -12
  258. package/docs/reference/type-aliases/SpecDocument.md +0 -84
  259. package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
  260. package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
  261. package/docs/reference/type-aliases/SpecFixture.md +0 -30
  262. package/docs/reference/type-aliases/SpecKind.md +0 -8
  263. package/docs/reference/type-aliases/SpecRun.md +0 -108
  264. package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
  265. package/docs/reference/type-aliases/SpecStream.md +0 -32
  266. package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
  267. package/docs/reference/type-aliases/SqliteOptions.md +0 -36
  268. package/docs/reference/type-aliases/SseEvent.md +0 -58
  269. package/docs/reference/type-aliases/StreamBody.md +0 -61
  270. package/docs/reference/type-aliases/TextFilter.md +0 -22
  271. package/docs/reference/type-aliases/VisitScenario.md +0 -20
  272. package/docs/reference/type-aliases/Visitor.md +0 -215
  273. package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
  274. package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
  275. package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
  276. package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
  277. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
  278. package/docs/reference/variables/anthropic.md +0 -19
  279. package/docs/reference/variables/banner.md +0 -10
  280. package/docs/reference/variables/button.md +0 -10
  281. package/docs/reference/variables/clock.md +0 -16
  282. package/docs/reference/variables/complementary.md +0 -10
  283. package/docs/reference/variables/component.md +0 -10
  284. package/docs/reference/variables/content.md +0 -10
  285. package/docs/reference/variables/contentinfo.md +0 -10
  286. package/docs/reference/variables/dialog.md +0 -10
  287. package/docs/reference/variables/field.md +0 -10
  288. package/docs/reference/variables/form.md +0 -10
  289. package/docs/reference/variables/heading.md +0 -10
  290. package/docs/reference/variables/http.md +0 -40
  291. package/docs/reference/variables/intercept.md +0 -11
  292. package/docs/reference/variables/link.md +0 -10
  293. package/docs/reference/variables/listitem.md +0 -10
  294. package/docs/reference/variables/main.md +0 -10
  295. package/docs/reference/variables/match.md +0 -8
  296. package/docs/reference/variables/navigation.md +0 -10
  297. package/docs/reference/variables/openai.md +0 -21
  298. package/docs/reference/variables/option.md +0 -17
  299. package/docs/reference/variables/region.md +0 -10
  300. package/docs/reference/variables/row.md +0 -10
  301. package/docs/reference/variables/search.md +0 -10
  302. package/docs/reference/variables/specification.md +0 -8
  303. package/docs/reference/variables/status.md +0 -10
  304. package/docs/reference/variables/table.md +0 -10
package/dist/stubs.js ADDED
@@ -0,0 +1,657 @@
1
+ import { t as isRecord } from "./shared-json-shape.js";
2
+ import { n as refusal } from "./shared-refusal.js";
3
+ import { a as defineStubs, d as structuralEquals, f as structuralSubset, i as defineStub, o as describeRoute, r as declared, t as STREAM_BODY, y as CaptureScope } from "./shared-exchange.js";
4
+ //#region src/stubs/kinds/http-filter.ts
5
+ function rawOf(body) {
6
+ return typeof body === "string" ? body : JSON.stringify(body ?? "");
7
+ }
8
+ function matchesBody(body, expected) {
9
+ if (typeof expected === "string") return rawOf(body).includes(expected);
10
+ if (expected instanceof RegExp) return expected.test(rawOf(body));
11
+ return structuralSubset(expected, body, new CaptureScope());
12
+ }
13
+ function matchesEntries(expected, lookup) {
14
+ return Object.entries(expected).every(([key, value]) => {
15
+ const actual = lookup(key);
16
+ if (actual === void 0) return false;
17
+ return value instanceof RegExp ? value.test(actual) : actual === value;
18
+ });
19
+ }
20
+ function buildMatch(filter) {
21
+ if (!filter) return;
22
+ return (request) => {
23
+ if (filter.body !== void 0 && !matchesBody(request.body, filter.body)) return false;
24
+ if (filter.headers && !matchesEntries(filter.headers, (key) => request.headers[key.toLowerCase()])) return false;
25
+ if (filter.query) {
26
+ let params;
27
+ try {
28
+ params = new URL(request.url, "http://stub.invalid").searchParams;
29
+ } catch {
30
+ return false;
31
+ }
32
+ if (!matchesEntries(filter.query, (key) => params.get(key) ?? void 0)) return false;
33
+ }
34
+ return true;
35
+ };
36
+ }
37
+ function absentHeadersOf(filter = {}) {
38
+ const absent = filter.without?.headers?.map((name) => name.toLowerCase()) ?? [];
39
+ if (absent.length === 0) return;
40
+ const required = new Set(Object.keys(filter.headers ?? {}).map((name) => name.toLowerCase()));
41
+ const contradicted = absent.filter((name) => required.has(name));
42
+ if (contradicted.length > 0) {
43
+ const names = contradicted.join(", ");
44
+ throw new Error(refusal({
45
+ fix: `state ${names} in \`headers\` or in \`without.headers\`, never both`,
46
+ found: `an http stub filter that both requires and forbids the header(s) ${names}`,
47
+ read: "docs/reference/stubs.md#http-any-url",
48
+ why: "A header both required and absent matches no request, so the stub could never fire."
49
+ }));
50
+ }
51
+ return absent;
52
+ }
53
+ //#endregion
54
+ //#region src/stubs/kinds/http.ts
55
+ function wrapJson(data) {
56
+ return {
57
+ status: 200,
58
+ body: data
59
+ };
60
+ }
61
+ const TEXT_CONTENT_TYPE = "text/plain; charset=utf-8";
62
+ const STREAM_CONTENT_TYPE = "application/octet-stream";
63
+ const SSE_CONTENT_TYPE = "text/event-stream";
64
+ function frameEvent(event) {
65
+ const lines = [];
66
+ if (event.event !== void 0) lines.push(`event: ${event.event}`);
67
+ if (event.id !== void 0) lines.push(`id: ${event.id}`);
68
+ if (event.retry !== void 0) lines.push(`retry: ${event.retry}`);
69
+ const payload = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
70
+ for (const line of payload.split("\n")) lines.push(`data: ${line}`);
71
+ return `${lines.join("\n")}\n\n`;
72
+ }
73
+ function declare(method, url, filter) {
74
+ const absentHeaders = absentHeadersOf(filter);
75
+ return {
76
+ ...absentHeaders && { absentHeaders },
77
+ adapter: "http",
78
+ match: buildMatch(filter),
79
+ method,
80
+ url,
81
+ wrap: wrapJson
82
+ };
83
+ }
84
+ /** Generic HTTP stub helpers: an absolute URL, or a path form (`/articles/{{uuid}}`) matching any origin. */
85
+ const http = {
86
+ any(url, filter) {
87
+ return declare("*", url, filter);
88
+ },
89
+ delete(url, filter) {
90
+ return declare("DELETE", url, filter);
91
+ },
92
+ get(url, filter) {
93
+ return declare("GET", url, filter);
94
+ },
95
+ patch(url, filter) {
96
+ return declare("PATCH", url, filter);
97
+ },
98
+ post(url, filter) {
99
+ return declare("POST", url, filter);
100
+ },
101
+ put(url, filter) {
102
+ return declare("PUT", url, filter);
103
+ },
104
+ /** A stub the chain fails on if the subject reaches its route: `required` with `times: 0`. */
105
+ never(request) {
106
+ return {
107
+ request,
108
+ required: true,
109
+ response: {
110
+ status: 418,
111
+ body: { error: `@jterrazz/test: ${describeRoute(request)} is declared never requested` }
112
+ },
113
+ times: 0
114
+ };
115
+ },
116
+ /** Response: a body-less reply (204 by default). */
117
+ empty(status = 204) {
118
+ return {
119
+ status,
120
+ body: null
121
+ };
122
+ },
123
+ /** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
124
+ error(status, body) {
125
+ return {
126
+ status,
127
+ body: body === void 0 ? { error: `HTTP ${status}` } : body
128
+ };
129
+ },
130
+ /** Response: a JSON body (200 by default). */
131
+ json(body, init) {
132
+ return {
133
+ status: init?.status ?? 200,
134
+ body,
135
+ delay: init?.delay,
136
+ headers: init?.headers
137
+ };
138
+ },
139
+ /** Response: no response at all; `fetch` rejects, as when no server is running. */
140
+ unreachable() {
141
+ return {
142
+ body: null,
143
+ transport: "network-error"
144
+ };
145
+ },
146
+ /** Response: a body that arrives in pieces, written in order. */
147
+ stream(chunks, init) {
148
+ return {
149
+ status: init?.status ?? 200,
150
+ body: {
151
+ chunks: [...chunks],
152
+ contentType: init?.contentType ?? STREAM_CONTENT_TYPE,
153
+ delayBetweenChunks: init?.delay ?? 0,
154
+ kind: STREAM_BODY
155
+ },
156
+ headers: init?.headers
157
+ };
158
+ },
159
+ /** Response: a server-sent event stream, served as `text/event-stream`. */
160
+ sse(events, init) {
161
+ return {
162
+ status: init?.status ?? 200,
163
+ body: {
164
+ chunks: events.map(frameEvent),
165
+ contentType: SSE_CONTENT_TYPE,
166
+ delayBetweenChunks: init?.delay ?? 0,
167
+ kind: STREAM_BODY
168
+ },
169
+ headers: {
170
+ "cache-control": "no-cache",
171
+ ...init?.headers
172
+ }
173
+ };
174
+ },
175
+ /** Response: a text body, served as `text/plain` (200 by default). */
176
+ text(body, init) {
177
+ return {
178
+ status: init?.status ?? 200,
179
+ body,
180
+ delay: init?.delay,
181
+ headers: {
182
+ "content-type": TEXT_CONTENT_TYPE,
183
+ ...init?.headers
184
+ }
185
+ };
186
+ }
187
+ };
188
+ //#endregion
189
+ //#region src/stubs/kinds/text-filter.ts
190
+ function matchesText(filter, actual) {
191
+ if (filter === void 0) return true;
192
+ if (typeof filter === "string") return actual === filter;
193
+ if (filter instanceof RegExp) return filter.test(actual);
194
+ return structuralEquals(filter, actual, new CaptureScope());
195
+ }
196
+ /** A field of a request body read as JSON: absent wherever the body is not an object holding it. */
197
+ function fieldOf(value, key) {
198
+ return isRecord(value) ? value[key] : void 0;
199
+ }
200
+ const listOf = (value) => Array.isArray(value) ? value : [];
201
+ const textOf = (value) => typeof value === "string" ? value : "";
202
+ /** The content of the first message of `role`, as text: a structured content is its JSON. */
203
+ function messageText(messages, role) {
204
+ const content = fieldOf(listOf(messages).find((message) => fieldOf(message, "role") === role), "content");
205
+ return content === void 0 || typeof content === "string" ? textOf(content) : JSON.stringify(content);
206
+ }
207
+ const namesOf = (tools, read) => listOf(tools).map((tool) => textOf(read(tool))).filter((name) => name !== "");
208
+ //#endregion
209
+ //#region src/stubs/kinds/anthropic.ts
210
+ const ANTHROPIC_MESSAGES_URL = "https://api.anthropic.com/v1/messages";
211
+ function matchesFilter(body, filter) {
212
+ const names = namesOf(fieldOf(body, "tools"), (tool) => fieldOf(tool, "name"));
213
+ return matchesText(filter.model, textOf(fieldOf(body, "model"))) && matchesText(filter.system, textOf(fieldOf(body, "system"))) && matchesText(filter.user, messageText(fieldOf(body, "messages"), "user")) && (filter.tools?.every((tool) => names.includes(tool)) ?? true);
214
+ }
215
+ function buildReply(data) {
216
+ return {
217
+ status: 200,
218
+ body: {
219
+ id: "msg-test",
220
+ type: "message",
221
+ role: "assistant",
222
+ content: [{
223
+ type: "text",
224
+ text: typeof data === "string" ? data : JSON.stringify(data)
225
+ }],
226
+ model: "claude-sonnet-4-20250514",
227
+ stop_reason: "end_turn",
228
+ usage: {
229
+ input_tokens: 10,
230
+ output_tokens: 10
231
+ }
232
+ }
233
+ };
234
+ }
235
+ function streamEvents(pieces) {
236
+ return [
237
+ {
238
+ type: "message_start",
239
+ message: {
240
+ id: "msg-test",
241
+ type: "message",
242
+ role: "assistant",
243
+ content: [],
244
+ model: "claude-sonnet-4-20250514",
245
+ stop_reason: null,
246
+ stop_sequence: null,
247
+ usage: {
248
+ input_tokens: 10,
249
+ output_tokens: 1
250
+ }
251
+ }
252
+ },
253
+ {
254
+ type: "content_block_start",
255
+ index: 0,
256
+ content_block: {
257
+ type: "text",
258
+ text: ""
259
+ }
260
+ },
261
+ { type: "ping" },
262
+ ...pieces.map((text) => ({
263
+ type: "content_block_delta",
264
+ index: 0,
265
+ delta: {
266
+ type: "text_delta",
267
+ text
268
+ }
269
+ })),
270
+ {
271
+ type: "content_block_stop",
272
+ index: 0
273
+ },
274
+ {
275
+ type: "message_delta",
276
+ delta: {
277
+ stop_reason: "end_turn",
278
+ stop_sequence: null
279
+ },
280
+ usage: { output_tokens: 10 }
281
+ },
282
+ { type: "message_stop" }
283
+ ].map((data) => ({
284
+ data,
285
+ event: data.type
286
+ }));
287
+ }
288
+ /** Anthropic API stub helpers. */
289
+ const anthropic = {
290
+ /** Match Messages API calls, optionally through a gateway URL; a string filter is exact equality. */
291
+ messages(filter, url) {
292
+ return {
293
+ adapter: "anthropic",
294
+ method: "POST",
295
+ url: url ?? ANTHROPIC_MESSAGES_URL,
296
+ match: filter ? ({ body }) => matchesFilter(body, filter) : void 0,
297
+ wrap(data) {
298
+ if (isRecord(data)) return {
299
+ status: 200,
300
+ body: data
301
+ };
302
+ return buildReply(data);
303
+ }
304
+ };
305
+ },
306
+ /** A stub the chain fails on if the subject reaches this endpoint: `http.never()`. */
307
+ never(request) {
308
+ return http.never(request);
309
+ },
310
+ /** Response: data wrapped in the Messages format. */
311
+ reply(data) {
312
+ return buildReply(data);
313
+ },
314
+ /** Response: the text streamed as server-sent events, one `text_delta` per piece. */
315
+ stream(pieces, init) {
316
+ return http.sse(streamEvents(pieces), { delay: init?.delay ?? 0 });
317
+ },
318
+ /** Response: return an Anthropic error. */
319
+ error(status, message) {
320
+ return {
321
+ status,
322
+ body: {
323
+ type: "error",
324
+ error: {
325
+ type: status === 429 ? "rate_limit_error" : "api_error",
326
+ message: message ?? `Anthropic error (${status})`
327
+ }
328
+ }
329
+ };
330
+ },
331
+ /** Response: simulate a timeout. */
332
+ timeout() {
333
+ return {
334
+ status: 200,
335
+ body: {},
336
+ delay: 3e4
337
+ };
338
+ }
339
+ };
340
+ //#endregion
341
+ //#region src/stubs/kinds/openai.ts
342
+ const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
343
+ const OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses";
344
+ function matchesChatFilter(body, filter) {
345
+ const messages = fieldOf(body, "messages");
346
+ const names = namesOf(fieldOf(body, "tools"), (tool) => fieldOf(fieldOf(tool, "function"), "name"));
347
+ return matchesText(filter.model, textOf(fieldOf(body, "model"))) && matchesText(filter.system, messageText(messages, "system")) && matchesText(filter.user, messageText(messages, "user")) && (filter.tools?.every((tool) => names.includes(tool)) ?? true) && (filter.temperature === void 0 || fieldOf(body, "temperature") === filter.temperature);
348
+ }
349
+ function matchesResponsesFilter(body, filter) {
350
+ const input = fieldOf(body, "input");
351
+ const instructions = textOf(fieldOf(body, "instructions"));
352
+ const userText = listOf(input).filter((message) => fieldOf(message, "role") === "user").map((message) => {
353
+ const content = fieldOf(message, "content");
354
+ return typeof content === "string" ? content : JSON.stringify(content);
355
+ }).join(" ");
356
+ const names = namesOf(fieldOf(body, "tools"), (tool) => fieldOf(tool, "name") ?? fieldOf(fieldOf(tool, "function"), "name"));
357
+ return matchesText(filter.model, textOf(fieldOf(body, "model"))) && matchesText(filter.system, instructions === "" ? messageText(input, "system") : instructions) && matchesText(filter.user, userText) && (filter.tools?.every((tool) => names.includes(tool)) ?? true);
358
+ }
359
+ function buildChatReply(data) {
360
+ const content = typeof data === "string" ? data : JSON.stringify(data);
361
+ return {
362
+ status: 200,
363
+ body: {
364
+ id: "chatcmpl-test",
365
+ object: "chat.completion",
366
+ created: Math.floor(Date.now() / 1e3),
367
+ model: "gpt-4o-test",
368
+ choices: [{
369
+ index: 0,
370
+ message: {
371
+ role: "assistant",
372
+ content
373
+ },
374
+ finish_reason: "stop"
375
+ }],
376
+ usage: {
377
+ prompt_tokens: 10,
378
+ completion_tokens: 10,
379
+ total_tokens: 20
380
+ }
381
+ }
382
+ };
383
+ }
384
+ function responsesEnvelope(text, usage = DEFAULT_USAGE) {
385
+ return {
386
+ id: "resp-test",
387
+ object: "response",
388
+ created_at: Math.floor(Date.now() / 1e3),
389
+ model: "gpt-4o-test",
390
+ output: [{
391
+ type: "message",
392
+ id: "msg-test",
393
+ role: "assistant",
394
+ content: [{
395
+ type: "output_text",
396
+ text,
397
+ annotations: []
398
+ }]
399
+ }],
400
+ usage: {
401
+ input_tokens: usage.promptTokens,
402
+ output_tokens: usage.completionTokens,
403
+ total_tokens: usage.promptTokens + usage.completionTokens
404
+ }
405
+ };
406
+ }
407
+ function buildResponsesReply(data) {
408
+ return {
409
+ status: 200,
410
+ body: responsesEnvelope(typeof data === "string" ? data : JSON.stringify(data))
411
+ };
412
+ }
413
+ const DEFAULT_USAGE = {
414
+ completionTokens: 10,
415
+ promptTokens: 10
416
+ };
417
+ function chatStreamEvents(pieces, usage) {
418
+ const created = Math.floor(Date.now() / 1e3);
419
+ const chunk = (delta, finishReason) => ({ data: {
420
+ id: "chatcmpl-test",
421
+ object: "chat.completion.chunk",
422
+ created,
423
+ model: "gpt-4o-test",
424
+ choices: [{
425
+ index: 0,
426
+ delta,
427
+ logprobs: null,
428
+ finish_reason: finishReason
429
+ }]
430
+ } });
431
+ return [
432
+ chunk({
433
+ role: "assistant",
434
+ content: ""
435
+ }, null),
436
+ ...pieces.map((piece) => chunk({ content: piece }, null)),
437
+ chunk({}, "stop"),
438
+ ...usage === void 0 ? [] : [{ data: {
439
+ id: "chatcmpl-test",
440
+ object: "chat.completion.chunk",
441
+ created,
442
+ model: "gpt-4o-test",
443
+ choices: [],
444
+ usage: {
445
+ prompt_tokens: usage.promptTokens,
446
+ completion_tokens: usage.completionTokens,
447
+ total_tokens: usage.promptTokens + usage.completionTokens
448
+ }
449
+ } }],
450
+ { data: "[DONE]" }
451
+ ];
452
+ }
453
+ function responsesStreamEvents(pieces, usage) {
454
+ const text = pieces.join("");
455
+ const part = {
456
+ type: "output_text",
457
+ text,
458
+ annotations: []
459
+ };
460
+ const item = {
461
+ id: "msg-test",
462
+ type: "message",
463
+ role: "assistant"
464
+ };
465
+ const done = {
466
+ ...item,
467
+ status: "completed",
468
+ content: [part]
469
+ };
470
+ const completed = {
471
+ ...responsesEnvelope(text, usage),
472
+ status: "completed"
473
+ };
474
+ const at = {
475
+ item_id: "msg-test",
476
+ output_index: 0,
477
+ content_index: 0
478
+ };
479
+ return [
480
+ {
481
+ type: "response.created",
482
+ response: {
483
+ ...completed,
484
+ status: "in_progress",
485
+ output: [],
486
+ usage: null
487
+ }
488
+ },
489
+ {
490
+ type: "response.output_item.added",
491
+ output_index: 0,
492
+ item: {
493
+ ...item,
494
+ status: "in_progress",
495
+ content: []
496
+ }
497
+ },
498
+ {
499
+ type: "response.content_part.added",
500
+ ...at,
501
+ part: {
502
+ ...part,
503
+ text: ""
504
+ }
505
+ },
506
+ ...pieces.map((delta) => ({
507
+ type: "response.output_text.delta",
508
+ ...at,
509
+ delta
510
+ })),
511
+ {
512
+ type: "response.output_text.done",
513
+ ...at,
514
+ text
515
+ },
516
+ {
517
+ type: "response.content_part.done",
518
+ ...at,
519
+ part
520
+ },
521
+ {
522
+ type: "response.output_item.done",
523
+ output_index: 0,
524
+ item: done
525
+ },
526
+ {
527
+ type: "response.completed",
528
+ response: {
529
+ ...completed,
530
+ output: [done]
531
+ }
532
+ }
533
+ ].map((body, index) => ({
534
+ data: {
535
+ ...body,
536
+ sequence_number: index
537
+ },
538
+ event: body.type
539
+ }));
540
+ }
541
+ /** OpenAI API stub helpers. */
542
+ const openai = {
543
+ /** Match Chat Completions calls, at `url` where the client reaches another host; a string filter is exact equality, a RegExp or `match.includes` loosens it. */
544
+ chat(filter, url) {
545
+ return {
546
+ adapter: "openai",
547
+ method: "POST",
548
+ url: url ?? OPENAI_CHAT_URL,
549
+ match: filter ? ({ body }) => matchesChatFilter(body, filter) : void 0,
550
+ wrap: buildChatReply
551
+ };
552
+ },
553
+ /** Match Responses API calls; a JSON file is wrapped in the Responses envelope. */
554
+ responses(filter, url) {
555
+ return {
556
+ adapter: "openai",
557
+ method: "POST",
558
+ url: url ?? OPENAI_RESPONSES_URL,
559
+ match: filter ? ({ body }) => matchesResponsesFilter(body, filter) : void 0,
560
+ wrap: buildResponsesReply
561
+ };
562
+ },
563
+ /** A stub the chain fails on if the subject reaches this endpoint: `http.never()`. */
564
+ never(request) {
565
+ return http.never(request);
566
+ },
567
+ /** Response: data wrapped in the Chat Completions format. */
568
+ reply(data) {
569
+ return buildChatReply(data);
570
+ },
571
+ /** Response: the text streamed as server-sent events; `api` picks Chat Completions (default) or Responses. */
572
+ stream(pieces, init) {
573
+ const events = init?.api === "responses" ? responsesStreamEvents(pieces, init.usage) : chatStreamEvents(pieces, init?.usage);
574
+ return http.sse(events, { delay: init?.delay ?? 0 });
575
+ },
576
+ /** Response: return an OpenAI error. */
577
+ error(status, message) {
578
+ return {
579
+ status,
580
+ body: { error: {
581
+ message: message ?? `OpenAI error (${status})`,
582
+ type: status === 429 ? "rate_limit_error" : "api_error",
583
+ code: status === 429 ? "rate_limit_exceeded" : null
584
+ } }
585
+ };
586
+ },
587
+ /** Response: return malformed content. */
588
+ malformed(content) {
589
+ return buildChatReply(content);
590
+ },
591
+ /** Response: simulate a timeout. */
592
+ timeout() {
593
+ return {
594
+ status: 200,
595
+ body: {},
596
+ delay: 3e4
597
+ };
598
+ }
599
+ };
600
+ //#endregion
601
+ //#region src/stubs/intercept/intercept.ts
602
+ function isOptions(value) {
603
+ return isRecord(value) && "origin" in value;
604
+ }
605
+ function resolveRelativeAgainst(origin) {
606
+ let base;
607
+ try {
608
+ base = new URL(origin);
609
+ } catch {
610
+ throw new Error(refusal({
611
+ fix: "state an absolute URL: intercept(stubs, { origin: 'http://console.test' })",
612
+ found: `intercept(): \`origin\` is \`${origin}\`, not an absolute URL`,
613
+ read: "docs/reference/stubs.md#intercept-the-same-double-with-no-chain",
614
+ why: "`origin` states where a relative request resolves, so it is an absolute URL."
615
+ }));
616
+ }
617
+ return () => {
618
+ const underlying = globalThis.fetch;
619
+ globalThis.fetch = async (input, init) => typeof input === "string" ? await underlying(new URL(input, base).toString(), init) : await underlying(input, init);
620
+ return () => {
621
+ globalThis.fetch = underlying;
622
+ };
623
+ };
624
+ }
625
+ function interceptThrough(register) {
626
+ async function intercept(requestOrStubs, maybeResponseOrOptions, maybeOptions) {
627
+ const options = isOptions(maybeOptions) ? maybeOptions : isOptions(maybeResponseOrOptions) ? maybeResponseOrOptions : void 0;
628
+ const stubs = declared("intercept", requestOrStubs, isOptions(maybeResponseOrOptions) ? void 0 : maybeResponseOrOptions);
629
+ if (stubs.length === 0) throw new Error(refusal({
630
+ fix: "declare at least one stub, or drop the intercept: a subject with no network needs none",
631
+ found: "intercept(), and the list of stubs is empty",
632
+ read: "docs/reference/stubs.md#intercept-the-same-double-with-no-chain",
633
+ why: "An empty list guards nothing."
634
+ }));
635
+ const installOrigin = options?.origin === void 0 ? void 0 : resolveRelativeAgainst(options.origin);
636
+ const registration = await register(stubs);
637
+ const restoreOrigin = installOrigin?.();
638
+ return { [Symbol.asyncDispose]: async () => {
639
+ restoreOrigin?.();
640
+ const violation = registration.violation();
641
+ registration.cleanup();
642
+ if (violation) throw violation;
643
+ } };
644
+ }
645
+ return intercept;
646
+ }
647
+ //#endregion
648
+ //#region src/stubs/delivery/same-process/intercept.ts
649
+ /** The module-scope network double, on msw's node server. */
650
+ const intercept = interceptThrough(async (stubs) => {
651
+ const { registerStubs } = await import("./shared-stub-server.js");
652
+ return await registerStubs(stubs);
653
+ });
654
+ //#endregion
655
+ export { anthropic, defineStub, defineStubs, http, intercept, openai };
656
+
657
+ //# sourceMappingURL=stubs.js.map
@@ -0,0 +1,7 @@
1
+ //#region src/chain/paths/temp-root.global-setup.d.ts
2
+ /** Named by uid: a temp directory users share never holds a folder one of them cannot sweep. */
3
+ export declare const TEMP_ROOTS_PARENT: string;
4
+ export declare function tempRootSetup(): string;
5
+ export default function setup(): () => void;
6
+ //#endregion
7
+ //# sourceMappingURL=temp-root.global-setup.d.ts.map
@@ -0,0 +1,2 @@
1
+ import { n as setup, r as tempRootSetup, t as TEMP_ROOTS_PARENT } from "./shared-temp-root.global-setup.js";
2
+ export { TEMP_ROOTS_PARENT, setup as default, tempRootSetup };