@kvman/testkit 0.1.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 (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/dist/bin/bin-failure.d.ts +16 -0
  4. package/dist/bin/bin-failure.d.ts.map +1 -0
  5. package/dist/bin/bin-failure.js +28 -0
  6. package/dist/bin/bin-failure.js.map +1 -0
  7. package/dist/bin/program-command.d.ts +7 -0
  8. package/dist/bin/program-command.d.ts.map +1 -0
  9. package/dist/bin/program-command.js +8 -0
  10. package/dist/bin/program-command.js.map +1 -0
  11. package/dist/bin/run-program.d.ts +10 -0
  12. package/dist/bin/run-program.d.ts.map +1 -0
  13. package/dist/bin/run-program.js +30 -0
  14. package/dist/bin/run-program.js.map +1 -0
  15. package/dist/check/check-extension.d.ts +4 -0
  16. package/dist/check/check-extension.d.ts.map +1 -0
  17. package/dist/check/check-extension.js +78 -0
  18. package/dist/check/check-extension.js.map +1 -0
  19. package/dist/check/check-output.d.ts +4 -0
  20. package/dist/check/check-output.d.ts.map +1 -0
  21. package/dist/check/check-output.js +13 -0
  22. package/dist/check/check-output.js.map +1 -0
  23. package/dist/check/docs-findings.d.ts +8 -0
  24. package/dist/check/docs-findings.d.ts.map +1 -0
  25. package/dist/check/docs-findings.js +65 -0
  26. package/dist/check/docs-findings.js.map +1 -0
  27. package/dist/check/field-descriptions.d.ts +8 -0
  28. package/dist/check/field-descriptions.d.ts.map +1 -0
  29. package/dist/check/field-descriptions.js +19 -0
  30. package/dist/check/field-descriptions.js.map +1 -0
  31. package/dist/check/finding.d.ts +11 -0
  32. package/dist/check/finding.d.ts.map +1 -0
  33. package/dist/check/finding.js +2 -0
  34. package/dist/check/finding.js.map +1 -0
  35. package/dist/check/locale-keys.d.ts +7 -0
  36. package/dist/check/locale-keys.d.ts.map +1 -0
  37. package/dist/check/locale-keys.js +40 -0
  38. package/dist/check/locale-keys.js.map +1 -0
  39. package/dist/check/timer-scan.d.ts +6 -0
  40. package/dist/check/timer-scan.d.ts.map +1 -0
  41. package/dist/check/timer-scan.js +84 -0
  42. package/dist/check/timer-scan.js.map +1 -0
  43. package/dist/check/ui-keys.d.ts +3 -0
  44. package/dist/check/ui-keys.d.ts.map +1 -0
  45. package/dist/check/ui-keys.js +58 -0
  46. package/dist/check/ui-keys.js.map +1 -0
  47. package/dist/check-bin.d.ts +3 -0
  48. package/dist/check-bin.d.ts.map +1 -0
  49. package/dist/check-bin.js +17 -0
  50. package/dist/check-bin.js.map +1 -0
  51. package/dist/docs/built-in-guides.d.ts +12 -0
  52. package/dist/docs/built-in-guides.d.ts.map +1 -0
  53. package/dist/docs/built-in-guides.js +20 -0
  54. package/dist/docs/built-in-guides.js.map +1 -0
  55. package/dist/docs/docs-bin.d.ts +3 -0
  56. package/dist/docs/docs-bin.d.ts.map +1 -0
  57. package/dist/docs/docs-bin.js +98 -0
  58. package/dist/docs/docs-bin.js.map +1 -0
  59. package/dist/docs/docs-get.d.ts +11 -0
  60. package/dist/docs/docs-get.d.ts.map +1 -0
  61. package/dist/docs/docs-get.js +54 -0
  62. package/dist/docs/docs-get.js.map +1 -0
  63. package/dist/docs/docs-list.d.ts +22 -0
  64. package/dist/docs/docs-list.d.ts.map +1 -0
  65. package/dist/docs/docs-list.js +51 -0
  66. package/dist/docs/docs-list.js.map +1 -0
  67. package/dist/fake-clock.d.ts +16 -0
  68. package/dist/fake-clock.d.ts.map +1 -0
  69. package/dist/fake-clock.js +47 -0
  70. package/dist/fake-clock.js.map +1 -0
  71. package/dist/fake-openai.d.ts +55 -0
  72. package/dist/fake-openai.d.ts.map +1 -0
  73. package/dist/fake-openai.js +115 -0
  74. package/dist/fake-openai.js.map +1 -0
  75. package/dist/index.d.ts +55 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +111 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/new/new-bin.d.ts +3 -0
  80. package/dist/new/new-bin.d.ts.map +1 -0
  81. package/dist/new/new-bin.js +60 -0
  82. package/dist/new/new-bin.js.map +1 -0
  83. package/dist/new/new-project.d.ts +11 -0
  84. package/dist/new/new-project.d.ts.map +1 -0
  85. package/dist/new/new-project.js +36 -0
  86. package/dist/new/new-project.js.map +1 -0
  87. package/dist/new/scaffold-files.d.ts +7 -0
  88. package/dist/new/scaffold-files.d.ts.map +1 -0
  89. package/dist/new/scaffold-files.js +100 -0
  90. package/dist/new/scaffold-files.js.map +1 -0
  91. package/dist/new/scaffold-versions.d.ts +10 -0
  92. package/dist/new/scaffold-versions.d.ts.map +1 -0
  93. package/dist/new/scaffold-versions.js +12 -0
  94. package/dist/new/scaffold-versions.js.map +1 -0
  95. package/dist/preset/preset-bin.d.ts +3 -0
  96. package/dist/preset/preset-bin.d.ts.map +1 -0
  97. package/dist/preset/preset-bin.js +91 -0
  98. package/dist/preset/preset-bin.js.map +1 -0
  99. package/dist/preset/preset-check.d.ts +15 -0
  100. package/dist/preset/preset-check.d.ts.map +1 -0
  101. package/dist/preset/preset-check.js +113 -0
  102. package/dist/preset/preset-check.js.map +1 -0
  103. package/dist/preset/preset-new.d.ts +5 -0
  104. package/dist/preset/preset-new.d.ts.map +1 -0
  105. package/dist/preset/preset-new.js +16 -0
  106. package/dist/preset/preset-new.js.map +1 -0
  107. package/dist/preset/preset-reach.d.ts +18 -0
  108. package/dist/preset/preset-reach.d.ts.map +1 -0
  109. package/dist/preset/preset-reach.js +33 -0
  110. package/dist/preset/preset-reach.js.map +1 -0
  111. package/dist/preset/project-manifest.d.ts +5 -0
  112. package/dist/preset/project-manifest.d.ts.map +1 -0
  113. package/dist/preset/project-manifest.js +30 -0
  114. package/dist/preset/project-manifest.js.map +1 -0
  115. package/dist/preview/preview-bin.d.ts +3 -0
  116. package/dist/preview/preview-bin.d.ts.map +1 -0
  117. package/dist/preview/preview-bin.js +63 -0
  118. package/dist/preview/preview-bin.js.map +1 -0
  119. package/dist/preview/preview-home.d.ts +5 -0
  120. package/dist/preview/preview-home.d.ts.map +1 -0
  121. package/dist/preview/preview-home.js +64 -0
  122. package/dist/preview/preview-home.js.map +1 -0
  123. package/dist/preview/preview-manifest.d.ts +9 -0
  124. package/dist/preview/preview-manifest.d.ts.map +1 -0
  125. package/dist/preview/preview-manifest.js +32 -0
  126. package/dist/preview/preview-manifest.js.map +1 -0
  127. package/dist/preview/preview-port.d.ts +5 -0
  128. package/dist/preview/preview-port.d.ts.map +1 -0
  129. package/dist/preview/preview-port.js +22 -0
  130. package/dist/preview/preview-port.js.map +1 -0
  131. package/dist/preview/preview-preset.d.ts +14 -0
  132. package/dist/preview/preview-preset.d.ts.map +1 -0
  133. package/dist/preview/preview-preset.js +13 -0
  134. package/dist/preview/preview-preset.js.map +1 -0
  135. package/dist/preview/preview-run.d.ts +14 -0
  136. package/dist/preview/preview-run.d.ts.map +1 -0
  137. package/dist/preview/preview-run.js +241 -0
  138. package/dist/preview/preview-run.js.map +1 -0
  139. package/dist/preview/process-tree.d.ts +40 -0
  140. package/dist/preview/process-tree.d.ts.map +1 -0
  141. package/dist/preview/process-tree.js +92 -0
  142. package/dist/preview/process-tree.js.map +1 -0
  143. package/dist/running/call-query.d.ts +5 -0
  144. package/dist/running/call-query.d.ts.map +1 -0
  145. package/dist/running/call-query.js +34 -0
  146. package/dist/running/call-query.js.map +1 -0
  147. package/dist/running/running-kvman.d.ts +14 -0
  148. package/dist/running/running-kvman.d.ts.map +1 -0
  149. package/dist/running/running-kvman.js +80 -0
  150. package/dist/running/running-kvman.js.map +1 -0
  151. package/docs/i18n.md +20 -0
  152. package/docs/presets.md +15 -0
  153. package/docs/sdk.md +77 -0
  154. package/package.json +49 -0
  155. package/templates/AGENTS.md.template +13 -0
  156. package/templates/README.md.template +12 -0
  157. package/templates/extension-docs/usage.md.template +8 -0
  158. package/templates/src/docs.ts.template +46 -0
  159. package/templates/src/index.ts.template +42 -0
  160. package/templates/test/extension.test.ts.template +29 -0
  161. package/templates/web/components/Hello.vue.template +42 -0
  162. package/templates/web-build.ts.template +26 -0
@@ -0,0 +1,22 @@
1
+ import { createServer } from 'node:net';
2
+ import { BinFailure } from "../bin/bin-failure.js";
3
+ // The preview's port (plan 09 §9.3): the first free port from 3738 to 3837 on 127.0.0.1, which is the only address
4
+ // the bin ever binds (CLAUDE.md §6).
5
+ export const firstPreviewPort = 3738;
6
+ export const lastPreviewPort = 3837;
7
+ function isPortFree(port) {
8
+ return new Promise((resolve) => {
9
+ const server = createServer();
10
+ server.once('error', () => resolve(false));
11
+ server.listen(port, '127.0.0.1', () => server.close(() => resolve(true)));
12
+ });
13
+ }
14
+ /** The first free preview port, or `NO_FREE_PORT` when 3738 to 3837 are all taken. */
15
+ export async function previewPort(isFree = isPortFree) {
16
+ for (let port = firstPreviewPort; port <= lastPreviewPort; port += 1) {
17
+ if (await isFree(port))
18
+ return port;
19
+ }
20
+ throw new BinFailure('NO_FREE_PORT', 'Ports 3738 to 3837 are all taken; stop something that listens on one.');
21
+ }
22
+ //# sourceMappingURL=preview-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-port.js","sourceRoot":"","sources":["../../src/preview/preview-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,mHAAmH;AACnH,qCAAqC;AAErC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAA6C,UAAU;IACvF,KAAK,IAAI,IAAI,GAAG,gBAAgB,EAAE,IAAI,IAAI,eAAe,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACrE,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IACD,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,uEAAuE,CAAC,CAAC;AAChH,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Preset } from '@kvman/sdk';
2
+ /** An extension folder to preview, by package name. */
3
+ export type PreviewExtension = {
4
+ name: string;
5
+ folder: string;
6
+ };
7
+ /** A preset the preview builds on, with the folder its `path:` entries resolve against. */
8
+ export type GivenPreset = {
9
+ preset: Preset;
10
+ folder: string;
11
+ };
12
+ /** Builds the preview's preset from the dev extensions, optionally over a given preset. */
13
+ export declare function previewPreset(extensions: readonly PreviewExtension[], given?: GivenPreset): Preset;
14
+ //# sourceMappingURL=preview-preset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-preset.d.ts","sourceRoot":"","sources":["../../src/preview/preview-preset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAMzC,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,2FAA2F;AAC3F,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAQ7D,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,gBAAgB,EAAE,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAMlG"}
@@ -0,0 +1,13 @@
1
+ import path from 'node:path';
2
+ function absoluteSources(given) {
3
+ return Object.fromEntries(Object.entries(given.preset.extensions).map(([name, source]) => [name, source.startsWith('path:') ? `path:${path.resolve(given.folder, source.slice('path:'.length))}` : source]));
4
+ }
5
+ /** Builds the preview's preset from the dev extensions, optionally over a given preset. */
6
+ export function previewPreset(extensions, given) {
7
+ const dev = Object.fromEntries(extensions.map((extension) => [extension.name, `path:${extension.folder}`]));
8
+ if (given === undefined) {
9
+ return { name: 'preview', extensions: { '@kvman/kvai': 'bundled', '@kvman/kvwebui': 'bundled', ...dev }, settings: { 'kvwebui.home': 'kvwebui.extensions' } };
10
+ }
11
+ return { ...given.preset, extensions: { ...absoluteSources(given), ...dev } };
12
+ }
13
+ //# sourceMappingURL=preview-preset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-preset.js","sourceRoot":"","sources":["../../src/preview/preview-preset.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,SAAS,eAAe,CAAC,KAAkB;IACzC,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAClL,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,UAAuC,EAAE,KAAmB;IACxF,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAA8B,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,oBAAoB,EAAE,EAAE,CAAC;IAChK,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC;AAChF,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** What the preview runs: folders resolved against the current working folder, preset and entry files with them. */
2
+ export type PreviewRunOptions = {
3
+ /** Extension project folders, resolved against the current working folder. */
4
+ folders: readonly string[];
5
+ /** A preset file the preview builds on, or `undefined` for the generated one. */
6
+ preset?: string | undefined;
7
+ /** The kvman entry file to run, or `undefined` for the `kvman` on the PATH. */
8
+ kvman?: string | undefined;
9
+ /** Names the home `<os temp>/kvman-preview-<name>`. */
10
+ name: string;
11
+ };
12
+ /** Runs the preview until SIGINT or SIGTERM, or the preview kvman exits; failures are `BinFailure`s. */
13
+ export declare function runPreview(options: PreviewRunOptions, onReady: (url: string) => void): Promise<void>;
14
+ //# sourceMappingURL=preview-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-run.d.ts","sourceRoot":"","sources":["../../src/preview/preview-run.ts"],"names":[],"mappings":"AA2BA,oHAAoH;AACpH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAwLF,wGAAwG;AACxG,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC1G"}
@@ -0,0 +1,241 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { setTimeout as delay } from 'node:timers/promises';
5
+ import { envelopeSchema, healthSchema, presetSchema, z } from '@kvman/sdk';
6
+ import { BinFailure } from "../bin/bin-failure.js";
7
+ import { lastLines, runProgram } from "../bin/run-program.js";
8
+ import { preparePreviewHome } from "./preview-home.js";
9
+ import { readPreviewProject } from "./preview-manifest.js";
10
+ import { previewPort } from "./preview-port.js";
11
+ import { previewPreset } from "./preview-preset.js";
12
+ import { isRunning, killProcessTree, spawnCommand, stopPreviewChild, trackChild } from "./process-tree.js";
13
+ // `kvman-preview`'s run (plan 09 §9.3, ADR 0010, 7, 21): a fresh home and generated preset, one `web:build` then a
14
+ // `web:watch` child per project that has one, the preview kvman as a child, and its URL once it answers
15
+ // `kernel.health.get`. `shutdown` stops the preview, the watchers, and removes the home on every exit path, so no
16
+ // child or home is ever left behind. The readiness poll and the preview's stop wait are the only timers.
17
+ const readyWithinMs = 30_000;
18
+ const pollEveryMs = 200;
19
+ const stopPreviewWithinMs = 10_000;
20
+ const maxPreviewOutput = 65_536;
21
+ function hasCode(error, code) {
22
+ return error instanceof Error && 'code' in error && error.code === code;
23
+ }
24
+ function checkProjects(folders) {
25
+ return folders.map((folder) => {
26
+ const project = readPreviewProject(path.resolve(folder));
27
+ if (project === undefined)
28
+ throw new BinFailure('VALIDATION_FAILED', `${folder} has no package.json with a kvman field.`, { folder });
29
+ return { given: folder, absolute: path.resolve(folder), name: project.name, webWatch: project.webWatch };
30
+ });
31
+ }
32
+ function readGivenPreset(file) {
33
+ if (file === undefined)
34
+ return undefined;
35
+ const absolute = path.resolve(file);
36
+ let raw;
37
+ try {
38
+ raw = JSON.parse(readFileSync(absolute, 'utf8'));
39
+ }
40
+ catch {
41
+ throw new BinFailure('VALIDATION_FAILED', `The preset ${file} is invalid; run kvman-preset check on it.`, { preset: file });
42
+ }
43
+ const parsed = presetSchema.safeParse(raw);
44
+ if (!parsed.success)
45
+ throw new BinFailure('VALIDATION_FAILED', `The preset ${file} is invalid; run kvman-preset check on it.`, { preset: file });
46
+ return { preset: parsed.data, folder: path.dirname(absolute) };
47
+ }
48
+ function checkKvmanEntry(entry) {
49
+ if (entry === undefined)
50
+ return undefined;
51
+ if (!existsSync(path.resolve(entry))) {
52
+ throw new BinFailure('VALIDATION_FAILED', `The kvman entry ${entry} doesn't exist; pass the entry file of the kvman to run.`, { kvman: entry });
53
+ }
54
+ return path.resolve(entry);
55
+ }
56
+ function waitForSignals() {
57
+ const listeners = new Set();
58
+ let fired = false;
59
+ const fire = () => {
60
+ if (fired)
61
+ return;
62
+ fired = true;
63
+ for (const listener of [...listeners])
64
+ listener();
65
+ };
66
+ const onSigint = () => fire();
67
+ const onSigterm = () => fire();
68
+ process.on('SIGINT', onSigint);
69
+ process.on('SIGTERM', onSigterm);
70
+ return {
71
+ signalled: () => fired,
72
+ onSignal: (listener) => {
73
+ listeners.add(listener);
74
+ return () => {
75
+ listeners.delete(listener);
76
+ };
77
+ },
78
+ dispose: () => {
79
+ process.off('SIGINT', onSigint);
80
+ process.off('SIGTERM', onSigterm);
81
+ listeners.clear();
82
+ },
83
+ };
84
+ }
85
+ async function answers(url) {
86
+ let response;
87
+ try {
88
+ response = await fetch(`${url}api/queries/kernel.health.get`, {
89
+ method: 'POST',
90
+ headers: { 'content-type': 'application/json' },
91
+ body: '{"input":{}}',
92
+ signal: AbortSignal.timeout(pollEveryMs * 5),
93
+ });
94
+ }
95
+ catch (error) {
96
+ // A refused connection is a TypeError; a preview that accepts and never replies ends the attempt with a TimeoutError.
97
+ if (error instanceof TypeError || (error instanceof DOMException && error.name === 'TimeoutError'))
98
+ return false;
99
+ throw error;
100
+ }
101
+ if (!response.ok)
102
+ return false;
103
+ let body;
104
+ try {
105
+ body = await response.json();
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ const parsed = envelopeSchema({ output: healthSchema, jobId: z.string() }).safeParse(body);
111
+ return parsed.success && parsed.data.ok;
112
+ }
113
+ function previewStopped(state) {
114
+ const preview = state.preview;
115
+ if (preview === undefined || isRunning(preview))
116
+ return undefined;
117
+ if (state.kvmanFromPath && preview.spawnError !== undefined && hasCode(preview.spawnError, 'ENOENT')) {
118
+ return new BinFailure('PREVIEW_FAILED', `kvman isn't on the PATH; install it with npm i -g kvman, or pass --kvman <entry>.`);
119
+ }
120
+ return new BinFailure('PREVIEW_FAILED', `The preview kvman exited before it answered:\n${lastLines(state.previewOutput())}`);
121
+ }
122
+ async function waitUntilReady(url, state, signals) {
123
+ const deadline = Date.now() + readyWithinMs;
124
+ while (Date.now() < deadline) {
125
+ if (signals.signalled())
126
+ return false;
127
+ if (await answers(url))
128
+ return true;
129
+ const stopped = previewStopped(state);
130
+ if (stopped !== undefined)
131
+ throw stopped;
132
+ if (signals.signalled())
133
+ return false;
134
+ await delay(pollEveryMs);
135
+ }
136
+ throw new BinFailure('PREVIEW_FAILED', `The preview kvman didn't answer within 30 s:\n${lastLines(state.previewOutput())}`);
137
+ }
138
+ async function startWatchers(projects, state, signals) {
139
+ for (const project of projects) {
140
+ if (!project.webWatch || signals.signalled())
141
+ continue;
142
+ const build = await runProgram('npm', ['run', 'web:build'], project.absolute);
143
+ if (build.exitCode !== 0)
144
+ throw new BinFailure('PREVIEW_FAILED', `npm run web:build failed in ${project.given}:\n${lastLines(build.output)}`);
145
+ if (signals.signalled())
146
+ return;
147
+ const watch = spawnCommand(process.platform, 'npm', ['run', 'web:watch']);
148
+ const child = spawn(watch.command, watch.args, { cwd: project.absolute, stdio: 'ignore', detached: watch.detached, windowsHide: true });
149
+ state.watchers.push(trackChild(child));
150
+ }
151
+ }
152
+ function startPreviewChild(state, options) {
153
+ const kvmanArgs = ['--home', options.home, '--port', String(options.port), '--yes', '--no-open', '--preset', options.presetFile];
154
+ const start = options.kvmanEntry === undefined
155
+ ? spawnCommand(process.platform, 'kvman', kvmanArgs)
156
+ : spawnCommand(process.platform, process.execPath, [...process.execArgv, options.kvmanEntry, ...kvmanArgs]);
157
+ const child = spawn(start.command, start.args, { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'pipe'], detached: start.detached, windowsHide: true });
158
+ const tracked = trackChild(child);
159
+ state.preview = tracked;
160
+ let output = '';
161
+ const append = (text) => {
162
+ output += text;
163
+ if (output.length > maxPreviewOutput)
164
+ output = output.slice(-maxPreviewOutput);
165
+ };
166
+ child.stdout?.setEncoding('utf8').on('data', append);
167
+ child.stderr?.setEncoding('utf8').on('data', append);
168
+ state.previewOutput = () => output;
169
+ }
170
+ async function waitWhileRunning(state, signals) {
171
+ const preview = state.preview;
172
+ if (preview === undefined || signals.signalled() || !isRunning(preview))
173
+ return signals.signalled() ? 'signalled' : 'exited';
174
+ return await new Promise((resolve) => {
175
+ let settled = false;
176
+ const settle = (outcome) => {
177
+ if (settled)
178
+ return;
179
+ settled = true;
180
+ offSignal();
181
+ preview.child.off('exit', onExit);
182
+ preview.child.off('close', onExit);
183
+ resolve(outcome);
184
+ };
185
+ const offSignal = signals.onSignal(() => settle('signalled'));
186
+ const onExit = () => settle('exited');
187
+ preview.child.once('exit', onExit);
188
+ preview.child.once('close', onExit);
189
+ if (signals.signalled() || !isRunning(preview))
190
+ settle(signals.signalled() ? 'signalled' : 'exited');
191
+ });
192
+ }
193
+ // The one stop every exit path uses: the preview first (SIGINT, 10 s, then its group; the tree on Windows), then
194
+ // each `web:watch` tree, then the home.
195
+ async function shutdown(state) {
196
+ if (state.preview !== undefined)
197
+ await stopPreviewChild(state.preview, stopPreviewWithinMs);
198
+ for (const watcher of state.watchers)
199
+ await killProcessTree(watcher);
200
+ rmSync(state.home, { recursive: true, force: true });
201
+ }
202
+ /** Runs the preview until SIGINT or SIGTERM, or the preview kvman exits; failures are `BinFailure`s. */
203
+ export async function runPreview(options, onReady) {
204
+ const projects = checkProjects(options.folders);
205
+ const given = readGivenPreset(options.preset);
206
+ const kvmanEntry = checkKvmanEntry(options.kvman);
207
+ const home = preparePreviewHome(options.name);
208
+ const state = { home, preview: undefined, previewOutput: () => '', watchers: [], kvmanFromPath: kvmanEntry === undefined };
209
+ const signals = waitForSignals();
210
+ let failure;
211
+ try {
212
+ const port = await previewPort();
213
+ const presetFile = path.join(home, 'preview-preset.json');
214
+ writeFileSync(presetFile, JSON.stringify(previewPreset(projects.map((project) => ({ name: project.name, folder: project.absolute })), given)));
215
+ if (!signals.signalled()) {
216
+ await startWatchers(projects, state, signals);
217
+ if (!signals.signalled()) {
218
+ startPreviewChild(state, { home, port, presetFile, kvmanEntry });
219
+ const url = `http://127.0.0.1:${String(port)}/`;
220
+ if (await waitUntilReady(url, state, signals)) {
221
+ onReady(url);
222
+ if ((await waitWhileRunning(state, signals)) === 'exited') {
223
+ failure = new BinFailure('PREVIEW_FAILED', `The preview kvman exited:\n${lastLines(state.previewOutput())}`);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+ catch (error) {
230
+ if (!(error instanceof BinFailure))
231
+ throw error;
232
+ failure = error;
233
+ }
234
+ finally {
235
+ signals.dispose();
236
+ await shutdown(state);
237
+ }
238
+ if (failure !== undefined)
239
+ throw failure;
240
+ }
241
+ //# sourceMappingURL=preview-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-run.js","sourceRoot":"","sources":["../../src/preview/preview-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ /** How to kill a child's whole process tree: its group on Linux and macOS, `taskkill /T /F` on Windows. */
3
+ export type TreeKill = {
4
+ kind: 'group';
5
+ pid: number;
6
+ } | {
7
+ kind: 'taskkill';
8
+ command: 'taskkill';
9
+ args: string[];
10
+ };
11
+ /** How to kill a child's whole process tree, per OS. */
12
+ export declare function treeKill(platform: NodeJS.Platform, pid: number): TreeKill;
13
+ /** How to start `program` with `args`: directly on Linux and macOS, through `cmd.exe /d /s /c` on Windows. */
14
+ export type SpawnCommand = {
15
+ command: string;
16
+ args: string[];
17
+ detached: boolean;
18
+ };
19
+ /** How to start `program` with `args`, per OS. */
20
+ export declare function spawnCommand(platform: NodeJS.Platform, program: string, args: readonly string[]): SpawnCommand;
21
+ /** A spawned child with its exit watched from the start, so stopping it never misses the event it waits for. */
22
+ export type TrackedChild = {
23
+ /** The child process. */
24
+ child: ChildProcess;
25
+ /** Resolves once the child has exited, closed, or failed to spawn. */
26
+ exited: Promise<void>;
27
+ /** The spawn failure, when the child never started. */
28
+ spawnError: unknown;
29
+ };
30
+ /** Tracks `child` from now on: `exited` resolves on its first exit, close, or spawn error. */
31
+ export declare function trackChild(child: ChildProcess): TrackedChild;
32
+ /** Whether the child may still run: no exit, no signal, and no spawn failure. */
33
+ export declare function isRunning(tracked: TrackedChild): boolean;
34
+ /** Kills a child's whole process tree and resolves once it has exited. */
35
+ export declare function killProcessTree(tracked: TrackedChild): Promise<void>;
36
+ /** Resolves whether the child exits within `withinMs`; the wait ends on the exit, never on a fixed sleep. */
37
+ export declare function exitWithin(tracked: TrackedChild, withinMs: number): Promise<boolean>;
38
+ /** Stops a preview child: SIGINT first (it stops on it), then SIGKILL to its group; on Windows the tree is killed. */
39
+ export declare function stopPreviewChild(tracked: TrackedChild, withinMs: number): Promise<void>;
40
+ //# sourceMappingURL=process-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../src/preview/process-tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAM9D,2GAA2G;AAC3G,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElH,wDAAwD;AACxD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAKzE;AAED,8GAA8G;AAC9G,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAElF,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY,CAK9G;AAMD,gHAAgH;AAChH,MAAM,MAAM,YAAY,GAAG;IACzB,yBAAyB;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAa5D;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAExD;AAED,0EAA0E;AAC1E,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB1E;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpF;AAED,sHAAsH;AACtH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7F"}
@@ -0,0 +1,92 @@
1
+ import { spawn } from 'node:child_process';
2
+ /** How to kill a child's whole process tree, per OS. */
3
+ export function treeKill(platform, pid) {
4
+ if (platform === 'win32')
5
+ return { kind: 'taskkill', command: 'taskkill', args: ['/PID', String(pid), '/T', '/F'] };
6
+ if (platform === 'darwin')
7
+ return { kind: 'group', pid };
8
+ if (platform === 'linux')
9
+ return { kind: 'group', pid };
10
+ return { kind: 'group', pid };
11
+ }
12
+ /** How to start `program` with `args`, per OS. */
13
+ export function spawnCommand(platform, program, args) {
14
+ if (platform === 'win32')
15
+ return { command: 'cmd.exe', args: ['/d', '/s', '/c', program, ...args], detached: false };
16
+ if (platform === 'darwin')
17
+ return { command: program, args: [...args], detached: true };
18
+ if (platform === 'linux')
19
+ return { command: program, args: [...args], detached: true };
20
+ return { command: program, args: [...args], detached: true };
21
+ }
22
+ function hasCode(error, code) {
23
+ return error instanceof Error && 'code' in error && error.code === code;
24
+ }
25
+ /** Tracks `child` from now on: `exited` resolves on its first exit, close, or spawn error. */
26
+ export function trackChild(child) {
27
+ let resolveExited = () => { };
28
+ const exited = new Promise((resolve) => {
29
+ resolveExited = resolve;
30
+ });
31
+ const tracked = { child, exited, spawnError: undefined };
32
+ child.once('error', (error) => {
33
+ tracked.spawnError = error;
34
+ resolveExited();
35
+ });
36
+ child.once('exit', () => resolveExited());
37
+ child.once('close', () => resolveExited());
38
+ return tracked;
39
+ }
40
+ /** Whether the child may still run: no exit, no signal, and no spawn failure. */
41
+ export function isRunning(tracked) {
42
+ return tracked.child.exitCode === null && tracked.child.signalCode === null && tracked.spawnError === undefined;
43
+ }
44
+ /** Kills a child's whole process tree and resolves once it has exited. */
45
+ export async function killProcessTree(tracked) {
46
+ if (tracked.child.pid !== undefined) {
47
+ const kill = treeKill(process.platform, tracked.child.pid);
48
+ if (kill.kind === 'taskkill') {
49
+ await new Promise((resolve) => {
50
+ const taskkill = spawn(kill.command, kill.args, { stdio: 'ignore', windowsHide: true });
51
+ taskkill.once('error', () => resolve());
52
+ taskkill.once('close', () => resolve());
53
+ });
54
+ }
55
+ else {
56
+ try {
57
+ process.kill(-kill.pid, 'SIGKILL');
58
+ }
59
+ catch (error) {
60
+ if (!hasCode(error, 'ESRCH'))
61
+ throw error;
62
+ }
63
+ }
64
+ }
65
+ await tracked.exited;
66
+ }
67
+ /** Resolves whether the child exits within `withinMs`; the wait ends on the exit, never on a fixed sleep. */
68
+ export function exitWithin(tracked, withinMs) {
69
+ let timer;
70
+ const done = tracked.exited.then(() => true);
71
+ const timedOut = new Promise((resolve) => {
72
+ timer = setTimeout(() => resolve(false), withinMs);
73
+ });
74
+ return Promise.race([done, timedOut]).finally(() => {
75
+ if (timer !== undefined)
76
+ clearTimeout(timer);
77
+ });
78
+ }
79
+ /** Stops a preview child: SIGINT first (it stops on it), then SIGKILL to its group; on Windows the tree is killed. */
80
+ export async function stopPreviewChild(tracked, withinMs) {
81
+ if (!isRunning(tracked))
82
+ return;
83
+ if (process.platform === 'win32') {
84
+ await killProcessTree(tracked);
85
+ return;
86
+ }
87
+ tracked.child.kill('SIGINT');
88
+ if (await exitWithin(tracked, withinMs))
89
+ return;
90
+ await killProcessTree(tracked);
91
+ }
92
+ //# sourceMappingURL=process-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-tree.js","sourceRoot":"","sources":["../../src/preview/process-tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAS9D,wDAAwD;AACxD,MAAM,UAAU,QAAQ,CAAC,QAAyB,EAAE,GAAW;IAC7D,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;IACpH,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IACzD,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IACxD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAChC,CAAC;AAKD,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,QAAyB,EAAE,OAAe,EAAE,IAAuB;IAC9F,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACrH,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACxF,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,OAAO,CAAC,KAAc,EAAE,IAAY;IAC3C,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAC1E,CAAC;AAYD,8FAA8F;AAC9F,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,IAAI,aAAa,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC3C,aAAa,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;IACvE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC5B,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC;QAC3B,aAAa,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,SAAS,CAAC,OAAqB;IAC7C,OAAO,OAAO,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC;AAClH,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAqB;IACzD,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxF,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBACxC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;oBAAE,MAAM,KAAK,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,UAAU,CAAC,OAAqB,EAAE,QAAgB;IAChE,IAAI,KAAiC,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,OAAO,EAAE,EAAE;QAC9C,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sHAAsH;AACtH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAqB,EAAE,QAAgB;IAC5E,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAAE,OAAO;IAChC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,IAAI,MAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC;QAAE,OAAO;IAChD,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { z, type Json } from '@kvman/sdk';
2
+ import { type RunningKvman } from './running-kvman.ts';
3
+ /** Calls a public query on the running kvman and returns its validated output. */
4
+ export declare function callQuery<Output>(running: RunningKvman, name: string, input: Json, outputSchema: z.ZodType<Output>): Promise<Output>;
5
+ //# sourceMappingURL=call-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-query.d.ts","sourceRoot":"","sources":["../../src/running/call-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,CAAC,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAc9E,kFAAkF;AAClF,wBAAsB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAe1I"}
@@ -0,0 +1,34 @@
1
+ import { envelopeSchema, ProblemError, z } from '@kvman/sdk';
2
+ import { KvmanUnreachableError } from "./running-kvman.js";
3
+ // Calling the running kvman (plan 04 §4.1–4.2): `POST /api/queries/<name>` with `{ input }` and no `Origin` header,
4
+ // answered with the envelope. A refused connection means it isn't running.
5
+ function isConnectionRefused(error) {
6
+ let current = error;
7
+ while (current instanceof Error) {
8
+ if ('code' in current && current.code === 'ECONNREFUSED')
9
+ return true;
10
+ current = current.cause;
11
+ }
12
+ return false;
13
+ }
14
+ /** Calls a public query on the running kvman and returns its validated output. */
15
+ export async function callQuery(running, name, input, outputSchema) {
16
+ let response;
17
+ try {
18
+ response = await fetch(`${running.baseUrl}/api/queries/${name}`, {
19
+ method: 'POST',
20
+ headers: { 'content-type': 'application/json' },
21
+ body: JSON.stringify({ input }),
22
+ });
23
+ }
24
+ catch (error) {
25
+ if (isConnectionRefused(error))
26
+ throw new KvmanUnreachableError(`The kvman at ${running.baseUrl} refused the connection.`);
27
+ throw error;
28
+ }
29
+ const answer = envelopeSchema({ output: outputSchema, jobId: z.string() }).parse(await response.json());
30
+ if (!answer.ok)
31
+ throw new ProblemError(answer.problem);
32
+ return answer.output;
33
+ }
34
+ //# sourceMappingURL=call-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-query.js","sourceRoot":"","sources":["../../src/running/call-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,EAAa,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAqB,MAAM,oBAAoB,CAAC;AAE9E,oHAAoH;AACpH,2EAA2E;AAE3E,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,OAAO,OAAO,YAAY,KAAK,EAAE,CAAC;QAChC,IAAI,MAAM,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc;YAAE,OAAO,IAAI,CAAC;QACtE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAS,OAAqB,EAAE,IAAY,EAAE,KAAW,EAAE,YAA+B;IACvH,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,gBAAgB,IAAI,EAAE,EAAE;YAC/D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,mBAAmB,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,qBAAqB,CAAC,gBAAgB,OAAO,CAAC,OAAO,0BAA0B,CAAC,CAAC;QAC3H,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACxG,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** A kvman that answers queries over HTTP. */
2
+ export type RunningKvman = {
3
+ baseUrl: string;
4
+ };
5
+ /** The kvman can't be reached; the caller treats it as not running. */
6
+ export declare class KvmanUnreachableError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ /** Finds the running kvman: `undefined` when no lock file, stale lock, or portless lock says one runs. */
10
+ export declare function findRunningKvman(options: {
11
+ home?: string;
12
+ url?: string;
13
+ }): RunningKvman | undefined;
14
+ //# sourceMappingURL=running-kvman.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"running-kvman.d.ts","sourceRoot":"","sources":["../../src/running/running-kvman.ts"],"names":[],"mappings":"AASA,8CAA8C;AAC9C,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,uEAAuE;AACvE,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AA0DD,0GAA0G;AAC1G,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,GAAG,SAAS,CAInG"}