@hydranium/cli 1.0.0-next.10
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 +240 -0
- package/heap-analysis/analyze-heap.mjs +798 -0
- package/heap-analysis/heap/core/aggregate.mjs +152 -0
- package/heap-analysis/heap/core/bucket-label.mjs +23 -0
- package/heap-analysis/heap/core/cutpoints.mjs +51 -0
- package/heap-analysis/heap/core/diff.mjs +56 -0
- package/heap-analysis/heap/core/format.mjs +47 -0
- package/heap-analysis/heap/core/holders.mjs +80 -0
- package/heap-analysis/heap/core/load.mjs +46 -0
- package/heap-analysis/heap/core/report.mjs +68 -0
- package/heap-analysis/heap/core/strings.mjs +80 -0
- package/heap-analysis/heap/langium/concepts.mjs +230 -0
- package/heap-analysis/heap/langium/glsp.classifier.mjs +80 -0
- package/heap-analysis/heap/langium/langium.classifier.mjs +203 -0
- package/heap-analysis/heap/renderer/renderer.mjs +122 -0
- package/lib/cli-version.d.ts +21 -0
- package/lib/cli-version.d.ts.map +1 -0
- package/lib/cli-version.js +30 -0
- package/lib/cli-version.js.map +1 -0
- package/lib/cli.d.ts +11 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +135 -0
- package/lib/cli.js.map +1 -0
- package/lib/commands/analyze-heap-args.d.ts +46 -0
- package/lib/commands/analyze-heap-args.d.ts.map +1 -0
- package/lib/commands/analyze-heap-args.js +174 -0
- package/lib/commands/analyze-heap-args.js.map +1 -0
- package/lib/commands/analyze-heap.d.ts +37 -0
- package/lib/commands/analyze-heap.d.ts.map +1 -0
- package/lib/commands/analyze-heap.js +63 -0
- package/lib/commands/analyze-heap.js.map +1 -0
- package/lib/commands/ast-ground-truth-args.d.ts +23 -0
- package/lib/commands/ast-ground-truth-args.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-args.js +47 -0
- package/lib/commands/ast-ground-truth-args.js.map +1 -0
- package/lib/commands/ast-ground-truth-driver.d.ts +10 -0
- package/lib/commands/ast-ground-truth-driver.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-driver.js +72 -0
- package/lib/commands/ast-ground-truth-driver.js.map +1 -0
- package/lib/commands/ast-ground-truth.d.ts +31 -0
- package/lib/commands/ast-ground-truth.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth.js +36 -0
- package/lib/commands/ast-ground-truth.js.map +1 -0
- package/lib/commands/generate-transfer-model-args.d.ts +38 -0
- package/lib/commands/generate-transfer-model-args.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-args.js +183 -0
- package/lib/commands/generate-transfer-model-args.js.map +1 -0
- package/lib/commands/generate-transfer-model-config.d.ts +32 -0
- package/lib/commands/generate-transfer-model-config.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-config.js +122 -0
- package/lib/commands/generate-transfer-model-config.js.map +1 -0
- package/lib/commands/generate-transfer-model.d.ts +110 -0
- package/lib/commands/generate-transfer-model.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model.js +587 -0
- package/lib/commands/generate-transfer-model.js.map +1 -0
- package/lib/commands/harness-args.d.ts +198 -0
- package/lib/commands/harness-args.d.ts.map +1 -0
- package/lib/commands/harness-args.js +339 -0
- package/lib/commands/harness-args.js.map +1 -0
- package/lib/commands/headless-harness.d.ts +97 -0
- package/lib/commands/headless-harness.d.ts.map +1 -0
- package/lib/commands/headless-harness.js +121 -0
- package/lib/commands/headless-harness.js.map +1 -0
- package/lib/commands/init-args.d.ts +81 -0
- package/lib/commands/init-args.d.ts.map +1 -0
- package/lib/commands/init-args.js +250 -0
- package/lib/commands/init-args.js.map +1 -0
- package/lib/commands/init-prompt.d.ts +66 -0
- package/lib/commands/init-prompt.d.ts.map +1 -0
- package/lib/commands/init-prompt.js +71 -0
- package/lib/commands/init-prompt.js.map +1 -0
- package/lib/commands/init-templates.d.ts +42 -0
- package/lib/commands/init-templates.d.ts.map +1 -0
- package/lib/commands/init-templates.js +1902 -0
- package/lib/commands/init-templates.js.map +1 -0
- package/lib/commands/init-wizard.d.ts +65 -0
- package/lib/commands/init-wizard.d.ts.map +1 -0
- package/lib/commands/init-wizard.js +302 -0
- package/lib/commands/init-wizard.js.map +1 -0
- package/lib/commands/init-workspace.d.ts +168 -0
- package/lib/commands/init-workspace.d.ts.map +1 -0
- package/lib/commands/init-workspace.js +372 -0
- package/lib/commands/init-workspace.js.map +1 -0
- package/lib/commands/init.d.ts +273 -0
- package/lib/commands/init.d.ts.map +1 -0
- package/lib/commands/init.js +303 -0
- package/lib/commands/init.js.map +1 -0
- package/lib/commands/lint-grammar-args.d.ts +23 -0
- package/lib/commands/lint-grammar-args.d.ts.map +1 -0
- package/lib/commands/lint-grammar-args.js +63 -0
- package/lib/commands/lint-grammar-args.js.map +1 -0
- package/lib/commands/lint-grammar-driver.d.ts +10 -0
- package/lib/commands/lint-grammar-driver.d.ts.map +1 -0
- package/lib/commands/lint-grammar-driver.js +72 -0
- package/lib/commands/lint-grammar-driver.js.map +1 -0
- package/lib/commands/lint-grammar-report.d.ts +24 -0
- package/lib/commands/lint-grammar-report.d.ts.map +1 -0
- package/lib/commands/lint-grammar-report.js +65 -0
- package/lib/commands/lint-grammar-report.js.map +1 -0
- package/lib/commands/lint-grammar.d.ts +33 -0
- package/lib/commands/lint-grammar.d.ts.map +1 -0
- package/lib/commands/lint-grammar.js +42 -0
- package/lib/commands/lint-grammar.js.map +1 -0
- package/lib/commands/measure-memory-args.d.ts +23 -0
- package/lib/commands/measure-memory-args.d.ts.map +1 -0
- package/lib/commands/measure-memory-args.js +106 -0
- package/lib/commands/measure-memory-args.js.map +1 -0
- package/lib/commands/measure-memory-driver.d.ts +10 -0
- package/lib/commands/measure-memory-driver.d.ts.map +1 -0
- package/lib/commands/measure-memory-driver.js +110 -0
- package/lib/commands/measure-memory-driver.js.map +1 -0
- package/lib/commands/measure-memory.d.ts +68 -0
- package/lib/commands/measure-memory.d.ts.map +1 -0
- package/lib/commands/measure-memory.js +100 -0
- package/lib/commands/measure-memory.js.map +1 -0
- package/lib/commands/model-docs-args.d.ts +23 -0
- package/lib/commands/model-docs-args.d.ts.map +1 -0
- package/lib/commands/model-docs-args.js +53 -0
- package/lib/commands/model-docs-args.js.map +1 -0
- package/lib/commands/model-docs-driver.d.ts +10 -0
- package/lib/commands/model-docs-driver.d.ts.map +1 -0
- package/lib/commands/model-docs-driver.js +61 -0
- package/lib/commands/model-docs-driver.js.map +1 -0
- package/lib/commands/model-docs-report.d.ts +18 -0
- package/lib/commands/model-docs-report.d.ts.map +1 -0
- package/lib/commands/model-docs-report.js +123 -0
- package/lib/commands/model-docs-report.js.map +1 -0
- package/lib/commands/model-docs.d.ts +30 -0
- package/lib/commands/model-docs.d.ts.map +1 -0
- package/lib/commands/model-docs.js +37 -0
- package/lib/commands/model-docs.js.map +1 -0
- package/lib/commands/projects-args.d.ts +25 -0
- package/lib/commands/projects-args.d.ts.map +1 -0
- package/lib/commands/projects-args.js +49 -0
- package/lib/commands/projects-args.js.map +1 -0
- package/lib/commands/projects.d.ts +45 -0
- package/lib/commands/projects.d.ts.map +1 -0
- package/lib/commands/projects.js +39 -0
- package/lib/commands/projects.js.map +1 -0
- package/lib/commands/query-args.d.ts +25 -0
- package/lib/commands/query-args.d.ts.map +1 -0
- package/lib/commands/query-args.js +46 -0
- package/lib/commands/query-args.js.map +1 -0
- package/lib/commands/query.d.ts +32 -0
- package/lib/commands/query.d.ts.map +1 -0
- package/lib/commands/query.js +29 -0
- package/lib/commands/query.js.map +1 -0
- package/lib/commands/reflect-args.d.ts +23 -0
- package/lib/commands/reflect-args.d.ts.map +1 -0
- package/lib/commands/reflect-args.js +59 -0
- package/lib/commands/reflect-args.js.map +1 -0
- package/lib/commands/reflect-driver.d.ts +10 -0
- package/lib/commands/reflect-driver.d.ts.map +1 -0
- package/lib/commands/reflect-driver.js +66 -0
- package/lib/commands/reflect-driver.js.map +1 -0
- package/lib/commands/reflect-report.d.ts +20 -0
- package/lib/commands/reflect-report.d.ts.map +1 -0
- package/lib/commands/reflect-report.js +127 -0
- package/lib/commands/reflect-report.js.map +1 -0
- package/lib/commands/reflect.d.ts +30 -0
- package/lib/commands/reflect.d.ts.map +1 -0
- package/lib/commands/reflect.js +38 -0
- package/lib/commands/reflect.js.map +1 -0
- package/lib/commands/save-args.d.ts +26 -0
- package/lib/commands/save-args.d.ts.map +1 -0
- package/lib/commands/save-args.js +59 -0
- package/lib/commands/save-args.js.map +1 -0
- package/lib/commands/save.d.ts +38 -0
- package/lib/commands/save.d.ts.map +1 -0
- package/lib/commands/save.js +48 -0
- package/lib/commands/save.js.map +1 -0
- package/lib/commands/validate-args.d.ts +23 -0
- package/lib/commands/validate-args.d.ts.map +1 -0
- package/lib/commands/validate-args.js +60 -0
- package/lib/commands/validate-args.js.map +1 -0
- package/lib/commands/validate-driver.d.ts +10 -0
- package/lib/commands/validate-driver.d.ts.map +1 -0
- package/lib/commands/validate-driver.js +72 -0
- package/lib/commands/validate-driver.js.map +1 -0
- package/lib/commands/validate-report.d.ts +30 -0
- package/lib/commands/validate-report.d.ts.map +1 -0
- package/lib/commands/validate-report.js +87 -0
- package/lib/commands/validate-report.js.map +1 -0
- package/lib/commands/validate.d.ts +34 -0
- package/lib/commands/validate.d.ts.map +1 -0
- package/lib/commands/validate.js +41 -0
- package/lib/commands/validate.js.map +1 -0
- package/lib/commands/watch-args.d.ts +26 -0
- package/lib/commands/watch-args.d.ts.map +1 -0
- package/lib/commands/watch-args.js +59 -0
- package/lib/commands/watch-args.js.map +1 -0
- package/lib/commands/watch.d.ts +75 -0
- package/lib/commands/watch.d.ts.map +1 -0
- package/lib/commands/watch.js +112 -0
- package/lib/commands/watch.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/log-level.d.ts +39 -0
- package/lib/log-level.d.ts.map +1 -0
- package/lib/log-level.js +47 -0
- package/lib/log-level.js.map +1 -0
- package/lib/package-name.d.ts +33 -0
- package/lib/package-name.d.ts.map +1 -0
- package/lib/package-name.js +37 -0
- package/lib/package-name.js.map +1 -0
- package/lib/spawn-data-server.d.ts +102 -0
- package/lib/spawn-data-server.d.ts.map +1 -0
- package/lib/spawn-data-server.js +154 -0
- package/lib/spawn-data-server.js.map +1 -0
- package/lib/testing/echo-server.d.ts +11 -0
- package/lib/testing/echo-server.d.ts.map +1 -0
- package/lib/testing/echo-server.js +79 -0
- package/lib/testing/echo-server.js.map +1 -0
- package/package.json +105 -0
- package/src/cli-version.ts +32 -0
- package/src/cli.ts +139 -0
- package/src/commands/analyze-heap-args.ts +184 -0
- package/src/commands/analyze-heap.ts +76 -0
- package/src/commands/ast-ground-truth-args.ts +63 -0
- package/src/commands/ast-ground-truth-driver.ts +81 -0
- package/src/commands/ast-ground-truth.ts +49 -0
- package/src/commands/generate-transfer-model-args.ts +203 -0
- package/src/commands/generate-transfer-model-config.ts +128 -0
- package/src/commands/generate-transfer-model.ts +716 -0
- package/src/commands/harness-args.ts +429 -0
- package/src/commands/headless-harness.ts +165 -0
- package/src/commands/init-args.ts +273 -0
- package/src/commands/init-prompt.ts +149 -0
- package/src/commands/init-templates.ts +2037 -0
- package/src/commands/init-wizard.ts +378 -0
- package/src/commands/init-workspace.ts +442 -0
- package/src/commands/init.ts +554 -0
- package/src/commands/lint-grammar-args.ts +79 -0
- package/src/commands/lint-grammar-driver.ts +83 -0
- package/src/commands/lint-grammar-report.ts +74 -0
- package/src/commands/lint-grammar.ts +57 -0
- package/src/commands/measure-memory-args.ts +123 -0
- package/src/commands/measure-memory-driver.ts +128 -0
- package/src/commands/measure-memory.ts +143 -0
- package/src/commands/model-docs-args.ts +69 -0
- package/src/commands/model-docs-driver.ts +68 -0
- package/src/commands/model-docs-report.ts +133 -0
- package/src/commands/model-docs.ts +48 -0
- package/src/commands/projects-args.ts +62 -0
- package/src/commands/projects.ts +76 -0
- package/src/commands/query-args.ts +61 -0
- package/src/commands/query.ts +58 -0
- package/src/commands/reflect-args.ts +75 -0
- package/src/commands/reflect-driver.ts +76 -0
- package/src/commands/reflect-report.ts +138 -0
- package/src/commands/reflect.ts +51 -0
- package/src/commands/save-args.ts +74 -0
- package/src/commands/save.ts +84 -0
- package/src/commands/validate-args.ts +76 -0
- package/src/commands/validate-driver.ts +84 -0
- package/src/commands/validate-report.ts +95 -0
- package/src/commands/validate.ts +58 -0
- package/src/commands/watch-args.ts +74 -0
- package/src/commands/watch.ts +185 -0
- package/src/index.ts +16 -0
- package/src/log-level.ts +49 -0
- package/src/package-name.ts +39 -0
- package/src/spawn-data-server.ts +247 -0
- package/src/testing/echo-server.ts +93 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Spawned child of the `lint-grammar` subcommand. Launched by the parent with
|
|
12
|
+
* `node <this> --services <module> [--name-property <p>]... [--json] [--strict]`.
|
|
13
|
+
* It dynamic-imports the head's services module, runs the framework's
|
|
14
|
+
* `lintGrammar`, prints the report (human or `--json`) and exits with the gate
|
|
15
|
+
* code (non-zero on errors; `--strict` also fails on warnings). Never imported —
|
|
16
|
+
* only spawned.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { loadHeadlessContext } from './headless-harness.js';
|
|
20
|
+
import { formatLintReport, lintExitCode } from './lint-grammar-report.js';
|
|
21
|
+
|
|
22
|
+
interface DriverArgs {
|
|
23
|
+
servicesModule?: string;
|
|
24
|
+
nameProperties: string[];
|
|
25
|
+
json: boolean;
|
|
26
|
+
strict: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv: string[]): DriverArgs {
|
|
30
|
+
const parsed: DriverArgs = { nameProperties: [], json: false, strict: false };
|
|
31
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
32
|
+
const flag = argv[index];
|
|
33
|
+
const next = (): string => {
|
|
34
|
+
const value = argv[index + 1];
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
console.error(`Missing value for ${flag}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
index += 1;
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
42
|
+
switch (flag) {
|
|
43
|
+
case '--services':
|
|
44
|
+
parsed.servicesModule = next();
|
|
45
|
+
break;
|
|
46
|
+
case '--name-property':
|
|
47
|
+
parsed.nameProperties.push(next());
|
|
48
|
+
break;
|
|
49
|
+
case '--json':
|
|
50
|
+
parsed.json = true;
|
|
51
|
+
break;
|
|
52
|
+
case '--strict':
|
|
53
|
+
parsed.strict = true;
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
console.error(`Unknown option: ${flag}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function main(argv: string[]): Promise<number> {
|
|
64
|
+
const args = parseArgs(argv);
|
|
65
|
+
if (!args.servicesModule) {
|
|
66
|
+
console.error('lint-grammar-driver: --services <module> is required.');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const { createServices, coreNode } = await loadHeadlessContext(args.servicesModule);
|
|
70
|
+
const result = coreNode.lintGrammar({
|
|
71
|
+
createServices,
|
|
72
|
+
nameProperties: args.nameProperties.length ? args.nameProperties : undefined
|
|
73
|
+
});
|
|
74
|
+
console.log(formatLintReport(result, { json: args.json }));
|
|
75
|
+
return lintExitCode(result, args.strict);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
main(process.argv.slice(2))
|
|
79
|
+
.then(code => process.exit(code))
|
|
80
|
+
.catch((error: unknown) => {
|
|
81
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
// Type-only import: the CLI is head-neutral and carries no runtime `@hydranium/core`
|
|
11
|
+
// dependency (the head's copy is resolved at run time via `loadHeadlessContext`).
|
|
12
|
+
import type { GrammarLintResult } from '@hydranium/core/node';
|
|
13
|
+
|
|
14
|
+
/** `s` when `count !== 1`, else empty. */
|
|
15
|
+
function plural(count: number): string {
|
|
16
|
+
return count === 1 ? '' : 's';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The exit code for a lint run: non-zero blocks the CI gate. Errors always fail;
|
|
21
|
+
* `strict` additionally fails on warnings.
|
|
22
|
+
*/
|
|
23
|
+
export function lintExitCode(result: GrammarLintResult, strict = false): number {
|
|
24
|
+
if (result.counts.error > 0) {
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
if (strict && result.counts.warning > 0) {
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One-line severity tally, or the clean-grammar message. */
|
|
34
|
+
function summaryLine(result: GrammarLintResult): string {
|
|
35
|
+
const { error, warning } = result.counts;
|
|
36
|
+
if (error + warning === 0) {
|
|
37
|
+
// "target type", not "target": the tally counts distinct TYPES reachable
|
|
38
|
+
// as a cross-reference target, not cross-reference properties. Several
|
|
39
|
+
// references can share one target type, so the bare word reads as though
|
|
40
|
+
// the lint had missed the rest.
|
|
41
|
+
return `No grammar-convention problems found (${result.checkedReferenceTargets} reference target type${plural(
|
|
42
|
+
result.checkedReferenceTargets
|
|
43
|
+
)} checked).`;
|
|
44
|
+
}
|
|
45
|
+
const parts: string[] = [];
|
|
46
|
+
if (error) {
|
|
47
|
+
parts.push(`${error} error${plural(error)}`);
|
|
48
|
+
}
|
|
49
|
+
if (warning) {
|
|
50
|
+
parts.push(`${warning} warning${plural(warning)}`);
|
|
51
|
+
}
|
|
52
|
+
return `${parts.join(', ')}.`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Render a grammar-convention lint result for the terminal. `json` emits the raw
|
|
57
|
+
* {@link GrammarLintResult} (the machine-readable CI contract); the default is a
|
|
58
|
+
* human report — one `severity [rule] message` line per finding — closing with
|
|
59
|
+
* the {@link summaryLine} tally.
|
|
60
|
+
*/
|
|
61
|
+
export function formatLintReport(result: GrammarLintResult, options: { json?: boolean } = {}): string {
|
|
62
|
+
if (options.json) {
|
|
63
|
+
return JSON.stringify(result, undefined, 2);
|
|
64
|
+
}
|
|
65
|
+
const lines: string[] = [];
|
|
66
|
+
for (const finding of result.findings) {
|
|
67
|
+
lines.push(` ${finding.severity.padEnd(7)} [${finding.rule}] ${finding.message}`);
|
|
68
|
+
}
|
|
69
|
+
if (result.findings.length > 0) {
|
|
70
|
+
lines.push('');
|
|
71
|
+
}
|
|
72
|
+
lines.push(summaryLine(result));
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { runDriverChild, type DriverSpawnOptions } from './headless-harness.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Absolute path to the spawned driver. From `lib/commands/lint-grammar.js` the
|
|
15
|
+
* compiled driver sits beside it at `lib/commands/lint-grammar-driver.js`.
|
|
16
|
+
*/
|
|
17
|
+
const DRIVER = fileURLToPath(new URL('./lint-grammar-driver.js', import.meta.url));
|
|
18
|
+
|
|
19
|
+
/** Options for the {@link runLintGrammar} subcommand. */
|
|
20
|
+
export interface LintGrammarCommandOptions extends DriverSpawnOptions {
|
|
21
|
+
/** ESM module exporting a zero-arg `createServices(): { shared }` thunk. */
|
|
22
|
+
readonly servicesModule: string;
|
|
23
|
+
/** Property names that satisfy the nameability convention. Empty → the framework default `name`. */
|
|
24
|
+
readonly nameProperties?: readonly string[];
|
|
25
|
+
/** Emit the raw JSON result instead of the human report. */
|
|
26
|
+
readonly json?: boolean;
|
|
27
|
+
/** Also fail (non-zero exit) on warnings, not only errors. */
|
|
28
|
+
readonly strict?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Build the argv the driver child parses. Pure → unit-testable. */
|
|
32
|
+
export function buildLintGrammarDriverArgs(options: LintGrammarCommandOptions): string[] {
|
|
33
|
+
const args = ['--services', options.servicesModule];
|
|
34
|
+
for (const property of options.nameProperties ?? []) {
|
|
35
|
+
args.push('--name-property', property);
|
|
36
|
+
}
|
|
37
|
+
if (options.json) {
|
|
38
|
+
args.push('--json');
|
|
39
|
+
}
|
|
40
|
+
if (options.strict) {
|
|
41
|
+
args.push('--strict');
|
|
42
|
+
}
|
|
43
|
+
return args;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Headless grammar-convention lint — a CI gate that checks a head's grammar against
|
|
48
|
+
* the framework's expectations (nameable cross-reference targets, an entry rule) and
|
|
49
|
+
* exits non-zero when a violation is found (`--strict` also fails on warnings).
|
|
50
|
+
* Read-only: no workspace is built (the reflection is static once the head registers
|
|
51
|
+
* its language), so this subcommand takes only `--services` and the name-property
|
|
52
|
+
* overrides. The work runs in a spawned child that dynamic-imports the head's
|
|
53
|
+
* `createServices` module, keeping the binary language-agnostic.
|
|
54
|
+
*/
|
|
55
|
+
export function runLintGrammar(options: LintGrammarCommandOptions): Promise<void> {
|
|
56
|
+
return runDriverChild(['--max-old-space-size=8192', DRIVER, ...buildLintGrammarDriverArgs(options)], options);
|
|
57
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
exitWithUsage,
|
|
12
|
+
helpRequested,
|
|
13
|
+
LOG_LEVEL_FLAG,
|
|
14
|
+
logLevelHelpLine,
|
|
15
|
+
logLevelOption,
|
|
16
|
+
numericOption,
|
|
17
|
+
parseHarnessArgs,
|
|
18
|
+
printHelp,
|
|
19
|
+
SERVICES_FLAG,
|
|
20
|
+
type UsageError
|
|
21
|
+
} from './harness-args.js';
|
|
22
|
+
import { parseProfileDimensions, runMeasureMemory, type MeasureMemoryCommandOptions } from './measure-memory.js';
|
|
23
|
+
|
|
24
|
+
const VALUE_FLAGS = [
|
|
25
|
+
'--edits',
|
|
26
|
+
'--edit-docs',
|
|
27
|
+
'--churn-suffix',
|
|
28
|
+
'--settle',
|
|
29
|
+
'--snapshot-path',
|
|
30
|
+
'--profile',
|
|
31
|
+
'--session-out',
|
|
32
|
+
LOG_LEVEL_FLAG
|
|
33
|
+
] as const;
|
|
34
|
+
const BOOL_FLAGS = ['--snapshot', '--json'] as const;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Every flag `measure-memory` accepts, derived from the sets the parser is
|
|
38
|
+
* handed so the list cannot claim a flag the parser would reject. `--services`
|
|
39
|
+
* is consumed by the shared harness parser itself, so it is in neither set.
|
|
40
|
+
*/
|
|
41
|
+
export const MEASURE_MEMORY_FLAGS: readonly string[] = [SERVICES_FLAG, ...VALUE_FLAGS, ...BOOL_FLAGS];
|
|
42
|
+
|
|
43
|
+
/** The subset that takes a value, so `--help` in a value position reads as data. */
|
|
44
|
+
export const MEASURE_MEMORY_VALUE_FLAGS: readonly string[] = [SERVICES_FLAG, ...VALUE_FLAGS];
|
|
45
|
+
|
|
46
|
+
/** The `--help` text, as data, held to {@link MEASURE_MEMORY_FLAGS} by a test. */
|
|
47
|
+
export const MEASURE_MEMORY_HELP: readonly string[] = [
|
|
48
|
+
'Usage: hydranium-cli measure-memory --services <module> <workspace> [options]',
|
|
49
|
+
'',
|
|
50
|
+
'Measure model-store memory for a workspace. `<module>` is an ESM module that',
|
|
51
|
+
'exports a zero-arg `createServices(): { shared }` thunk (the head wires its own',
|
|
52
|
+
'filesystem inside). Runs in a `--expose-gc` child for post-GC readings; the',
|
|
53
|
+
'baseline / after-build / churn lines print on stdout. Needs @hydranium/core.',
|
|
54
|
+
'',
|
|
55
|
+
'Options:',
|
|
56
|
+
' --services <module> ESM module exporting `createServices(): { shared }` (required).',
|
|
57
|
+
' <workspace> Workspace root (path or file URI) to build (required).',
|
|
58
|
+
' --edits <N> Rebuild-churn cycles to probe for retention. Default: 0 (skip).',
|
|
59
|
+
' --edit-docs <N> Documents to churn per cycle. Default: 25.',
|
|
60
|
+
' --churn-suffix <ext> Only churn documents whose URI ends with this suffix.',
|
|
61
|
+
' --settle <ms> Wait <ms> after the build before measuring, so a',
|
|
62
|
+
' timer-driven residency policy (shed-closed-when-idle)',
|
|
63
|
+
" sheds first. Pass above the policy's idleMs; omit to",
|
|
64
|
+
' measure the pre-shed (always-keep) baseline.',
|
|
65
|
+
' --snapshot Write a `.heapsnapshot` after the build.',
|
|
66
|
+
' --snapshot-path <p> Snapshot path. Default: <cwd>/<workspace-basename>.heapsnapshot.',
|
|
67
|
+
' --profile <dims> Capture sampled profiles around the build (+churn) into a',
|
|
68
|
+
' profiling session. Comma-separated: cpu,alloc,gc,eld,heap.',
|
|
69
|
+
' --session-out <dir> Parent directory for the profiling session folder (with --profile).',
|
|
70
|
+
' --json Emit the measurement as one JSON document instead of the',
|
|
71
|
+
' progress lines: documentCount, buildMs, emptyHeapBytes,',
|
|
72
|
+
' afterBuildHeapBytes, and churnGrowthBytes / snapshotPath /',
|
|
73
|
+
' profilingSession where the run produced them.',
|
|
74
|
+
logLevelHelpLine(22)
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate `--profile` in the PARENT, where the numeric flags beside it are
|
|
79
|
+
* already validated.
|
|
80
|
+
*
|
|
81
|
+
* Returns the RAW string rather than the parsed dimensions on purpose: the child
|
|
82
|
+
* re-parses them, because that is where they are used, and this exists only so a
|
|
83
|
+
* typo fails as a usage error before a process is spawned and a whole head
|
|
84
|
+
* imported. Parsing here and passing the result would change what crosses to the
|
|
85
|
+
* child for no gain.
|
|
86
|
+
*/
|
|
87
|
+
function validatedProfile(csv: string | undefined, onError: UsageError): string | undefined {
|
|
88
|
+
if (csv === undefined) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
parseProfileDimensions(csv);
|
|
93
|
+
} catch (err: unknown) {
|
|
94
|
+
onError(`Option --profile: ${err instanceof Error ? err.message : String(err)}`);
|
|
95
|
+
}
|
|
96
|
+
return csv;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function parseMeasureMemoryArgs(args: string[], onError: UsageError = exitWithUsage): MeasureMemoryCommandOptions {
|
|
100
|
+
const { servicesModule, workspace, options } = parseHarnessArgs(args, 'measure-memory', VALUE_FLAGS, BOOL_FLAGS, { onError });
|
|
101
|
+
return {
|
|
102
|
+
servicesModule,
|
|
103
|
+
workspace,
|
|
104
|
+
editCycles: numericOption(options['--edits'], '--edits', onError),
|
|
105
|
+
editDocs: numericOption(options['--edit-docs'], '--edit-docs', onError),
|
|
106
|
+
churnSuffix: options['--churn-suffix'],
|
|
107
|
+
settleMs: numericOption(options['--settle'], '--settle', onError),
|
|
108
|
+
writeSnapshot: options['--snapshot'] === 'true',
|
|
109
|
+
snapshotPath: options['--snapshot-path'],
|
|
110
|
+
profile: validatedProfile(options['--profile'], onError),
|
|
111
|
+
sessionOut: options['--session-out'],
|
|
112
|
+
json: options['--json'] === 'true',
|
|
113
|
+
logLevel: logLevelOption(options[LOG_LEVEL_FLAG])
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function runMeasureMemoryCommand(args: string[]): Promise<void> {
|
|
118
|
+
if (helpRequested(args, MEASURE_MEMORY_VALUE_FLAGS)) {
|
|
119
|
+
printHelp(MEASURE_MEMORY_HELP);
|
|
120
|
+
return Promise.resolve();
|
|
121
|
+
}
|
|
122
|
+
return runMeasureMemory(parseMeasureMemoryArgs(args));
|
|
123
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Spawned child of the `measure-memory` subcommand. Launched by the parent with
|
|
12
|
+
* `node --expose-gc --max-old-space-size=8192 <this> --services <module> <workspace> ...`
|
|
13
|
+
* so the underlying harness gets post-GC readings. It dynamic-imports the head's
|
|
14
|
+
* services module and runs the framework's `measureModelMemory`, streaming the
|
|
15
|
+
* baseline / after-build / churn lines to stdout. Never imported — only spawned.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { loadHeadlessContext } from './headless-harness.js';
|
|
19
|
+
import { parseProfileDimensions } from './measure-memory.js';
|
|
20
|
+
|
|
21
|
+
interface DriverArgs {
|
|
22
|
+
servicesModule?: string;
|
|
23
|
+
workspace?: string;
|
|
24
|
+
editCycles?: number;
|
|
25
|
+
editDocs?: number;
|
|
26
|
+
churnDocSuffix?: string;
|
|
27
|
+
settleMs?: number;
|
|
28
|
+
writeSnapshot: boolean;
|
|
29
|
+
snapshotPath?: string;
|
|
30
|
+
profile?: string;
|
|
31
|
+
sessionOut?: string;
|
|
32
|
+
json: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseArgs(argv: string[]): DriverArgs {
|
|
36
|
+
const parsed: DriverArgs = { writeSnapshot: false, json: false };
|
|
37
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
38
|
+
const flag = argv[index];
|
|
39
|
+
const next = (): string => {
|
|
40
|
+
const value = argv[index + 1];
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
console.error(`Missing value for ${flag}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
index += 1;
|
|
46
|
+
return value;
|
|
47
|
+
};
|
|
48
|
+
switch (flag) {
|
|
49
|
+
case '--services':
|
|
50
|
+
parsed.servicesModule = next();
|
|
51
|
+
break;
|
|
52
|
+
case '--edits':
|
|
53
|
+
parsed.editCycles = Number(next());
|
|
54
|
+
break;
|
|
55
|
+
case '--edit-docs':
|
|
56
|
+
parsed.editDocs = Number(next());
|
|
57
|
+
break;
|
|
58
|
+
case '--churn-suffix':
|
|
59
|
+
parsed.churnDocSuffix = next();
|
|
60
|
+
break;
|
|
61
|
+
case '--settle':
|
|
62
|
+
parsed.settleMs = Number(next());
|
|
63
|
+
break;
|
|
64
|
+
case '--snapshot':
|
|
65
|
+
parsed.writeSnapshot = true;
|
|
66
|
+
break;
|
|
67
|
+
case '--snapshot-path':
|
|
68
|
+
parsed.snapshotPath = next();
|
|
69
|
+
break;
|
|
70
|
+
case '--profile':
|
|
71
|
+
parsed.profile = next();
|
|
72
|
+
break;
|
|
73
|
+
case '--session-out':
|
|
74
|
+
parsed.sessionOut = next();
|
|
75
|
+
break;
|
|
76
|
+
case '--json':
|
|
77
|
+
parsed.json = true;
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (flag.startsWith('--')) {
|
|
81
|
+
console.error(`Unknown option: ${flag}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
parsed.workspace = flag;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function main(argv: string[]): Promise<void> {
|
|
91
|
+
const args = parseArgs(argv);
|
|
92
|
+
if (!args.servicesModule || !args.workspace) {
|
|
93
|
+
console.error('measure-memory-driver: --services <module> and a <workspace> are required.');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
const { createServices, coreNode } = await loadHeadlessContext(args.servicesModule);
|
|
97
|
+
const result = await coreNode.measureModelMemory({
|
|
98
|
+
createServices,
|
|
99
|
+
workspace: args.workspace,
|
|
100
|
+
editCycles: args.editCycles,
|
|
101
|
+
editDocs: args.editDocs,
|
|
102
|
+
churnDocSuffix: args.churnDocSuffix,
|
|
103
|
+
settleMs: args.settleMs,
|
|
104
|
+
writeSnapshot: args.writeSnapshot,
|
|
105
|
+
snapshotPath: args.snapshotPath,
|
|
106
|
+
profile: args.profile ? parseProfileDimensions(args.profile) : undefined,
|
|
107
|
+
sessionOut: args.sessionOut,
|
|
108
|
+
// Omitted under `--json` rather than redirected: every progress line would
|
|
109
|
+
// otherwise land on the same stream as the document, so the caller's
|
|
110
|
+
// `JSON.parse` would see a preamble it has no way to bound.
|
|
111
|
+
log: args.json ? undefined : line => console.log(line)
|
|
112
|
+
});
|
|
113
|
+
if (args.json) {
|
|
114
|
+
console.log(JSON.stringify(result, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(`SUMMARY: docs=${result.documentCount} build=${Math.round(result.buildMs)}ms`);
|
|
118
|
+
if (result.profilingSession) {
|
|
119
|
+
console.log(`SESSION: ${result.profilingSession}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main(process.argv.slice(2))
|
|
124
|
+
.then(() => process.exit(0))
|
|
125
|
+
.catch((error: unknown) => {
|
|
126
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import type { ProfileCaptureOptions } from '@hydranium/core/node';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { runDriverChild, type DriverSpawnOptions } from './headless-harness.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Absolute path to the spawned driver. From `lib/commands/measure-memory.js`
|
|
16
|
+
* the compiled driver sits beside it at `lib/commands/measure-memory-driver.js`.
|
|
17
|
+
*/
|
|
18
|
+
const DRIVER = fileURLToPath(new URL('./measure-memory-driver.js', import.meta.url));
|
|
19
|
+
|
|
20
|
+
/** Options for the {@link runMeasureMemory} subcommand. */
|
|
21
|
+
export interface MeasureMemoryCommandOptions extends DriverSpawnOptions {
|
|
22
|
+
/** ESM module exporting a zero-arg `createServices(): { shared }` thunk. */
|
|
23
|
+
readonly servicesModule: string;
|
|
24
|
+
/** Workspace root (filesystem path or file URI) to build. */
|
|
25
|
+
readonly workspace: string;
|
|
26
|
+
/** Rebuild-churn cycles to probe for retention (default 0 = skip). */
|
|
27
|
+
readonly editCycles?: number;
|
|
28
|
+
/** Documents to churn per cycle. */
|
|
29
|
+
readonly editDocs?: number;
|
|
30
|
+
/** Only churn documents whose URI path ends with this suffix. */
|
|
31
|
+
readonly churnSuffix?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Wait this many milliseconds after the build before measuring, so a
|
|
34
|
+
* timer-driven residency policy (`CstResidencyService`'s
|
|
35
|
+
* `shed-closed-when-idle`) sheds before the reading is taken. Pass a value
|
|
36
|
+
* comfortably above the policy's `idleMs`; omit (or `0`) to measure the
|
|
37
|
+
* pre-shed heap (the always-keep-equivalent baseline).
|
|
38
|
+
*/
|
|
39
|
+
readonly settleMs?: number;
|
|
40
|
+
/** Write a `.heapsnapshot` after the build. */
|
|
41
|
+
readonly writeSnapshot?: boolean;
|
|
42
|
+
/** Snapshot path (default `<cwd>/<workspace-basename>.heapsnapshot`). */
|
|
43
|
+
readonly snapshotPath?: string;
|
|
44
|
+
/** Comma-separated profile dimensions to capture; see {@link parseProfileDimensions} for the tokens. */
|
|
45
|
+
readonly profile?: string;
|
|
46
|
+
/** Parent directory for the profiling session folder. Only used with {@link profile}. */
|
|
47
|
+
readonly sessionOut?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Emit the `MeasureModelMemoryResult` as JSON instead of the progress lines.
|
|
50
|
+
*
|
|
51
|
+
* The progress lines are suppressed rather than kept alongside it, because the
|
|
52
|
+
* flag exists so a script can read stdout as one document — a preamble on the
|
|
53
|
+
* same stream would leave every caller stripping it by hand.
|
|
54
|
+
*/
|
|
55
|
+
readonly json?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse a `--profile` dimension list (`cpu,alloc,gc,eld,heap`) into
|
|
60
|
+
* {@link ProfileCaptureOptions}. Case- and whitespace-tolerant; throws on an
|
|
61
|
+
* unknown token so a typo fails loud rather than silently capturing nothing.
|
|
62
|
+
*/
|
|
63
|
+
export function parseProfileDimensions(csv: string): ProfileCaptureOptions {
|
|
64
|
+
const options: ProfileCaptureOptions = {};
|
|
65
|
+
for (const raw of csv.split(',')) {
|
|
66
|
+
const token = raw.trim().toLowerCase();
|
|
67
|
+
if (!token) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
switch (token) {
|
|
71
|
+
case 'cpu':
|
|
72
|
+
options.cpu = true;
|
|
73
|
+
break;
|
|
74
|
+
case 'alloc':
|
|
75
|
+
case 'allocation':
|
|
76
|
+
options.allocation = true;
|
|
77
|
+
break;
|
|
78
|
+
case 'gc':
|
|
79
|
+
options.gc = true;
|
|
80
|
+
break;
|
|
81
|
+
case 'eld':
|
|
82
|
+
case 'eventloop':
|
|
83
|
+
case 'event-loop':
|
|
84
|
+
options.eventLoopDelay = true;
|
|
85
|
+
break;
|
|
86
|
+
case 'heap':
|
|
87
|
+
case 'heapsnapshot':
|
|
88
|
+
options.heapSnapshot = true;
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
throw new Error(`Unknown profile dimension: ${token} (expected cpu, alloc, gc, eld, heap)`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return options;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build the argv the driver child parses. Pure → unit-testable; mirrors the
|
|
99
|
+
* driver's own option names.
|
|
100
|
+
*/
|
|
101
|
+
export function buildDriverArgs(options: MeasureMemoryCommandOptions): string[] {
|
|
102
|
+
const args = ['--services', options.servicesModule, options.workspace];
|
|
103
|
+
if (options.editCycles !== undefined) {
|
|
104
|
+
args.push('--edits', String(options.editCycles));
|
|
105
|
+
}
|
|
106
|
+
if (options.editDocs !== undefined) {
|
|
107
|
+
args.push('--edit-docs', String(options.editDocs));
|
|
108
|
+
}
|
|
109
|
+
if (options.churnSuffix !== undefined) {
|
|
110
|
+
args.push('--churn-suffix', options.churnSuffix);
|
|
111
|
+
}
|
|
112
|
+
if (options.settleMs !== undefined) {
|
|
113
|
+
args.push('--settle', String(options.settleMs));
|
|
114
|
+
}
|
|
115
|
+
if (options.writeSnapshot) {
|
|
116
|
+
args.push('--snapshot');
|
|
117
|
+
}
|
|
118
|
+
if (options.snapshotPath !== undefined) {
|
|
119
|
+
args.push('--snapshot-path', options.snapshotPath);
|
|
120
|
+
}
|
|
121
|
+
if (options.profile !== undefined) {
|
|
122
|
+
args.push('--profile', options.profile);
|
|
123
|
+
}
|
|
124
|
+
if (options.sessionOut !== undefined) {
|
|
125
|
+
args.push('--session-out', options.sessionOut);
|
|
126
|
+
}
|
|
127
|
+
if (options.json) {
|
|
128
|
+
args.push('--json');
|
|
129
|
+
}
|
|
130
|
+
return args;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Headless model-store memory measurement. `hydranium-cli` is language-agnostic
|
|
135
|
+
* and cannot import a head's `create<Lang>Services`, so the head passes a module
|
|
136
|
+
* that exports a zero-arg `createServices` thunk (`--services`). Because the
|
|
137
|
+
* underlying `measureModelMemory` harness needs post-GC readings, the work runs
|
|
138
|
+
* in a spawned child launched with `--expose-gc`; the driver dynamic-imports the
|
|
139
|
+
* module and reports the baseline / after-build / churn lines on stdout.
|
|
140
|
+
*/
|
|
141
|
+
export function runMeasureMemory(options: MeasureMemoryCommandOptions): Promise<void> {
|
|
142
|
+
return runDriverChild(['--expose-gc', '--max-old-space-size=8192', DRIVER, ...buildDriverArgs(options)], options);
|
|
143
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
exitWithUsage,
|
|
12
|
+
helpRequested,
|
|
13
|
+
LOG_LEVEL_FLAG,
|
|
14
|
+
logLevelHelpLine,
|
|
15
|
+
logLevelOption,
|
|
16
|
+
parseHarnessArgs,
|
|
17
|
+
printHelp,
|
|
18
|
+
SERVICES_FLAG,
|
|
19
|
+
type UsageError
|
|
20
|
+
} from './harness-args.js';
|
|
21
|
+
import { OUT_FILE_FLAG } from './headless-harness.js';
|
|
22
|
+
import { runModelDocs, type ModelDocsCommandOptions } from './model-docs.js';
|
|
23
|
+
|
|
24
|
+
const VALUE_FLAGS = [OUT_FILE_FLAG, LOG_LEVEL_FLAG] as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Every flag `model-docs` accepts, derived from the sets the parser is handed so
|
|
28
|
+
* the list cannot claim a flag the parser would reject. `--services` is consumed
|
|
29
|
+
* by the shared harness parser itself, so it is in neither set.
|
|
30
|
+
*/
|
|
31
|
+
export const MODEL_DOCS_FLAGS: readonly string[] = [SERVICES_FLAG, ...VALUE_FLAGS];
|
|
32
|
+
|
|
33
|
+
/** The subset that takes a value, so `--help` in a value position reads as data. */
|
|
34
|
+
export const MODEL_DOCS_VALUE_FLAGS: readonly string[] = MODEL_DOCS_FLAGS;
|
|
35
|
+
|
|
36
|
+
/** The `--help` text, as data, held to {@link MODEL_DOCS_FLAGS} by a test. */
|
|
37
|
+
export const MODEL_DOCS_HELP: readonly string[] = [
|
|
38
|
+
'Usage: hydranium-cli model-docs --services <module> [--out-file <file>] [--log-level <lvl>]',
|
|
39
|
+
'',
|
|
40
|
+
'Generate a navigable Markdown reference of every AST node type — a type index,',
|
|
41
|
+
'cross-linked super/sub types, a reverse "referenced by" index, and a per-language',
|
|
42
|
+
'grammar summary — for publishing into adopter docs. Read-only: no workspace is',
|
|
43
|
+
'built. Emits Markdown on stdout, or into `--out-file`; the machine-readable form',
|
|
44
|
+
'is `hydranium-cli reflect --json`. `<module>` is an ESM module exporting a',
|
|
45
|
+
'zero-arg `createServices(): { shared }` thunk. Needs @hydranium/core.',
|
|
46
|
+
'',
|
|
47
|
+
'Options:',
|
|
48
|
+
' --services <module> ESM module exporting `createServices(): { shared }` (required).',
|
|
49
|
+
' --out-file <file> Write the Markdown to this file instead of stdout. Written',
|
|
50
|
+
' only once the report exists, unlike a shell redirection,',
|
|
51
|
+
' which truncates the file before the head is even booted.',
|
|
52
|
+
logLevelHelpLine(22)
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export function parseModelDocsArgs(args: string[], onError: UsageError = exitWithUsage): ModelDocsCommandOptions {
|
|
56
|
+
const { servicesModule, options } = parseHarnessArgs(args, 'model-docs', VALUE_FLAGS, [], {
|
|
57
|
+
requireWorkspace: false,
|
|
58
|
+
onError
|
|
59
|
+
});
|
|
60
|
+
return { servicesModule, outFile: options[OUT_FILE_FLAG], logLevel: logLevelOption(options[LOG_LEVEL_FLAG]) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function runModelDocsCommand(args: string[]): Promise<void> {
|
|
64
|
+
if (helpRequested(args, MODEL_DOCS_VALUE_FLAGS)) {
|
|
65
|
+
printHelp(MODEL_DOCS_HELP);
|
|
66
|
+
return Promise.resolve();
|
|
67
|
+
}
|
|
68
|
+
return runModelDocs(parseModelDocsArgs(args));
|
|
69
|
+
}
|