@jterrazz/test 16.1.1 → 18.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 (309) hide show
  1. package/README.md +45 -171
  2. package/dist/browser/index.js +1 -2980
  3. package/dist/browser/services.js +1 -0
  4. package/dist/browser/stubs.js +1 -0
  5. package/dist/browser-shared-index.browser.js +3233 -0
  6. package/dist/checker.d.ts +6 -1
  7. package/dist/checker.js +3721 -2624
  8. package/dist/config.d.ts +40 -0
  9. package/dist/config.js +309 -0
  10. package/dist/in-place.global-setup.d.ts +12 -0
  11. package/dist/in-place.global-setup.js +21 -0
  12. package/dist/index.d.ts +1641 -3004
  13. package/dist/index.js +5095 -3835
  14. package/dist/mobile-session.global-setup.d.ts +15 -0
  15. package/dist/mobile-session.global-setup.js +31 -0
  16. package/dist/oxlint.cjs +6069 -5634
  17. package/dist/oxlint.d.cts +43 -7212
  18. package/dist/oxlint.d.ts +43 -7212
  19. package/dist/oxlint.js +3979 -5799
  20. package/dist/route-coverage.global-setup.d.ts +43 -0
  21. package/dist/route-coverage.global-setup.js +1 -0
  22. package/dist/services.d.ts +3 -0
  23. package/dist/services.global-setup.d.ts +43 -0
  24. package/dist/services.global-setup.js +1 -0
  25. package/dist/services.js +597 -0
  26. package/dist/shared-appium.adapter.js +428 -0
  27. package/dist/shared-artifacts.js +34 -0
  28. package/dist/shared-chapter.js +7 -0
  29. package/dist/shared-container.port.d.ts +31 -0
  30. package/dist/shared-derived-projects.js +73 -0
  31. package/dist/shared-device.port.d.ts +189 -0
  32. package/dist/shared-exchange.js +447 -0
  33. package/dist/shared-in-place.js +172 -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-listened-port.js +19 -0
  40. package/dist/shared-literate-plugin.js +184 -0
  41. package/dist/shared-manifest.js +1321 -0
  42. package/dist/shared-pause.js +22 -0
  43. package/dist/shared-peer.js +88 -0
  44. package/dist/shared-playwright.adapter.js +328 -0
  45. package/dist/shared-process-alive.js +13 -0
  46. package/dist/shared-process.adapter.js +74 -0
  47. package/dist/shared-record-key.js +13 -0
  48. package/dist/shared-refusal.js +35 -0
  49. package/dist/shared-resolve.js +24 -0
  50. package/dist/shared-route-coverage.global-setup.js +187 -0
  51. package/dist/shared-scenario-failure.js +198 -0
  52. package/dist/shared-serve.js +407 -0
  53. package/dist/shared-service-images.js +10 -0
  54. package/dist/shared-services.global-setup.js +174 -0
  55. package/dist/shared-session-ledger.js +305 -0
  56. package/dist/shared-setup-entry.js +12 -0
  57. package/dist/shared-source-scan.js +133 -0
  58. package/dist/shared-spec-document.js +303 -0
  59. package/dist/shared-spec-grammar.js +66 -0
  60. package/dist/shared-specifications.js +26 -0
  61. package/dist/shared-specs-modules.js +1033 -0
  62. package/dist/shared-stub-reply.js +167 -0
  63. package/dist/shared-stub-server.js +142 -0
  64. package/dist/shared-temp-root.global-setup.js +73 -0
  65. package/dist/shared-test-baseline.js +26 -0
  66. package/dist/shared-test-section.js +58 -0
  67. package/dist/shared-testcontainers.adapter.js +306 -0
  68. package/dist/shared-tokens.js +55 -0
  69. package/dist/shared-yaml-document.js +119 -0
  70. package/dist/stubs.d.ts +2 -0
  71. package/dist/stubs.js +657 -0
  72. package/dist/temp-root.global-setup.d.ts +7 -0
  73. package/dist/temp-root.global-setup.js +1 -0
  74. package/dist/vitest.d.ts +93 -164
  75. package/dist/vitest.js +382 -550
  76. package/docs/README.md +82 -36
  77. package/docs/concepts/architecture.md +118 -0
  78. package/docs/concepts/conventions.md +333 -0
  79. package/docs/guides/api-specs.md +374 -0
  80. package/docs/guides/cli-specs.md +566 -0
  81. package/docs/guides/component-specs.md +252 -0
  82. package/docs/guides/develop.md +97 -0
  83. package/docs/guides/integration-specs.md +181 -0
  84. package/docs/guides/job-specs.md +180 -0
  85. package/docs/guides/lint-specs.md +174 -0
  86. package/docs/guides/mobile-specs.md +319 -0
  87. package/docs/guides/module-tests.md +149 -0
  88. package/docs/guides/release.md +44 -0
  89. package/docs/guides/spec-documents.md +189 -0
  90. package/docs/guides/website-specs.md +413 -0
  91. package/docs/reference/assertions.md +345 -0
  92. package/docs/reference/glossary.md +20 -0
  93. package/docs/reference/linting.md +483 -0
  94. package/docs/reference/services.md +236 -0
  95. package/docs/reference/stubs.md +500 -0
  96. package/docs/reference/time-and-doubles.md +137 -0
  97. package/docs/reference/tokens.md +258 -0
  98. package/docs/reference/visitor.md +221 -0
  99. package/docs/start/getting-started.md +187 -0
  100. package/docs/start/set-up-a-project.md +228 -0
  101. package/package.json +56 -36
  102. package/schema/spec.schema.json +43 -17
  103. package/dist/appium.adapter.js +0 -489
  104. package/dist/artifacts.js +0 -32
  105. package/dist/fixtures.js +0 -110
  106. package/dist/literate.js +0 -1747
  107. package/dist/match.js +0 -804
  108. package/dist/playwright.adapter.js +0 -438
  109. package/dist/server.js +0 -160
  110. package/dist/stream.js +0 -321
  111. package/dist/substring-warning.js +0 -282
  112. package/docs/01-architecture.md +0 -95
  113. package/docs/02-developing.md +0 -351
  114. package/docs/03-testing.md +0 -495
  115. package/docs/04-operating.md +0 -101
  116. package/docs/05-module-tests.md +0 -152
  117. package/docs/06-integration.md +0 -180
  118. package/docs/07-component.md +0 -246
  119. package/docs/08-website.md +0 -404
  120. package/docs/09-mobile.md +0 -270
  121. package/docs/10-api.md +0 -264
  122. package/docs/11-jobs.md +0 -157
  123. package/docs/12-cli.md +0 -677
  124. package/docs/13-elements.md +0 -248
  125. package/docs/14-assertions.md +0 -314
  126. package/docs/15-tokens.md +0 -253
  127. package/docs/16-contracts.md +0 -438
  128. package/docs/17-services.md +0 -188
  129. package/docs/18-conventions.md +0 -332
  130. package/docs/19-linting.md +0 -512
  131. package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
  132. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
  133. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
  134. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
  135. package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
  136. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
  137. package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
  138. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
  139. package/docs/decisions/_template.md +0 -22
  140. package/docs/reference/classes/Matcher.md +0 -120
  141. package/docs/reference/classes/ProcessHandle.md +0 -368
  142. package/docs/reference/functions/defineContract.md +0 -39
  143. package/docs/reference/functions/defineContracts.md +0 -36
  144. package/docs/reference/functions/disabled.md +0 -28
  145. package/docs/reference/functions/enabled.md +0 -20
  146. package/docs/reference/functions/focused.md +0 -26
  147. package/docs/reference/functions/mockOf.md +0 -79
  148. package/docs/reference/functions/postgres.md +0 -31
  149. package/docs/reference/functions/process.md +0 -33
  150. package/docs/reference/functions/redis.md +0 -28
  151. package/docs/reference/functions/required.md +0 -41
  152. package/docs/reference/functions/selected.md +0 -26
  153. package/docs/reference/functions/sqlite.md +0 -33
  154. package/docs/reference/functions/testId.md +0 -20
  155. package/docs/reference/functions/text.md +0 -40
  156. package/docs/reference/functions/valued.md +0 -28
  157. package/docs/reference/functions/waitUntil.md +0 -46
  158. package/docs/reference/functions/within.md +0 -29
  159. package/docs/reference/index.md +0 -196
  160. package/docs/reference/interfaces/BaseResult.md +0 -87
  161. package/docs/reference/interfaces/CallResult.md +0 -160
  162. package/docs/reference/interfaces/CaptureScope.md +0 -82
  163. package/docs/reference/interfaces/CliResult.md +0 -253
  164. package/docs/reference/interfaces/ContainerAccessor.md +0 -166
  165. package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
  166. package/docs/reference/interfaces/FetchResult.md +0 -180
  167. package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
  168. package/docs/reference/interfaces/HttpResult.md +0 -126
  169. package/docs/reference/interfaces/JsonAccessor.md +0 -46
  170. package/docs/reference/interfaces/PageResult.md +0 -356
  171. package/docs/reference/interfaces/RenderResult.md +0 -72
  172. package/docs/reference/interfaces/RenderedText.md +0 -110
  173. package/docs/reference/interfaces/ResponseAccessor.md +0 -47
  174. package/docs/reference/interfaces/ScreenResult.md +0 -128
  175. package/docs/reference/interfaces/TableAccessor.md +0 -26
  176. package/docs/reference/interfaces/TextAccessor.md +0 -96
  177. package/docs/reference/type-aliases/ApiHandle.md +0 -68
  178. package/docs/reference/type-aliases/ApiSpecification.md +0 -213
  179. package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
  180. package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
  181. package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
  182. package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
  183. package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
  184. package/docs/reference/type-aliases/BrowserPage.md +0 -132
  185. package/docs/reference/type-aliases/BrowserPort.md +0 -51
  186. package/docs/reference/type-aliases/CliEnv.md +0 -11
  187. package/docs/reference/type-aliases/CliHandle.md +0 -80
  188. package/docs/reference/type-aliases/CliInput.md +0 -36
  189. package/docs/reference/type-aliases/CliOutput.md +0 -46
  190. package/docs/reference/type-aliases/CliPort.md +0 -58
  191. package/docs/reference/type-aliases/CliSpecification.md +0 -144
  192. package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
  193. package/docs/reference/type-aliases/ComponentChain.md +0 -156
  194. package/docs/reference/type-aliases/ComponentScenario.md +0 -20
  195. package/docs/reference/type-aliases/ComponentUi.md +0 -47
  196. package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
  197. package/docs/reference/type-aliases/ContainerPort.md +0 -113
  198. package/docs/reference/type-aliases/Contract.md +0 -68
  199. package/docs/reference/type-aliases/ContractInput.md +0 -13
  200. package/docs/reference/type-aliases/ContractRequest.md +0 -95
  201. package/docs/reference/type-aliases/ContractResponder.md +0 -23
  202. package/docs/reference/type-aliases/ContractResponse.md +0 -78
  203. package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
  204. package/docs/reference/type-aliases/Contracts.md +0 -48
  205. package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
  206. package/docs/reference/type-aliases/DatabasePort.md +0 -72
  207. package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
  208. package/docs/reference/type-aliases/DevicePort.md +0 -51
  209. package/docs/reference/type-aliases/DeviceScreen.md +0 -32
  210. package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
  211. package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
  212. package/docs/reference/type-aliases/DomMount.md +0 -21
  213. package/docs/reference/type-aliases/ElementKind.md +0 -27
  214. package/docs/reference/type-aliases/ElementMatch.md +0 -74
  215. package/docs/reference/type-aliases/ElementOptions.md +0 -29
  216. package/docs/reference/type-aliases/ElementRef.md +0 -128
  217. package/docs/reference/type-aliases/ExecOptions.md +0 -37
  218. package/docs/reference/type-aliases/FileAccessor.md +0 -54
  219. package/docs/reference/type-aliases/HonoApp.md +0 -31
  220. package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
  221. package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
  222. package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
  223. package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
  224. package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
  225. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
  226. package/docs/reference/type-aliases/Intercept.md +0 -19
  227. package/docs/reference/type-aliases/InterceptOptions.md +0 -28
  228. package/docs/reference/type-aliases/InterceptScope.md +0 -11
  229. package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
  230. package/docs/reference/type-aliases/JobHandle.md +0 -34
  231. package/docs/reference/type-aliases/JobsHandle.md +0 -57
  232. package/docs/reference/type-aliases/JobsResult.md +0 -16
  233. package/docs/reference/type-aliases/JobsSpecification.md +0 -94
  234. package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
  235. package/docs/reference/type-aliases/LandmarkKind.md +0 -20
  236. package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
  237. package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
  238. package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
  239. package/docs/reference/type-aliases/MatchableRequest.md +0 -61
  240. package/docs/reference/type-aliases/MatcherKind.md +0 -34
  241. package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
  242. package/docs/reference/type-aliases/MobileElementKind.md +0 -14
  243. package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
  244. package/docs/reference/type-aliases/MobileElementRef.md +0 -20
  245. package/docs/reference/type-aliases/MobileHandle.md +0 -65
  246. package/docs/reference/type-aliases/MobileScenario.md +0 -20
  247. package/docs/reference/type-aliases/MobileSpecification.md +0 -51
  248. package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
  249. package/docs/reference/type-aliases/MobileVisitor.md +0 -81
  250. package/docs/reference/type-aliases/MockOfOptions.md +0 -26
  251. package/docs/reference/type-aliases/MockPort.md +0 -10
  252. package/docs/reference/type-aliases/PinnedClock.md +0 -10
  253. package/docs/reference/type-aliases/PostgresOptions.md +0 -32
  254. package/docs/reference/type-aliases/ProcessOptions.md +0 -112
  255. package/docs/reference/type-aliases/RedisOptions.md +0 -20
  256. package/docs/reference/type-aliases/RenderSubject.md +0 -12
  257. package/docs/reference/type-aliases/ScreenNode.md +0 -67
  258. package/docs/reference/type-aliases/ServerPort.md +0 -36
  259. package/docs/reference/type-aliases/ServerResponse.md +0 -46
  260. package/docs/reference/type-aliases/ServerSpec.md +0 -21
  261. package/docs/reference/type-aliases/ServiceHandle.md +0 -216
  262. package/docs/reference/type-aliases/ServiceRecord.md +0 -12
  263. package/docs/reference/type-aliases/SpecDocument.md +0 -84
  264. package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
  265. package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
  266. package/docs/reference/type-aliases/SpecFixture.md +0 -30
  267. package/docs/reference/type-aliases/SpecKind.md +0 -8
  268. package/docs/reference/type-aliases/SpecRun.md +0 -108
  269. package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
  270. package/docs/reference/type-aliases/SpecStream.md +0 -32
  271. package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
  272. package/docs/reference/type-aliases/SqliteOptions.md +0 -36
  273. package/docs/reference/type-aliases/SseEvent.md +0 -58
  274. package/docs/reference/type-aliases/StreamBody.md +0 -61
  275. package/docs/reference/type-aliases/TextFilter.md +0 -22
  276. package/docs/reference/type-aliases/VisitScenario.md +0 -20
  277. package/docs/reference/type-aliases/Visitor.md +0 -215
  278. package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
  279. package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
  280. package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
  281. package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
  282. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
  283. package/docs/reference/variables/anthropic.md +0 -19
  284. package/docs/reference/variables/banner.md +0 -10
  285. package/docs/reference/variables/button.md +0 -10
  286. package/docs/reference/variables/clock.md +0 -16
  287. package/docs/reference/variables/complementary.md +0 -10
  288. package/docs/reference/variables/component.md +0 -10
  289. package/docs/reference/variables/content.md +0 -10
  290. package/docs/reference/variables/contentinfo.md +0 -10
  291. package/docs/reference/variables/dialog.md +0 -10
  292. package/docs/reference/variables/field.md +0 -10
  293. package/docs/reference/variables/form.md +0 -10
  294. package/docs/reference/variables/heading.md +0 -10
  295. package/docs/reference/variables/http.md +0 -40
  296. package/docs/reference/variables/intercept.md +0 -11
  297. package/docs/reference/variables/link.md +0 -10
  298. package/docs/reference/variables/listitem.md +0 -10
  299. package/docs/reference/variables/main.md +0 -10
  300. package/docs/reference/variables/match.md +0 -8
  301. package/docs/reference/variables/navigation.md +0 -10
  302. package/docs/reference/variables/openai.md +0 -21
  303. package/docs/reference/variables/option.md +0 -17
  304. package/docs/reference/variables/region.md +0 -10
  305. package/docs/reference/variables/row.md +0 -10
  306. package/docs/reference/variables/search.md +0 -10
  307. package/docs/reference/variables/specification.md +0 -8
  308. package/docs/reference/variables/status.md +0 -10
  309. 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 @@
1
+ export { t as TEMP_ROOTS_PARENT, n as default, r as tempRootSetup } from "./shared-temp-root.global-setup.js";