@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.
- package/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/bin/bin-failure.d.ts +16 -0
- package/dist/bin/bin-failure.d.ts.map +1 -0
- package/dist/bin/bin-failure.js +28 -0
- package/dist/bin/bin-failure.js.map +1 -0
- package/dist/bin/program-command.d.ts +7 -0
- package/dist/bin/program-command.d.ts.map +1 -0
- package/dist/bin/program-command.js +8 -0
- package/dist/bin/program-command.js.map +1 -0
- package/dist/bin/run-program.d.ts +10 -0
- package/dist/bin/run-program.d.ts.map +1 -0
- package/dist/bin/run-program.js +30 -0
- package/dist/bin/run-program.js.map +1 -0
- package/dist/check/check-extension.d.ts +4 -0
- package/dist/check/check-extension.d.ts.map +1 -0
- package/dist/check/check-extension.js +78 -0
- package/dist/check/check-extension.js.map +1 -0
- package/dist/check/check-output.d.ts +4 -0
- package/dist/check/check-output.d.ts.map +1 -0
- package/dist/check/check-output.js +13 -0
- package/dist/check/check-output.js.map +1 -0
- package/dist/check/docs-findings.d.ts +8 -0
- package/dist/check/docs-findings.d.ts.map +1 -0
- package/dist/check/docs-findings.js +65 -0
- package/dist/check/docs-findings.js.map +1 -0
- package/dist/check/field-descriptions.d.ts +8 -0
- package/dist/check/field-descriptions.d.ts.map +1 -0
- package/dist/check/field-descriptions.js +19 -0
- package/dist/check/field-descriptions.js.map +1 -0
- package/dist/check/finding.d.ts +11 -0
- package/dist/check/finding.d.ts.map +1 -0
- package/dist/check/finding.js +2 -0
- package/dist/check/finding.js.map +1 -0
- package/dist/check/locale-keys.d.ts +7 -0
- package/dist/check/locale-keys.d.ts.map +1 -0
- package/dist/check/locale-keys.js +40 -0
- package/dist/check/locale-keys.js.map +1 -0
- package/dist/check/timer-scan.d.ts +6 -0
- package/dist/check/timer-scan.d.ts.map +1 -0
- package/dist/check/timer-scan.js +84 -0
- package/dist/check/timer-scan.js.map +1 -0
- package/dist/check/ui-keys.d.ts +3 -0
- package/dist/check/ui-keys.d.ts.map +1 -0
- package/dist/check/ui-keys.js +58 -0
- package/dist/check/ui-keys.js.map +1 -0
- package/dist/check-bin.d.ts +3 -0
- package/dist/check-bin.d.ts.map +1 -0
- package/dist/check-bin.js +17 -0
- package/dist/check-bin.js.map +1 -0
- package/dist/docs/built-in-guides.d.ts +12 -0
- package/dist/docs/built-in-guides.d.ts.map +1 -0
- package/dist/docs/built-in-guides.js +20 -0
- package/dist/docs/built-in-guides.js.map +1 -0
- package/dist/docs/docs-bin.d.ts +3 -0
- package/dist/docs/docs-bin.d.ts.map +1 -0
- package/dist/docs/docs-bin.js +98 -0
- package/dist/docs/docs-bin.js.map +1 -0
- package/dist/docs/docs-get.d.ts +11 -0
- package/dist/docs/docs-get.d.ts.map +1 -0
- package/dist/docs/docs-get.js +54 -0
- package/dist/docs/docs-get.js.map +1 -0
- package/dist/docs/docs-list.d.ts +22 -0
- package/dist/docs/docs-list.d.ts.map +1 -0
- package/dist/docs/docs-list.js +51 -0
- package/dist/docs/docs-list.js.map +1 -0
- package/dist/fake-clock.d.ts +16 -0
- package/dist/fake-clock.d.ts.map +1 -0
- package/dist/fake-clock.js +47 -0
- package/dist/fake-clock.js.map +1 -0
- package/dist/fake-openai.d.ts +55 -0
- package/dist/fake-openai.d.ts.map +1 -0
- package/dist/fake-openai.js +115 -0
- package/dist/fake-openai.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/new/new-bin.d.ts +3 -0
- package/dist/new/new-bin.d.ts.map +1 -0
- package/dist/new/new-bin.js +60 -0
- package/dist/new/new-bin.js.map +1 -0
- package/dist/new/new-project.d.ts +11 -0
- package/dist/new/new-project.d.ts.map +1 -0
- package/dist/new/new-project.js +36 -0
- package/dist/new/new-project.js.map +1 -0
- package/dist/new/scaffold-files.d.ts +7 -0
- package/dist/new/scaffold-files.d.ts.map +1 -0
- package/dist/new/scaffold-files.js +100 -0
- package/dist/new/scaffold-files.js.map +1 -0
- package/dist/new/scaffold-versions.d.ts +10 -0
- package/dist/new/scaffold-versions.d.ts.map +1 -0
- package/dist/new/scaffold-versions.js +12 -0
- package/dist/new/scaffold-versions.js.map +1 -0
- package/dist/preset/preset-bin.d.ts +3 -0
- package/dist/preset/preset-bin.d.ts.map +1 -0
- package/dist/preset/preset-bin.js +91 -0
- package/dist/preset/preset-bin.js.map +1 -0
- package/dist/preset/preset-check.d.ts +15 -0
- package/dist/preset/preset-check.d.ts.map +1 -0
- package/dist/preset/preset-check.js +113 -0
- package/dist/preset/preset-check.js.map +1 -0
- package/dist/preset/preset-new.d.ts +5 -0
- package/dist/preset/preset-new.d.ts.map +1 -0
- package/dist/preset/preset-new.js +16 -0
- package/dist/preset/preset-new.js.map +1 -0
- package/dist/preset/preset-reach.d.ts +18 -0
- package/dist/preset/preset-reach.d.ts.map +1 -0
- package/dist/preset/preset-reach.js +33 -0
- package/dist/preset/preset-reach.js.map +1 -0
- package/dist/preset/project-manifest.d.ts +5 -0
- package/dist/preset/project-manifest.d.ts.map +1 -0
- package/dist/preset/project-manifest.js +30 -0
- package/dist/preset/project-manifest.js.map +1 -0
- package/dist/preview/preview-bin.d.ts +3 -0
- package/dist/preview/preview-bin.d.ts.map +1 -0
- package/dist/preview/preview-bin.js +63 -0
- package/dist/preview/preview-bin.js.map +1 -0
- package/dist/preview/preview-home.d.ts +5 -0
- package/dist/preview/preview-home.d.ts.map +1 -0
- package/dist/preview/preview-home.js +64 -0
- package/dist/preview/preview-home.js.map +1 -0
- package/dist/preview/preview-manifest.d.ts +9 -0
- package/dist/preview/preview-manifest.d.ts.map +1 -0
- package/dist/preview/preview-manifest.js +32 -0
- package/dist/preview/preview-manifest.js.map +1 -0
- package/dist/preview/preview-port.d.ts +5 -0
- package/dist/preview/preview-port.d.ts.map +1 -0
- package/dist/preview/preview-port.js +22 -0
- package/dist/preview/preview-port.js.map +1 -0
- package/dist/preview/preview-preset.d.ts +14 -0
- package/dist/preview/preview-preset.d.ts.map +1 -0
- package/dist/preview/preview-preset.js +13 -0
- package/dist/preview/preview-preset.js.map +1 -0
- package/dist/preview/preview-run.d.ts +14 -0
- package/dist/preview/preview-run.d.ts.map +1 -0
- package/dist/preview/preview-run.js +241 -0
- package/dist/preview/preview-run.js.map +1 -0
- package/dist/preview/process-tree.d.ts +40 -0
- package/dist/preview/process-tree.d.ts.map +1 -0
- package/dist/preview/process-tree.js +92 -0
- package/dist/preview/process-tree.js.map +1 -0
- package/dist/running/call-query.d.ts +5 -0
- package/dist/running/call-query.d.ts.map +1 -0
- package/dist/running/call-query.js +34 -0
- package/dist/running/call-query.js.map +1 -0
- package/dist/running/running-kvman.d.ts +14 -0
- package/dist/running/running-kvman.d.ts.map +1 -0
- package/dist/running/running-kvman.js +80 -0
- package/dist/running/running-kvman.js.map +1 -0
- package/docs/i18n.md +20 -0
- package/docs/presets.md +15 -0
- package/docs/sdk.md +77 -0
- package/package.json +49 -0
- package/templates/AGENTS.md.template +13 -0
- package/templates/README.md.template +12 -0
- package/templates/extension-docs/usage.md.template +8 -0
- package/templates/src/docs.ts.template +46 -0
- package/templates/src/index.ts.template +42 -0
- package/templates/test/extension.test.ts.template +29 -0
- package/templates/web/components/Hello.vue.template +42 -0
- package/templates/web-build.ts.template +26 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { BinFailure, printFailure } from "../bin/bin-failure.js";
|
|
4
|
+
import { findRunningKvman } from "../running/running-kvman.js";
|
|
5
|
+
import { checkPreset } from "./preset-check.js";
|
|
6
|
+
import { writePresetFile } from "./preset-new.js";
|
|
7
|
+
// The `kvman-preset` bin (plan 09 §9.1, plan 10, ADR 0010, 2): writes a starting preset (`new`) and validates a
|
|
8
|
+
// preset's schema and references (`check`), for any harness or none. `check` asks the running kvman for its
|
|
9
|
+
// extensions, settings, and pages; with none running it checks the schema and the `path:` folders only. With
|
|
10
|
+
// `--json` each prints its output as one JSON line.
|
|
11
|
+
const usage = 'Usage: kvman-preset new <file> --name <name> [--json]\nUsage: kvman-preset check <file> [--json] [--home <dir>] [--url <url>]';
|
|
12
|
+
function parseOptions(argv) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = parseArgs({
|
|
16
|
+
args: argv,
|
|
17
|
+
allowPositionals: true,
|
|
18
|
+
options: {
|
|
19
|
+
name: { type: 'string' },
|
|
20
|
+
json: { type: 'boolean', default: false },
|
|
21
|
+
home: { type: 'string' },
|
|
22
|
+
url: { type: 'string' },
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
28
|
+
throw new BinFailure('VALIDATION_FAILED', `${reason} ${usage}`);
|
|
29
|
+
}
|
|
30
|
+
const [subcommand, ...rest] = parsed.positionals;
|
|
31
|
+
if (subcommand === 'new') {
|
|
32
|
+
if (parsed.values.home !== undefined || parsed.values.url !== undefined)
|
|
33
|
+
throw new BinFailure('VALIDATION_FAILED', `new takes no --home or --url. ${usage}`);
|
|
34
|
+
const [file, ...extra] = rest;
|
|
35
|
+
if (file === undefined || extra.length > 0)
|
|
36
|
+
throw new BinFailure('VALIDATION_FAILED', `new takes a single <file> argument. ${usage}`);
|
|
37
|
+
if (parsed.values.name === undefined)
|
|
38
|
+
throw new BinFailure('VALIDATION_FAILED', `--name is required. ${usage}`);
|
|
39
|
+
if (parsed.values.name === '')
|
|
40
|
+
throw new BinFailure('VALIDATION_FAILED', `--name must not be empty. ${usage}`);
|
|
41
|
+
return { kind: 'new', file, name: parsed.values.name, json: parsed.values.json ?? false };
|
|
42
|
+
}
|
|
43
|
+
if (subcommand === 'check') {
|
|
44
|
+
if (parsed.values.name !== undefined)
|
|
45
|
+
throw new BinFailure('VALIDATION_FAILED', `check takes no --name. ${usage}`);
|
|
46
|
+
const [file, ...extra] = rest;
|
|
47
|
+
if (file === undefined || extra.length > 0)
|
|
48
|
+
throw new BinFailure('VALIDATION_FAILED', `check takes a single <file> argument. ${usage}`);
|
|
49
|
+
return { kind: 'check', file, json: parsed.values.json ?? false, home: parsed.values.home, url: parsed.values.url };
|
|
50
|
+
}
|
|
51
|
+
if (subcommand === undefined)
|
|
52
|
+
throw new BinFailure('VALIDATION_FAILED', `A subcommand is required. ${usage}`);
|
|
53
|
+
throw new BinFailure('VALIDATION_FAILED', `Unknown subcommand ${JSON.stringify(subcommand)}; give new or check. ${usage}`);
|
|
54
|
+
}
|
|
55
|
+
function readableFindings(findings) {
|
|
56
|
+
if (findings.length === 0)
|
|
57
|
+
return 'No findings.\n';
|
|
58
|
+
return `${findings.map((finding) => `${finding.file}: ${finding.message}\n ${finding.hint}`).join('\n')}\n`;
|
|
59
|
+
}
|
|
60
|
+
const json = process.argv.includes('--json');
|
|
61
|
+
try {
|
|
62
|
+
const command = parseOptions(process.argv.slice(2));
|
|
63
|
+
if (command.kind === 'new') {
|
|
64
|
+
const created = writePresetFile(command.file, command.name);
|
|
65
|
+
if (command.json)
|
|
66
|
+
process.stdout.write(`${JSON.stringify({ file: created.file })}\n`);
|
|
67
|
+
else
|
|
68
|
+
process.stdout.write(`Wrote ${command.file}.\n`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const locate = {};
|
|
72
|
+
if (command.home !== undefined)
|
|
73
|
+
locate.home = command.home;
|
|
74
|
+
if (command.url !== undefined)
|
|
75
|
+
locate.url = command.url;
|
|
76
|
+
const checked = await checkPreset(command.file, findRunningKvman(locate));
|
|
77
|
+
if (command.json)
|
|
78
|
+
process.stdout.write(`${JSON.stringify(checked.findings)}\n`);
|
|
79
|
+
else
|
|
80
|
+
process.stdout.write(readableFindings(checked.findings));
|
|
81
|
+
if (!checked.running)
|
|
82
|
+
process.stderr.write("kvman isn't running; only the preset's schema and path: folders were checked.\n");
|
|
83
|
+
if (checked.findings.length > 0)
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
printFailure(error, json);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=preset-bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-bin.js","sourceRoot":"","sources":["../../src/preset/preset-bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RunningKvman } from '../running/running-kvman.ts';
|
|
2
|
+
/** A preset finding: `file` is the checked file exactly as given. */
|
|
3
|
+
export type PresetFinding = {
|
|
4
|
+
file: string;
|
|
5
|
+
message: string;
|
|
6
|
+
hint: string;
|
|
7
|
+
};
|
|
8
|
+
/** The check's findings, and whether a running kvman answered. */
|
|
9
|
+
export type PresetCheck = {
|
|
10
|
+
findings: PresetFinding[];
|
|
11
|
+
running: boolean;
|
|
12
|
+
};
|
|
13
|
+
/** Checks the preset in `file`: `file` resolves against the current working folder and stays as given in findings. */
|
|
14
|
+
export declare function checkPreset(file: string, found: RunningKvman | undefined): Promise<PresetCheck>;
|
|
15
|
+
//# sourceMappingURL=preset-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-check.d.ts","sourceRoot":"","sources":["../../src/preset/preset-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AASvF,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAgF1E,sHAAsH;AACtH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAiBrG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { healthSchema, kernelQuerySchemas, presetSchema, ProblemError, z } from '@kvman/sdk';
|
|
4
|
+
import { callQuery } from "../running/call-query.js";
|
|
5
|
+
import { KvmanUnreachableError } from "../running/running-kvman.js";
|
|
6
|
+
import { presetReach } from "./preset-reach.js";
|
|
7
|
+
// kvwebui's own pages (plan 06 §6.6), which no ui.get lists.
|
|
8
|
+
const builtInPages = ['kvwebui.settings', 'kvwebui.extensions'];
|
|
9
|
+
const extensionsListOutput = kernelQuerySchemas['kernel.extensions.list'].output;
|
|
10
|
+
const settingsListOutput = kernelQuerySchemas['kernel.settings.list'].output;
|
|
11
|
+
const uiGetOutput = z.object({ pages: z.array(z.object({ id: z.string() }).loose()) }).loose();
|
|
12
|
+
function readPreset(absolute) {
|
|
13
|
+
let raw;
|
|
14
|
+
try {
|
|
15
|
+
raw = JSON.parse(readFileSync(absolute, 'utf8'));
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof SyntaxError || (error instanceof Error && 'code' in error && error.code === 'ENOENT'))
|
|
19
|
+
return { problems: [{ message: `The preset can't be read: ${error.message}`, hint: 'A preset is one JSON object.' }] };
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
const parsed = presetSchema.safeParse(raw);
|
|
23
|
+
if (parsed.success)
|
|
24
|
+
return { preset: parsed.data };
|
|
25
|
+
return { problems: parsed.error.issues.map((issue) => ({ message: `${issue.path.join('.') || 'preset'}: ${issue.message}`, hint: 'A preset is { name, extensions, settings? }, with no other keys (plan 02 §2.10).' })) };
|
|
26
|
+
}
|
|
27
|
+
function settingProblems(settings, reach, registered) {
|
|
28
|
+
const problems = [];
|
|
29
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
30
|
+
const namespace = key.split('.')[0] ?? '';
|
|
31
|
+
if (namespace === 'kernel' || reach.loaded.has(namespace)) {
|
|
32
|
+
const setting = registered.find((candidate) => candidate.key === key);
|
|
33
|
+
if (setting === undefined)
|
|
34
|
+
problems.push({ message: `${key} isn't a setting of ${namespace}.`, hint: 'kernel.settings.list lists every registered key.' });
|
|
35
|
+
else {
|
|
36
|
+
const checked = z.fromJSONSchema(setting.schema).safeParse(value);
|
|
37
|
+
if (!checked.success)
|
|
38
|
+
problems.push({ message: `${key}: ${checked.error.issues.map((issue) => issue.message).join('; ')}`, hint: `${key} is: ${setting.description}` });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (!reach.unloaded.has(namespace) && !reach.hasNpm) {
|
|
42
|
+
problems.push({ message: `${key} belongs to ${namespace}, which isn't kernel or one of the preset's extensions.`, hint: 'Add the extension that registers it, or remove the key.' });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return problems;
|
|
46
|
+
}
|
|
47
|
+
async function pagesOf(running, namespace) {
|
|
48
|
+
let answer;
|
|
49
|
+
try {
|
|
50
|
+
answer = await callQuery(running, `${namespace}.ui.get`, {}, uiGetOutput);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof ProblemError)
|
|
54
|
+
return error;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
return answer.pages.map((page) => `${namespace}.${page.id}`);
|
|
58
|
+
}
|
|
59
|
+
async function homeProblems(running, home, reach) {
|
|
60
|
+
if (typeof home !== 'string' || builtInPages.includes(home))
|
|
61
|
+
return [];
|
|
62
|
+
const namespace = home.split('.')[0] ?? '';
|
|
63
|
+
if (reach.unloaded.has(namespace) || (reach.hasNpm && !reach.loaded.has(namespace)))
|
|
64
|
+
return [];
|
|
65
|
+
const hint = 'kvwebui.home names a page with no params, as <namespace>.<page>, or kvwebui.extensions.';
|
|
66
|
+
if (!reach.loaded.has(namespace))
|
|
67
|
+
return [{ message: `kvwebui.home ${home} is under ${namespace}, which isn't one of the preset's extensions.`, hint }];
|
|
68
|
+
const pages = await pagesOf(running, namespace);
|
|
69
|
+
if (pages instanceof ProblemError)
|
|
70
|
+
return [{ message: `kvwebui.home ${home}: ${namespace}.ui.get fails with ${pages.problem.code}.`, hint }];
|
|
71
|
+
return pages.includes(home) ? [] : [{ message: `kvwebui.home ${home} isn't a page of ${namespace}.`, hint: `${namespace}'s pages: ${pages.join(', ') || 'none'}.` }];
|
|
72
|
+
}
|
|
73
|
+
function toFindings(file, problems) {
|
|
74
|
+
return problems.map((problem) => ({ file, ...problem }));
|
|
75
|
+
}
|
|
76
|
+
// A kvman that was found counts as running only once it answers; a refused connection means it doesn't.
|
|
77
|
+
async function answering(found) {
|
|
78
|
+
if (found === undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
try {
|
|
81
|
+
await callQuery(found, 'kernel.health.get', {}, healthSchema);
|
|
82
|
+
return found;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (error instanceof KvmanUnreachableError)
|
|
86
|
+
return undefined;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Checks the preset in `file`: `file` resolves against the current working folder and stays as given in findings. */
|
|
91
|
+
export async function checkPreset(file, found) {
|
|
92
|
+
const running = await answering(found);
|
|
93
|
+
const absolute = path.resolve(file);
|
|
94
|
+
const read = readPreset(absolute);
|
|
95
|
+
if ('problems' in read)
|
|
96
|
+
return { findings: toFindings(file, read.problems), running: running !== undefined };
|
|
97
|
+
const preset = read.preset;
|
|
98
|
+
const local = presetReach(preset, absolute, undefined);
|
|
99
|
+
if (running === undefined)
|
|
100
|
+
return { findings: toFindings(file, local.problems), running: false };
|
|
101
|
+
try {
|
|
102
|
+
const reach = presetReach(preset, absolute, await callQuery(running, 'kernel.extensions.list', {}, extensionsListOutput));
|
|
103
|
+
const registered = await callQuery(running, 'kernel.settings.list', {}, settingsListOutput);
|
|
104
|
+
const settings = preset.settings ?? {};
|
|
105
|
+
return { findings: toFindings(file, [...reach.problems, ...settingProblems(settings, reach, registered), ...(await homeProblems(running, settings['kvwebui.home'], reach))]), running: true };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof KvmanUnreachableError)
|
|
109
|
+
return { findings: toFindings(file, local.problems), running: false };
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=preset-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-check.js","sourceRoot":"","sources":["../../src/preset/preset-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-new.d.ts","sourceRoot":"","sources":["../../src/preset/preset-new.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAO5E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { BinFailure } from "../bin/bin-failure.js";
|
|
4
|
+
// `kvman-preset new` (plan 09 §9.1): a preset that runs as-is, kvai and kvwebui with the Extensions page as its
|
|
5
|
+
// home, for the person to add extensions and settings to. `<file>` resolves against the current working folder.
|
|
6
|
+
/** Writes the starting preset to `file` and resolves to its absolute path. */
|
|
7
|
+
export function writePresetFile(file, name) {
|
|
8
|
+
const absolute = path.resolve(file);
|
|
9
|
+
if (existsSync(absolute))
|
|
10
|
+
throw new BinFailure('FILE_EXISTS', `${file} already exists; choose another file.`, { file });
|
|
11
|
+
const preset = { name, extensions: { '@kvman/kvai': 'bundled', '@kvman/kvwebui': 'bundled' }, settings: { 'kvwebui.home': 'kvwebui.extensions' } };
|
|
12
|
+
mkdirSync(path.dirname(absolute), { recursive: true });
|
|
13
|
+
writeFileSync(absolute, `${JSON.stringify(preset, null, 2)}\n`);
|
|
14
|
+
return { file: absolute };
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=preset-new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-new.js","sourceRoot":"","sources":["../../src/preset/preset-new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,gHAAgH;AAChH,gHAAgH;AAEhH,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAY;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,aAAa,EAAE,GAAG,IAAI,uCAAuC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACxH,MAAM,MAAM,GAAW,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,oBAAoB,EAAE,EAAE,CAAC;IAC3J,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { extensionInfoSchema, type Preset, type z } from '@kvman/sdk';
|
|
2
|
+
/** An extension as `kernel.extensions.list` describes it. */
|
|
3
|
+
export type ExtensionInfo = z.infer<typeof extensionInfoSchema>;
|
|
4
|
+
export type PresetReach = {
|
|
5
|
+
/** Namespaces of the preset's extensions loaded by the running kvman, whose settings and pages can be checked. */
|
|
6
|
+
loaded: ReadonlySet<string>;
|
|
7
|
+
/** Namespaces of its `path:` extensions, which aren't loaded by the running kvman. */
|
|
8
|
+
unloaded: ReadonlySet<string>;
|
|
9
|
+
/** Whether it has `npm:` extensions, whose namespaces aren't known without fetching them. */
|
|
10
|
+
hasNpm: boolean;
|
|
11
|
+
problems: {
|
|
12
|
+
message: string;
|
|
13
|
+
hint: string;
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
/** Sorts the preset's extensions into loaded and unloaded namespaces, with a problem per bad reference. */
|
|
17
|
+
export declare function presetReach(preset: Preset, presetFile: string, extensions: readonly ExtensionInfo[] | undefined): PresetReach;
|
|
18
|
+
//# sourceMappingURL=preset-reach.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-reach.d.ts","sourceRoot":"","sources":["../../src/preset/preset-reach.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,KAAK,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,YAAY,CAAC;AAQtE,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG;IACxB,kHAAkH;IAClH,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,6FAA6F;IAC7F,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C,CAAC;AAEF,2GAA2G;AAC3G,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,GAAG,WAAW,CAsB7H"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { extensionInfoSchema } from '@kvman/sdk';
|
|
3
|
+
import { readProjectManifest } from "./project-manifest.js";
|
|
4
|
+
/** Sorts the preset's extensions into loaded and unloaded namespaces, with a problem per bad reference. */
|
|
5
|
+
export function presetReach(preset, presetFile, extensions) {
|
|
6
|
+
const loaded = new Set();
|
|
7
|
+
const unloaded = new Set();
|
|
8
|
+
const problems = [];
|
|
9
|
+
let hasNpm = false;
|
|
10
|
+
for (const [name, source] of Object.entries(preset.extensions)) {
|
|
11
|
+
if (source === 'bundled') {
|
|
12
|
+
const bundled = extensions?.find((extension) => extension.name === name && extension.source === 'bundled');
|
|
13
|
+
if (bundled !== undefined)
|
|
14
|
+
loaded.add(bundled.namespace);
|
|
15
|
+
else if (extensions !== undefined) {
|
|
16
|
+
problems.push({ message: `${name} isn't a bundled extension.`, hint: `The bundled extensions are ${extensions.filter((extension) => extension.source === 'bundled').map((extension) => extension.name).join(', ')}.` });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else if (source.startsWith('path:')) {
|
|
20
|
+
const folder = path.resolve(path.dirname(presetFile), source.slice('path:'.length));
|
|
21
|
+
const manifest = readProjectManifest(folder);
|
|
22
|
+
if (manifest === undefined)
|
|
23
|
+
problems.push({ message: `${name}: ${folder} has no package.json with a kvman field.`, hint: 'A path: source is a project folder, relative to the preset file.' });
|
|
24
|
+
else
|
|
25
|
+
unloaded.add(manifest.namespace);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
hasNpm = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { loaded, unloaded, hasNpm, problems };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=preset-reach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-reach.js","sourceRoot":"","sources":["../../src/preset/preset-reach.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAuB,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAoB5D,2GAA2G;AAC3G,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,UAAkB,EAAE,UAAgD;IAC9G,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAC7C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,UAAU,EAAE,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;YAC3G,IAAI,OAAO,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;iBACpD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,6BAA6B,EAAE,IAAI,EAAE,8BAA8B,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAC1N,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACpF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,QAAQ,KAAK,SAAS;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,KAAK,MAAM,0CAA0C,EAAE,IAAI,EAAE,kEAAkE,EAAE,CAAC,CAAC;;gBAC1L,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-manifest.d.ts","sourceRoot":"","sources":["../../src/preset/project-manifest.ts"],"names":[],"mappings":"AAaA,iHAAiH;AACjH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAarF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from '@kvman/sdk';
|
|
4
|
+
// Where a `path:` extension's namespace comes from (ADR 0009, 121): the folder, relative to the preset file, must
|
|
5
|
+
// hold a package.json with a `kvman` field. Anything else means the folder isn't a project.
|
|
6
|
+
const manifestSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
kvman: z.object({ namespace: z.string() }).loose(),
|
|
9
|
+
})
|
|
10
|
+
.loose();
|
|
11
|
+
/** The namespace of the project in `folder`, or `undefined` when it has no package.json with a `kvman` field. */
|
|
12
|
+
export function readProjectManifest(folder) {
|
|
13
|
+
const file = path.join(folder, 'package.json');
|
|
14
|
+
if (!existsSync(file))
|
|
15
|
+
return undefined;
|
|
16
|
+
let raw;
|
|
17
|
+
try {
|
|
18
|
+
raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof SyntaxError)
|
|
22
|
+
return undefined;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
const parsed = manifestSchema.safeParse(raw);
|
|
26
|
+
if (!parsed.success)
|
|
27
|
+
return undefined;
|
|
28
|
+
return { namespace: parsed.data.kvman.namespace };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=project-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-manifest.js","sourceRoot":"","sources":["../../src/preset/project-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,kHAAkH;AAClH,4FAA4F;AAE5F,MAAM,cAAc,GAAG,CAAC;KACrB,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE;CACnD,CAAC;KACD,KAAK,EAAE,CAAC;AAEX,iHAAiH;AACjH,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW;YAAE,OAAO,SAAS,CAAC;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-bin.d.ts","sourceRoot":"","sources":["../../src/preview/preview-bin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { z } from '@kvman/sdk';
|
|
4
|
+
import { BinFailure, printFailure } from "../bin/bin-failure.js";
|
|
5
|
+
import { runPreview } from "./preview-run.js";
|
|
6
|
+
// The `kvman-preview` bin (plan 09 §9.3, plan 10, ADR 0010, 2, 7, 21): runs a preview kvman in a terminal's foreground
|
|
7
|
+
// for the extension projects in `<folder>…`, on its own home and port. It prints the URL once the preview answers
|
|
8
|
+
// and keeps running until SIGINT or SIGTERM. With `--json` it prints `{ "url" }` as one JSON line.
|
|
9
|
+
const usage = 'Usage: kvman-preview <folder>… [--preset <file>] [--kvman <entry>] [--name <name>] [--json]';
|
|
10
|
+
const optionsSchema = z.object({
|
|
11
|
+
folders: z.array(z.string().min(1)).min(1),
|
|
12
|
+
preset: z.string().min(1).optional(),
|
|
13
|
+
kvman: z.string().min(1).optional(),
|
|
14
|
+
name: z.string().min(1),
|
|
15
|
+
json: z.boolean(),
|
|
16
|
+
});
|
|
17
|
+
function parseOptions(argv) {
|
|
18
|
+
let parsed;
|
|
19
|
+
try {
|
|
20
|
+
parsed = parseArgs({
|
|
21
|
+
args: argv,
|
|
22
|
+
allowPositionals: true,
|
|
23
|
+
options: {
|
|
24
|
+
preset: { type: 'string' },
|
|
25
|
+
kvman: { type: 'string' },
|
|
26
|
+
name: { type: 'string', default: 'preview' },
|
|
27
|
+
json: { type: 'boolean', default: false },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
33
|
+
throw new BinFailure('VALIDATION_FAILED', `${reason} ${usage}`);
|
|
34
|
+
}
|
|
35
|
+
const checked = optionsSchema.safeParse({
|
|
36
|
+
folders: parsed.positionals,
|
|
37
|
+
preset: parsed.values.preset,
|
|
38
|
+
kvman: parsed.values.kvman,
|
|
39
|
+
name: parsed.values.name ?? 'preview',
|
|
40
|
+
json: parsed.values.json ?? false,
|
|
41
|
+
});
|
|
42
|
+
if (!checked.success) {
|
|
43
|
+
if (parsed.positionals.length === 0)
|
|
44
|
+
throw new BinFailure('VALIDATION_FAILED', `At least one <folder> argument is required. ${usage}`);
|
|
45
|
+
throw new BinFailure('VALIDATION_FAILED', `${checked.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join('; ')} ${usage}`);
|
|
46
|
+
}
|
|
47
|
+
return checked.data;
|
|
48
|
+
}
|
|
49
|
+
const json = process.argv.includes('--json');
|
|
50
|
+
try {
|
|
51
|
+
const options = parseOptions(process.argv.slice(2));
|
|
52
|
+
await runPreview(options, (url) => {
|
|
53
|
+
if (options.json)
|
|
54
|
+
process.stdout.write(`${JSON.stringify({ url })}\n`);
|
|
55
|
+
else
|
|
56
|
+
process.stdout.write(`Preview running at ${url}\n`);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
printFailure(error, json);
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=preview-bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-bin.js","sourceRoot":"","sources":["../../src/preview/preview-bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The preview's home folder for `name`. */
|
|
2
|
+
export declare function previewHome(name: string): string;
|
|
3
|
+
/** Empties the preview's home, or fails `PREVIEW_FAILED` when its preview still runs. */
|
|
4
|
+
export declare function preparePreviewHome(name: string): string;
|
|
5
|
+
//# sourceMappingURL=preview-home.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-home.d.ts","sourceRoot":"","sources":["../../src/preview/preview-home.ts"],"names":[],"mappings":"AA8CA,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWvD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { z } from '@kvman/sdk';
|
|
5
|
+
import { BinFailure } from "../bin/bin-failure.js";
|
|
6
|
+
// The preview's home (plan 09 §9.3, ADR 0009, 125): `<os temp>/kvman-preview-<name>`, emptied at each start and removed
|
|
7
|
+
// when the preview stops. A home whose `kvman.lock` names a live pid means its preview still runs.
|
|
8
|
+
const lockSchema = z.object({ pid: z.number().int().positive() }).loose();
|
|
9
|
+
function hasCode(error, code) {
|
|
10
|
+
return error instanceof Error && 'code' in error && error.code === code;
|
|
11
|
+
}
|
|
12
|
+
// The same liveness rule as the running-kvman lookup: a pid that can't be signaled is gone, unless permission says
|
|
13
|
+
// it exists.
|
|
14
|
+
function isAlive(pid) {
|
|
15
|
+
try {
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (hasCode(error, 'ESRCH'))
|
|
21
|
+
return false;
|
|
22
|
+
if (hasCode(error, 'EPERM'))
|
|
23
|
+
return true;
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function lockPid(home) {
|
|
28
|
+
let text;
|
|
29
|
+
try {
|
|
30
|
+
text = readFileSync(path.join(home, 'kvman.lock'), 'utf8');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (hasCode(error, 'ENOENT'))
|
|
34
|
+
return undefined;
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = JSON.parse(text);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const lock = lockSchema.safeParse(raw);
|
|
45
|
+
return lock.success ? lock.data.pid : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** The preview's home folder for `name`. */
|
|
48
|
+
export function previewHome(name) {
|
|
49
|
+
return path.join(tmpdir(), `kvman-preview-${name}`);
|
|
50
|
+
}
|
|
51
|
+
/** Empties the preview's home, or fails `PREVIEW_FAILED` when its preview still runs. */
|
|
52
|
+
export function preparePreviewHome(name) {
|
|
53
|
+
const home = previewHome(name);
|
|
54
|
+
if (existsSync(home)) {
|
|
55
|
+
const pid = lockPid(home);
|
|
56
|
+
if (pid !== undefined && isAlive(pid)) {
|
|
57
|
+
throw new BinFailure('PREVIEW_FAILED', `A preview named ${name} already runs; stop it first or use --name.`, { name });
|
|
58
|
+
}
|
|
59
|
+
rmSync(home, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
mkdirSync(home, { recursive: true });
|
|
62
|
+
return home;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=preview-home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-home.js","sourceRoot":"","sources":["../../src/preview/preview-home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,wHAAwH;AACxH,mGAAmG;AAEnG,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AAE1E,SAAS,OAAO,CAAC,KAAc,EAAE,IAAY;IAC3C,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAC1E,CAAC;AAED,mHAAmH;AACnH,aAAa;AACb,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/C,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAE,mBAAmB,IAAI,6CAA6C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACzH,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** A project: its package name and whether it has a `web:watch` script. */
|
|
2
|
+
export type PreviewProject = {
|
|
3
|
+
name: string;
|
|
4
|
+
folder: string;
|
|
5
|
+
webWatch: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** Reads the project in `folder`, or `undefined` when it has no package.json with a `kvman` field. */
|
|
8
|
+
export declare function readPreviewProject(folder: string): PreviewProject | undefined;
|
|
9
|
+
//# sourceMappingURL=preview-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-manifest.d.ts","sourceRoot":"","sources":["../../src/preview/preview-manifest.ts"],"names":[],"mappings":"AAeA,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjF,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAa7E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from '@kvman/sdk';
|
|
4
|
+
// The project check (plan 09 §9.3, ADR 0009, 121): a folder is a project when its package.json has a `kvman` field.
|
|
5
|
+
// The preview needs the package name for the generated preset and the scripts for `web:watch`.
|
|
6
|
+
const manifestSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
name: z.string(),
|
|
9
|
+
scripts: z.record(z.string(), z.string()).optional(),
|
|
10
|
+
kvman: z.object({ namespace: z.string() }).loose(),
|
|
11
|
+
})
|
|
12
|
+
.loose();
|
|
13
|
+
/** Reads the project in `folder`, or `undefined` when it has no package.json with a `kvman` field. */
|
|
14
|
+
export function readPreviewProject(folder) {
|
|
15
|
+
const file = path.join(folder, 'package.json');
|
|
16
|
+
if (!existsSync(file))
|
|
17
|
+
return undefined;
|
|
18
|
+
let raw;
|
|
19
|
+
try {
|
|
20
|
+
raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof SyntaxError)
|
|
24
|
+
return undefined;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
const parsed = manifestSchema.safeParse(raw);
|
|
28
|
+
if (!parsed.success)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { name: parsed.data.name, folder, webWatch: parsed.data.scripts?.['web:watch'] !== undefined };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=preview-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-manifest.js","sourceRoot":"","sources":["../../src/preview/preview-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,oHAAoH;AACpH,+FAA+F;AAE/F,MAAM,cAAc,GAAG,CAAC;KACrB,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACpD,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE;CACnD,CAAC;KACD,KAAK,EAAE,CAAC;AAKX,sGAAsG;AACtG,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW;YAAE,OAAO,SAAS,CAAC;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;AACxG,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const firstPreviewPort = 3738;
|
|
2
|
+
export declare const lastPreviewPort = 3837;
|
|
3
|
+
/** The first free preview port, or `NO_FREE_PORT` when 3738 to 3837 are all taken. */
|
|
4
|
+
export declare function previewPort(isFree?: (port: number) => Promise<boolean>): Promise<number>;
|
|
5
|
+
//# sourceMappingURL=preview-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-port.d.ts","sourceRoot":"","sources":["../../src/preview/preview-port.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,eAAe,OAAO,CAAC;AAUpC,sFAAsF;AACtF,wBAAsB,WAAW,CAAC,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAK1G"}
|