@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
@@ -0,0 +1,172 @@
1
+ import { i as safeRealpath } from "./shared-resolve.js";
2
+ import { t as isRecord } from "./shared-json-shape.js";
3
+ import { n as refusal } from "./shared-refusal.js";
4
+ import { r as closingOf, t as argumentSpans } from "./shared-source-scan.js";
5
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
6
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { spawnSync } from "node:child_process";
9
+ import { createHash } from "node:crypto";
10
+ //#region src/chain/paths/caller.ts
11
+ const TEST_FILE = /\.test\.[cm]?[jt]s$/u;
12
+ const FRAME_LOCATION = /^at (?:.*? \((?<wrapped>[^()]+)\)|(?:async )?(?<bare>[^()\s]+))$/u;
13
+ const POSITION = /^(?<file>.+):(?<line>\d+):(?<column>\d+)$/u;
14
+ function stackFrames(stack) {
15
+ const frames = [];
16
+ for (const text of stack.split("\n")) {
17
+ const groups = FRAME_LOCATION.exec(text.trim())?.groups;
18
+ const position = POSITION.exec(groups?.wrapped ?? groups?.bare ?? "")?.groups;
19
+ if (position?.file === void 0 || position.file.startsWith("node:")) continue;
20
+ const file = position.file.startsWith("file:") ? fileURLToPath(position.file) : position.file;
21
+ frames.push({
22
+ column: Number(position.column),
23
+ file,
24
+ line: Number(position.line)
25
+ });
26
+ }
27
+ return frames;
28
+ }
29
+ function callerFrames() {
30
+ const { stack } = /* @__PURE__ */ new Error("@jterrazz/test: reading the call site");
31
+ return stackFrames(stack ?? "").filter(({ file }) => file !== import.meta.filename);
32
+ }
33
+ const SOURCE_LOCATION = join("chain", "paths");
34
+ const FRAMEWORK_DIR = safeRealpath(import.meta.dirname);
35
+ /** From a build, `dist/` plus the `src/` its source maps point back to: a mapped frame is still the framework's. */
36
+ function frameworkTreesOf(frameworkDir) {
37
+ return frameworkDir.endsWith(`${sep}${SOURCE_LOCATION}`) ? [resolve(frameworkDir, "../..")] : [frameworkDir, resolve(frameworkDir, "..", "src")];
38
+ }
39
+ const FRAMEWORK_TREES = frameworkTreesOf(FRAMEWORK_DIR);
40
+ function isStrictlyInside(directory, path) {
41
+ const rest = relative(directory, path);
42
+ return rest !== "" && !rest.startsWith("..") && !isAbsolute(rest);
43
+ }
44
+ /** By identity, never by substring: a consumer's own `src/chain/` is not this package's. */
45
+ function isFrameworkFrame(filePath, frameworkTrees = FRAMEWORK_TREES) {
46
+ if (TEST_FILE.test(filePath)) return false;
47
+ const real = safeRealpath(filePath);
48
+ return frameworkTrees.some((tree) => isStrictlyInside(safeRealpath(tree), real));
49
+ }
50
+ /** Must be called before the first `await`: async resumption drops the caller's frames. */
51
+ function getCallerDir() {
52
+ const caller = callerFrames().find(({ file }) => !file.includes("node_modules") && !isFrameworkFrame(file));
53
+ return caller === void 0 ? process.cwd() : dirname(caller.file);
54
+ }
55
+ //#endregion
56
+ //#region src/expectations/in-place/formatter.ts
57
+ const FORMATTER = join("node_modules", ".bin", "oxfmt");
58
+ function formatterOf(file) {
59
+ for (let directory = dirname(file);; directory = dirname(directory)) {
60
+ const binary = join(directory, FORMATTER);
61
+ if (existsSync(binary)) return binary;
62
+ if (dirname(directory) === directory) return;
63
+ }
64
+ }
65
+ function formatRewritten(files) {
66
+ for (const file of files) {
67
+ const binary = formatterOf(file);
68
+ if (binary !== void 0) {
69
+ const project = dirname(dirname(dirname(binary)));
70
+ spawnSync(binary, [file], {
71
+ cwd: project,
72
+ stdio: "ignore"
73
+ });
74
+ }
75
+ }
76
+ }
77
+ //#endregion
78
+ //#region src/expectations/in-place/in-place.ts
79
+ const TO_MATCH = /\btoMatch\s*\(/gu;
80
+ const LITERAL = /^(?:['"`{[]|-?\d|(?:null|true|false|undefined)$)/u;
81
+ function callSiteOf(stack) {
82
+ return stackFrames(stack).find(({ file }) => !file.includes(`${sep}node_modules${sep}`) && !isFrameworkFrame(file));
83
+ }
84
+ function lineStartOf(source, line) {
85
+ let offset = 0;
86
+ for (let current = 1; current < line; current += 1) offset = source.indexOf("\n", offset) + 1;
87
+ return offset;
88
+ }
89
+ /** The call a frame points at, by its name or its parenthesis, among the calls of its line; its value's span, trimmed. */
90
+ function locateArgument(source, site) {
91
+ const lineStart = lineStartOf(source, site.line);
92
+ const position = lineStart + site.column - 1;
93
+ TO_MATCH.lastIndex = lineStart;
94
+ for (const found of source.matchAll(TO_MATCH)) {
95
+ if (found.index + 7 < position) continue;
96
+ const open = found.index + found[0].length - 1;
97
+ const [span = {
98
+ end: open + 1,
99
+ start: open + 1
100
+ }] = argumentSpans(source, open + 1, closingOf(source, open));
101
+ const line = source.slice(source.lastIndexOf("\n", found.index) + 1);
102
+ return {
103
+ ...span,
104
+ indent: line.slice(0, line.length - line.trimStart().length)
105
+ };
106
+ }
107
+ }
108
+ const LEDGERS = /* @__PURE__ */ new Map();
109
+ const isPending = (value) => isRecord(value) && typeof value.file === "string" && typeof value.source === "string";
110
+ let pendingFolder = null;
111
+ /** Read at each write: the folder is the project's, and one worker may serve several. */
112
+ function deferRewritesTo(folder) {
113
+ pendingFolder = folder;
114
+ }
115
+ function save(file, source, folder) {
116
+ if (folder === void 0) {
117
+ writeFileSync(file, source);
118
+ return;
119
+ }
120
+ mkdirSync(folder, { recursive: true });
121
+ const name = createHash("sha256").update(file).digest("hex");
122
+ const pending = {
123
+ file,
124
+ source
125
+ };
126
+ writeFileSync(join(folder, `${name}.json`), JSON.stringify(pending));
127
+ }
128
+ function writePendingRewrites(folder) {
129
+ if (!existsSync(folder)) return;
130
+ const written = [];
131
+ for (const name of readdirSync(folder).filter((entry) => entry.endsWith(".json"))) {
132
+ const pending = JSON.parse(readFileSync(join(folder, name), "utf8"));
133
+ if (isPending(pending)) {
134
+ writeFileSync(pending.file, pending.source);
135
+ written.push(pending.file);
136
+ }
137
+ }
138
+ rmSync(folder, { recursive: true });
139
+ formatRewritten(written);
140
+ }
141
+ function writeInPlace(site, render, folder = pendingFolder?.()) {
142
+ const ledger = LEDGERS.get(site.file) ?? {
143
+ edits: /* @__PURE__ */ new Map(),
144
+ original: readFileSync(site.file, "utf8")
145
+ };
146
+ LEDGERS.set(site.file, ledger);
147
+ const span = locateArgument(ledger.original, site);
148
+ if (span === void 0) throw new Error(refusal({
149
+ fix: "call `.toMatch(` on the expect() written at that line, not through an alias",
150
+ found: `update mode: no toMatch( stands at ${site.file}:${site.line}:${site.column}, so the value in place cannot be written`,
151
+ read: "docs/reference/tokens.md#a-value-written-in-place-is-rewritten-in-the-test",
152
+ why: "A value written in place is rewritten where its `toMatch(` stands."
153
+ }));
154
+ const written = ledger.original.slice(span.start, span.end);
155
+ if (written !== "" && !LITERAL.test(written)) throw new Error(refusal({
156
+ fix: "write the expectation as a literal in `toMatch(`, or record it by hand: a table's rows are recorded one by one, never from the last row",
157
+ found: `update mode: toMatch(${written}) at ${site.file}:${site.line}:${site.column} holds a name or a call, not a literal`,
158
+ read: "docs/reference/tokens.md#a-value-written-in-place-is-rewritten-in-the-test",
159
+ why: "Update mode rewrites the literal a `toMatch(` holds; a variable stands for values the rewrite would replace by the last one it saw."
160
+ }));
161
+ ledger.edits.set(span.start, {
162
+ end: span.end,
163
+ text: render(span.indent)
164
+ });
165
+ let source = ledger.original;
166
+ for (const [start, edit] of [...ledger.edits].toSorted(([a], [b]) => b - a)) source = source.slice(0, start) + edit.text + source.slice(edit.end);
167
+ save(site.file, source, folder);
168
+ }
169
+ //#endregion
170
+ export { getCallerDir as a, writePendingRewrites as i, deferRewritesTo as n, writeInPlace as r, callSiteOf as t };
171
+
172
+ //# sourceMappingURL=shared-in-place.js.map
@@ -0,0 +1,346 @@
1
+ import { t as ContainerOptions } from "./shared-container.port.js";
2
+ //#region src/chain/config.d.ts
3
+ type ChainConfig = {
4
+ root?: string | undefined;
5
+ };
6
+ //#endregion
7
+ //#region src/chain/context/with.capability.d.ts
8
+ /** Merged over `process.env`; a `null` value unsets the variable. */
9
+ type EnvRecord = Record<string, null | string>;
10
+ /** What a file shares on every chain it starts: settings, never state. */
11
+ type ChainContext = {
12
+ /** The instant every chain pins; a chain's own `.clock()` wins. */
13
+ clock?: string;
14
+ /** The environment every command runs with; a chain's own `.env()` merges over it. */
15
+ env?: EnvRecord;
16
+ /** The headers every request carries; a chain's own `.headers()` merges over them. */
17
+ headers?: Record<string, string>;
18
+ };
19
+ //#endregion
20
+ //#region src/chain/capability.d.ts
21
+ type MatrixGroup = 'Constructor option' | 'Result accessor' | 'Route option' | 'Setup' | 'Target' | 'Terminal action' | 'Verb';
22
+ /** `probe` is a call in `lint/self/corpus/matrix-calls.ts`'s grammar, or a literal. */
23
+ type MatrixRow = {
24
+ readonly group: MatrixGroup;
25
+ readonly member?: string;
26
+ readonly name: string;
27
+ readonly probe: string;
28
+ };
29
+ type ContextKey = keyof ChainContext;
30
+ type Intersect<Union> = (Union extends unknown ? (member: Union) => void : never) extends ((all: infer All) => void) ? All : never;
31
+ type SpecificationParams = {
32
+ readonly databaseKey: string;
33
+ readonly services: Record<string, unknown>;
34
+ };
35
+ /** Each definition extends this, naming its methods over `this['self']` and `this['params']`; `ChainOf` fills both in. */
36
+ interface ChainSignature {
37
+ readonly chain: object;
38
+ readonly params: SpecificationParams;
39
+ readonly self: unknown;
40
+ }
41
+ type Armed = {
42
+ /** Released in reverse arm order; an adapter may defer it to the test's end. */
43
+ readonly release?: () => void;
44
+ /** Preferred over the action's own error. */
45
+ readonly violation?: () => Error | null;
46
+ };
47
+ type Run<Config extends ChainConfig = ChainConfig> = {
48
+ readonly config: Config;
49
+ readonly testDir: string;
50
+ readonly workDir: null | string;
51
+ };
52
+ type SetupContext<Needs, Config extends ChainConfig = ChainConfig> = {
53
+ readonly config: Config;
54
+ readonly adapters: Needs;
55
+ };
56
+ type Capability<Name extends string, Slice, Args extends readonly unknown[], Signature extends ChainSignature = ChainSignature, Needs extends object = Record<never, never>, Context extends ContextKey = never, Config extends ChainConfig = ChainConfig> = {
57
+ readonly context?: Context;
58
+ readonly initial: Slice;
59
+ readonly kind: 'setup';
60
+ readonly name: Name;
61
+ /** Arm order across the setups of a chain: a lower phase arms first, and each setup states its own. */
62
+ readonly phase: number;
63
+ readonly row: MatrixRow;
64
+ /** A type alone, never set at runtime: `ChainOf` reads it. */
65
+ readonly signature?: Signature;
66
+ /** Absent when the action reads the slice instead. */
67
+ arm?(slice: Slice, adapters: Needs, run: Run<Config>): Armed | Promise<Armed | undefined> | undefined;
68
+ reduce(slice: Slice, args: Args, setup: SetupContext<Needs, Config>): Slice;
69
+ };
70
+ type AnyCapability = Capability<string, unknown, readonly unknown[], ChainSignature, object, ContextKey>;
71
+ type ActionOption<Name extends string> = {
72
+ readonly kind: 'option';
73
+ readonly name: Name;
74
+ readonly row: MatrixRow;
75
+ };
76
+ type Sharing = {
77
+ readonly kind: 'context';
78
+ readonly name: 'with';
79
+ readonly row: MatrixRow;
80
+ };
81
+ //#endregion
82
+ //#region src/services/database.port.d.ts
83
+ /** Database port for specification runners; implement it to plug in your stack. */
84
+ type DatabasePort = {
85
+ /** Execute raw SQL (for seeding test data). */
86
+ seed: (sql: string) => Promise<void>;
87
+ /** Query a table and return rows as arrays of values. */
88
+ query: (table: string, columns: string[]) => Promise<unknown[][]>;
89
+ /** The table's columns, in the order the table declares them: what a whole-table comparison reads. */
90
+ columns: (table: string) => Promise<string[]>;
91
+ /** Reset database to clean state between tests. */
92
+ reset: () => Promise<void>;
93
+ };
94
+ //#endregion
95
+ //#region src/services/isolation.port.d.ts
96
+ /** Isolates service state across parallel workers: `acquire()` per worker, `reset()` per run, `release()` at shutdown. */
97
+ type IsolationStrategy = {
98
+ /** Create this worker's isolated namespace, once at worker start. */
99
+ acquire: (workerId: string) => Promise<void>;
100
+ /** Fast reset within the namespace between runs. */
101
+ reset: () => Promise<void>;
102
+ /** Tear down the namespace, once at worker shutdown. */
103
+ release: () => Promise<void>;
104
+ };
105
+ //#endregion
106
+ //#region src/services/service.port.d.ts
107
+ /** A service handle, as `postgres()` or `redis()` return it; `connectionString` fills in once started. */
108
+ type ServiceHandle = {
109
+ readonly type: string;
110
+ /** The kebab-case record key, assigned at start: `{ analyticsDb: postgres() }` reads `docker/analytics-db/`. */
111
+ serviceName: null | string;
112
+ /** Default container port for this service type. */
113
+ readonly defaultPort: number;
114
+ /** Default Docker image for this service type. */
115
+ readonly defaultImage: string;
116
+ /** Environment variables to pass to the container. */
117
+ readonly environment: Record<string, string>;
118
+ /** How the container runs beyond its image, port and environment; none when absent. */
119
+ readonly containerOptions?: ContainerOptions;
120
+ /** Connection string, populated after start. */
121
+ connectionString: string;
122
+ /** Build the connection string from host and port. */
123
+ buildConnectionString: (host: string, port: number) => string;
124
+ /** Create a DatabasePort adapter (if this is a database). Returns null otherwise. */
125
+ createDatabaseAdapter: () => DatabasePort | null;
126
+ /** Verify the service is ready and accepting connections. Throws with context if not. */
127
+ healthcheck: () => Promise<void>;
128
+ /** Run the init scripts from `<root>/docker/<name>/`; a cache goes under `<root>/.artifacts/`, never a machine-global directory. */
129
+ initialize: (dockerDir: string, root: string) => Promise<void>;
130
+ /** Reset state between tests (truncate tables, flush cache, etc.) */
131
+ reset: () => Promise<void>;
132
+ isolation: () => IsolationStrategy;
133
+ };
134
+ //#endregion
135
+ //#region src/services/services.d.ts
136
+ /** Services declared as a named record: the keys are the spec's database vocabulary. */
137
+ type ServiceRecord = Record<string, ServiceHandle>;
138
+ /** Keys of a services record whose handles are databases. */
139
+ type DatabaseKeys<Services extends ServiceRecord> = { [K in keyof Services]: Services[K] extends DatabasePort ? K & string : never; }[keyof Services];
140
+ type ServicesConfig = ChainConfig & {
141
+ database?: DatabasePort | undefined;
142
+ /** Drives `database-named-only-when-several-at-runtime`: with 2+ databases `database` is required on `.seed()` / `.table()`, with one it is refused. */
143
+ databaseKeys?: string[] | undefined;
144
+ databases?: Map<string, DatabasePort> | undefined;
145
+ /** In cli mode, drives the `<KEY>_URL` injection (`service-urls-injected`), plus `DATABASE_URL` / `REDIS_URL` when unambiguous. */
146
+ services?: Record<string, ServiceHandle> | undefined;
147
+ };
148
+ //#endregion
149
+ //#region src/disk/spec-path.d.ts
150
+ /** A path relative to the spec file; `Extension` narrows its tail. */
151
+ type RelativePath<Extension extends string = string> = `../${string}${Extension}` | `./${string}${Extension}`;
152
+ declare const DISK: unique symbol;
153
+ /** A file the spec names, at `Path`. */
154
+ type FileAt<Path extends string> = {
155
+ readonly [DISK]: 'file';
156
+ readonly path: Path;
157
+ };
158
+ /** A file whose path ends in `Extension`: what a verb that reads one file accepts. */
159
+ type DiskFile<Extension extends string = string> = FileAt<RelativePath<Extension>>;
160
+ /** A directory the spec names; its path ends in `/`. */
161
+ type DiskDirectory = {
162
+ readonly [DISK]: 'directory';
163
+ readonly path: RelativePath<'/'>;
164
+ };
165
+ /** Either of the two: what a verb that takes a file or a directory accepts. */
166
+ type DiskTarget = DiskDirectory | DiskFile;
167
+ /** Name one file, by its path from the spec file. */
168
+ declare function file<const Path extends RelativePath>(path: Path extends `${string}/` ? never : Path): FileAt<Path>;
169
+ /** Name one directory, by its path from the spec file, ending in `/`. */
170
+ declare function directory<const Path extends RelativePath<'/'>>(path: Path): DiskDirectory & {
171
+ readonly path: Path;
172
+ };
173
+ //#endregion
174
+ //#region src/services/postgres/postgres.adapter.d.ts
175
+ type PostgresOptions = {
176
+ /** Override image. */
177
+ image?: string;
178
+ /** Override environment variables. */
179
+ env?: Record<string, string>;
180
+ };
181
+ declare class PostgresHandle implements DatabasePort, ServiceHandle {
182
+ readonly type = "postgres";
183
+ serviceName: null | string;
184
+ readonly defaultPort = 5432;
185
+ readonly defaultImage: string;
186
+ readonly environment: Record<string, string>;
187
+ readonly containerOptions: ContainerOptions;
188
+ connectionString: string;
189
+ private client;
190
+ private template;
191
+ private clone;
192
+ constructor(options?: PostgresOptions);
193
+ buildConnectionString(host: string, port: number): string;
194
+ createDatabaseAdapter(): DatabasePort;
195
+ healthcheck(): Promise<void>;
196
+ initialize(dockerDir: string): Promise<void>;
197
+ private getClient;
198
+ seed(sql: string): Promise<void>;
199
+ reset(): Promise<void>;
200
+ query(table: string, columns: string[]): Promise<unknown[][]>;
201
+ columns(table: string): Promise<string[]>;
202
+ isolation(): IsolationStrategy;
203
+ private closeClient;
204
+ }
205
+ /** Create a PostgreSQL service handle; its record key names the init script folder (`docker/<key>/init.sql`). */
206
+ declare function postgres(options?: PostgresOptions): PostgresHandle;
207
+ //#endregion
208
+ //#region src/services/redis/redis.adapter.d.ts
209
+ type RedisOptions = {
210
+ /** Override image. */
211
+ image?: string;
212
+ };
213
+ declare class RedisHandle implements ServiceHandle {
214
+ readonly type = "redis";
215
+ serviceName: null | string;
216
+ readonly defaultPort = 6379;
217
+ readonly defaultImage: string;
218
+ readonly environment: Record<string, string>;
219
+ readonly containerOptions: ContainerOptions;
220
+ connectionString: string;
221
+ private dbIndex;
222
+ /** What this handle holds: its slot's release, and the container's own string it extended. */
223
+ private pair;
224
+ constructor(options?: RedisOptions);
225
+ buildConnectionString(host: string, port: number): string;
226
+ createDatabaseAdapter(): DatabasePort | null;
227
+ healthcheck(): Promise<void>;
228
+ initialize(): Promise<void>;
229
+ reset(): Promise<void>;
230
+ isolation(): IsolationStrategy;
231
+ private letGo;
232
+ }
233
+ /** Create a Redis service handle, reported under its record key. */
234
+ declare function redis(options?: RedisOptions): RedisHandle;
235
+ //#endregion
236
+ //#region src/services/sqlite/sqlite-template.d.ts
237
+ type SqliteOptions = {
238
+ /** Path to a SQL file initializing the schema; exclusive with `prismaSchema`. */
239
+ init?: string;
240
+ /** Path to a Prisma schema, resolved against the cwd and handed to `prisma db push --schema`; exclusive with `init`. */
241
+ prismaSchema?: string;
242
+ };
243
+ //#endregion
244
+ //#region src/services/sqlite/sqlite.adapter.d.ts
245
+ declare class SqliteHandle implements DatabasePort, ServiceHandle {
246
+ readonly type = "sqlite";
247
+ serviceName: null | string;
248
+ readonly defaultPort = 0;
249
+ readonly defaultImage = "";
250
+ readonly environment: Record<string, string>;
251
+ connectionString: string;
252
+ private db;
253
+ private templatePath;
254
+ private workerDbPath;
255
+ private readonly initSql;
256
+ private readonly prismaSchema;
257
+ private readonly options;
258
+ constructor(options?: SqliteOptions);
259
+ buildConnectionString(): string;
260
+ createDatabaseAdapter(): DatabasePort;
261
+ healthcheck(): Promise<void>;
262
+ /** One worker builds under an exclusive lock, the others wait: two concurrent `prisma db push` on one file is `database is locked`. */
263
+ initialize(_dockerDir?: string, root?: string): Promise<void>;
264
+ /** Built on a private path, then renamed into place: `prisma db push` writes the header long before the tables exist. */
265
+ private buildTemplate;
266
+ private writeTemplate;
267
+ private getDb;
268
+ private closeDb;
269
+ seed(sql: string): Promise<void>;
270
+ query(table: string, columns: string[]): Promise<unknown[][]>;
271
+ columns(table: string): Promise<string[]>;
272
+ reset(): Promise<void>;
273
+ isolation(): IsolationStrategy;
274
+ }
275
+ /** Create a SQLite service handle, isolated per test by file copy. */
276
+ declare function sqlite(options?: SqliteOptions): SqliteHandle;
277
+ //#endregion
278
+ //#region src/services/process/process.adapter.d.ts
279
+ /** The one shape an external process takes: `website({ server })`, `services: { api: process(...) }`, `serve:`. */
280
+ type ProcessOptions = {
281
+ /** A one-shot command run once per specification before the spawn, which must exit 0. */
282
+ before?: string;
283
+ /** Shell command that starts the process. Receives the chosen port as `PORT`. */
284
+ command: string;
285
+ /** Working directory, relative to the project root. Default: the root itself. A function is called when the process starts, once per file: a spec that lays a fresh project for each file returns the path it laid. */
286
+ cwd?: (() => string) | string;
287
+ /** Environment on top of the runner's; a function receives the services already started. */
288
+ env?: ((services: Record<string, {
289
+ connectionString: string;
290
+ }>) => Record<string, string>) | Record<string, string>;
291
+ /** Fixed port. Default: an OS-assigned free port, injected as `PORT`. */
292
+ port?: number;
293
+ /** Readiness: a path polled until any HTTP status answers (`PORT` injected), or a regex over the output whose one group captures the port. */
294
+ ready?: RegExp | string;
295
+ /** Readiness budget in milliseconds. Default 30 000. */
296
+ timeout?: number;
297
+ };
298
+ declare class ProcessHandle implements ServiceHandle {
299
+ /** Embedded: nothing to pull, nothing to run in a container. */
300
+ readonly defaultImage = "";
301
+ readonly defaultPort = 0;
302
+ readonly environment: Record<string, string>;
303
+ readonly type = "process";
304
+ serviceName: null | string;
305
+ connectionString: string;
306
+ started: boolean;
307
+ private served;
308
+ private readonly options;
309
+ constructor(options: ProcessOptions);
310
+ buildConnectionString(host: string, port: number): string;
311
+ createDatabaseAdapter(): DatabasePort | null;
312
+ healthcheck(): Promise<void>;
313
+ initialize(): Promise<void>;
314
+ /** Isolation is the run's: one child per evaluation of the specification module, told apart by the minted id. */
315
+ isolation(): IsolationStrategy;
316
+ reset(): Promise<void>;
317
+ /** Spawn it and wait until it answers; `services` is every sibling, already started. */
318
+ spawnWith(root: string, services: Record<string, {
319
+ connectionString: string;
320
+ }>, runId: string): Promise<void>;
321
+ /** Terminate the process group. Idempotent. */
322
+ shutdown(): Promise<void>;
323
+ /** What it was declared as: what a specification reads to start it in a slot of its own. */
324
+ get spec(): ProcessOptions;
325
+ /** The base URL it came up on, known only once it is started. */
326
+ get url(): string;
327
+ }
328
+ /** Declare an external process the framework owns for the life of the specification. */
329
+ declare function processService(options: ProcessOptions): ProcessHandle;
330
+ //#endregion
331
+ //#region src/services/seed/seed.capability.d.ts
332
+ /** What `.seed()` takes: a `.sql` file, never the statements themselves: SQL is not written in a test. */
333
+ type SeedSource = DiskFile<'.sql'>;
334
+ type SeedMethod<Self, DatabaseKey extends string> = (source: SeedSource, options?: {
335
+ database?: DatabaseKey;
336
+ }) => Self;
337
+ type SeedChain<Self, DatabaseKey extends string> = {
338
+ /** Queue a SQL seed (`file('./__seeds__/<name>.sql')`) to run before the action. */
339
+ seed: SeedMethod<Self, DatabaseKey>;
340
+ };
341
+ interface SeedSignature extends ChainSignature {
342
+ readonly chain: SeedChain<this['self'], this['params']['databaseKey']>;
343
+ }
344
+ //#endregion
345
+ export { Intersect as A, IsolationStrategy as C, Armed as D, AnyCapability as E, ChainContext as F, EnvRecord as I, ChainConfig as L, Run as M, Sharing as N, Capability as O, SpecificationParams as P, ServiceHandle as S, ActionOption as T, directory as _, processService as a, ServiceRecord as b, RedisOptions as c, postgres as d, DiskDirectory as f, RelativePath as g, FileAt as h, ProcessOptions as i, MatrixRow as j, ChainSignature as k, redis as l, DiskTarget as m, SeedSource as n, sqlite as o, DiskFile as p, ProcessHandle as r, SqliteOptions as s, SeedSignature as t, PostgresOptions as u, file as v, DatabasePort as w, ServicesConfig as x, DatabaseKeys as y };
346
+ //# sourceMappingURL=shared-index.services.d.ts.map