@ixo/domain.md 0.3.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 +201 -0
- package/NOTICE +6 -0
- package/README.md +30 -0
- package/assets/domain-md.schema.json +1103 -0
- package/assets/oracle-capsule-jcs-vectors.json +57 -0
- package/assets/oracle-capsule-manifest-contract.md +72 -0
- package/assets/oracle-capsule-source-lock.schema.json +45 -0
- package/assets/oracle-capsule.schema.json +265 -0
- package/assets/rules.json +275 -0
- package/assets/spec.md +1434 -0
- package/assets/template-manifest.schema.json +57 -0
- package/dist/capsule/index.d.ts +75 -0
- package/dist/capsule/index.js +20 -0
- package/dist/capsule/index.js.map +1 -0
- package/dist/chunk-BYBC2TSS.js +597 -0
- package/dist/chunk-BYBC2TSS.js.map +1 -0
- package/dist/chunk-G7PZJYAJ.js +31826 -0
- package/dist/chunk-G7PZJYAJ.js.map +1 -0
- package/dist/chunk-R6QZRNEX.js +164 -0
- package/dist/chunk-R6QZRNEX.js.map +1 -0
- package/dist/chunk-T3STBWXP.js +46 -0
- package/dist/chunk-T3STBWXP.js.map +1 -0
- package/dist/chunk-W7MRQP7I.js +862 -0
- package/dist/chunk-W7MRQP7I.js.map +1 -0
- package/dist/chunk-XDIWMZVF.js +1150 -0
- package/dist/chunk-XDIWMZVF.js.map +1 -0
- package/dist/cli-KcUIQEiE.d.ts +171 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +400 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/resolver-B3mwnjJq.d.ts +96 -0
- package/dist/spec.d.ts +20 -0
- package/dist/spec.js +23 -0
- package/dist/spec.js.map +1 -0
- package/dist/templates/index.d.ts +7 -0
- package/dist/templates/index.js +24 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/workers-Dr8B8a2O.d.ts +19 -0
- package/dist/workers.d.ts +3 -0
- package/dist/workers.js +31 -0
- package/dist/workers.js.map +1 -0
- package/package.json +97 -0
- package/src/capsule/canonical.ts +74 -0
- package/src/capsule/index.ts +10 -0
- package/src/capsule/json.ts +373 -0
- package/src/capsule/types.ts +63 -0
- package/src/capsule/validate.ts +551 -0
- package/src/cli.ts +405 -0
- package/src/compiled-validators.d.ts +7 -0
- package/src/compiled-validators.js +32512 -0
- package/src/constants.ts +250 -0
- package/src/diff.ts +111 -0
- package/src/export.ts +28 -0
- package/src/index.ts +36 -0
- package/src/lint.ts +42 -0
- package/src/parser.ts +174 -0
- package/src/sarif.ts +49 -0
- package/src/semantic.ts +1156 -0
- package/src/spec.ts +48 -0
- package/src/templates/errors.ts +3 -0
- package/src/templates/index.ts +11 -0
- package/src/templates/manifest.ts +94 -0
- package/src/templates/render.ts +435 -0
- package/src/templates/resolver.ts +200 -0
- package/src/templates/types.ts +98 -0
- package/src/types.ts +195 -0
- package/src/workers.ts +6 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import { defineCommand, runCommand, showUsage } from 'citty';
|
|
7
|
+
import { isAlias, parseDocument, visit as visitYaml } from 'yaml';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
MAX_DOMAIN_BYTES,
|
|
11
|
+
MAX_LINKED_BYTES,
|
|
12
|
+
MAX_SCALAR_LENGTH,
|
|
13
|
+
MAX_YAML_DEPTH,
|
|
14
|
+
MAX_YAML_NODES,
|
|
15
|
+
PACKAGE_VERSION,
|
|
16
|
+
} from './constants.js';
|
|
17
|
+
import { diffDomains } from './diff.js';
|
|
18
|
+
import { exportDomain } from './export.js';
|
|
19
|
+
import { lint } from './lint.js';
|
|
20
|
+
import { toSarif } from './sarif.js';
|
|
21
|
+
import { getRules, getSchemaSource, getSpecification } from './spec.js';
|
|
22
|
+
import { TemplateValidationError } from './templates/errors.js';
|
|
23
|
+
import { renderTemplateBundle } from './templates/render.js';
|
|
24
|
+
import type { TemplateDerivedType } from './templates/types.js';
|
|
25
|
+
import type { ConformanceProfile, Finding, LintReport } from './types.js';
|
|
26
|
+
|
|
27
|
+
class InputError extends Error {}
|
|
28
|
+
class ConformanceError extends Error {}
|
|
29
|
+
|
|
30
|
+
function stdinBytes(maxBytes: number): Promise<Buffer> {
|
|
31
|
+
return new Promise((resolvePromise, reject) => {
|
|
32
|
+
const chunks: Buffer[] = [];
|
|
33
|
+
let size = 0;
|
|
34
|
+
process.stdin.on('data', (chunk: Buffer | string) => {
|
|
35
|
+
const bytes = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
36
|
+
size += bytes.length;
|
|
37
|
+
if (size > maxBytes) {
|
|
38
|
+
process.stdin.pause();
|
|
39
|
+
reject(new InputError(`Standard input exceeds ${maxBytes} bytes.`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
chunks.push(bytes);
|
|
43
|
+
});
|
|
44
|
+
process.stdin.once('end', () => resolvePromise(Buffer.concat(chunks)));
|
|
45
|
+
process.stdin.once('error', reject);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function readInput(input: string): Promise<{ content: string; sourceName: string }> {
|
|
50
|
+
let bytes: Uint8Array;
|
|
51
|
+
let sourceName = input;
|
|
52
|
+
if (input === '-') bytes = await stdinBytes(MAX_DOMAIN_BYTES);
|
|
53
|
+
else {
|
|
54
|
+
let path = resolve(input);
|
|
55
|
+
let stat = await lstat(path).catch(() => undefined);
|
|
56
|
+
if (!stat) throw new InputError(`Input does not exist: ${input}`);
|
|
57
|
+
if (stat.isSymbolicLink()) throw new InputError(`Symlink inputs are forbidden: ${input}`);
|
|
58
|
+
if (stat.isDirectory()) {
|
|
59
|
+
path = join(path, 'domain.md');
|
|
60
|
+
stat = await lstat(path).catch(() => undefined);
|
|
61
|
+
}
|
|
62
|
+
if (!stat?.isFile() || stat.isSymbolicLink())
|
|
63
|
+
throw new InputError(`Expected a regular domain.md file: ${input}`);
|
|
64
|
+
if (stat.size > MAX_DOMAIN_BYTES)
|
|
65
|
+
throw new InputError(`Input exceeds ${MAX_DOMAIN_BYTES} bytes: ${input}`);
|
|
66
|
+
bytes = await readFile(path);
|
|
67
|
+
sourceName = path;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return { content: new TextDecoder('utf-8', { fatal: true }).decode(bytes), sourceName };
|
|
71
|
+
} catch {
|
|
72
|
+
throw new InputError(`Input is not valid UTF-8: ${sourceName}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function textReport(report: LintReport): string {
|
|
77
|
+
const lines = report.findings.map((item) => {
|
|
78
|
+
const location = item.path ? ` ${item.path}` : '';
|
|
79
|
+
return `${item.severity.toUpperCase()} ${item.code}${location}: ${item.message}`;
|
|
80
|
+
});
|
|
81
|
+
lines.push(
|
|
82
|
+
`${report.ok ? 'STATIC PASS' : 'FAIL'}: ${report.summary.errors} errors, ${report.summary.warnings} warnings, ${report.summary.infos} info`,
|
|
83
|
+
);
|
|
84
|
+
if (report.externalChecksRequired.length > 0) {
|
|
85
|
+
lines.push(
|
|
86
|
+
`External checks required: ${report.externalChecksRequired.map((check) => check.code).join(', ')}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return `${lines.join('\n')}\n`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function outputLint(report: LintReport, format: string): string {
|
|
93
|
+
if (format === 'json') return `${JSON.stringify(report, null, 2)}\n`;
|
|
94
|
+
if (format === 'sarif') return `${JSON.stringify(toSarif(report), null, 2)}\n`;
|
|
95
|
+
if (format === 'text') return textReport(report);
|
|
96
|
+
throw new InputError(`Unsupported format ${format}.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const lintCommand = defineCommand({
|
|
100
|
+
meta: { name: 'lint', description: 'Statically validate a domain.md file or package.' },
|
|
101
|
+
args: {
|
|
102
|
+
input: {
|
|
103
|
+
type: 'positional',
|
|
104
|
+
required: true,
|
|
105
|
+
description: 'File, package directory, or - for stdin',
|
|
106
|
+
},
|
|
107
|
+
format: { type: 'string', default: 'json', description: 'json, text, or sarif' },
|
|
108
|
+
profile: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Assert authoring_draft, persisted_draft, anchored, or runtime',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
async run({ args }) {
|
|
114
|
+
const input = await readInput(args.input);
|
|
115
|
+
const validProfiles: ConformanceProfile[] = [
|
|
116
|
+
'authoring_draft',
|
|
117
|
+
'persisted_draft',
|
|
118
|
+
'anchored',
|
|
119
|
+
'runtime',
|
|
120
|
+
];
|
|
121
|
+
if (args.profile !== undefined && !validProfiles.includes(args.profile as ConformanceProfile)) {
|
|
122
|
+
throw new InputError(`Unsupported profile ${args.profile}.`);
|
|
123
|
+
}
|
|
124
|
+
const profile = args.profile as ConformanceProfile | undefined;
|
|
125
|
+
const report = lint(input.content, {
|
|
126
|
+
sourceName: input.sourceName,
|
|
127
|
+
...(profile === undefined ? {} : { expectedProfile: profile }),
|
|
128
|
+
});
|
|
129
|
+
process.stdout.write(outputLint(report, args.format));
|
|
130
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const diffCommand = defineCommand({
|
|
135
|
+
meta: { name: 'diff', description: 'Compare domain.md documents and classify changes.' },
|
|
136
|
+
args: {
|
|
137
|
+
before: { type: 'positional', required: true },
|
|
138
|
+
after: { type: 'positional', required: true },
|
|
139
|
+
format: { type: 'string', default: 'json', description: 'json or text' },
|
|
140
|
+
failOn: { type: 'string', default: 'regression', description: 'regression or review' },
|
|
141
|
+
},
|
|
142
|
+
async run({ args }) {
|
|
143
|
+
const [before, after] = await Promise.all([readInput(args.before), readInput(args.after)]);
|
|
144
|
+
if (args.failOn !== 'regression' && args.failOn !== 'review')
|
|
145
|
+
throw new InputError(`Unsupported fail-on mode ${args.failOn}.`);
|
|
146
|
+
const failOn = args.failOn === 'review' ? 'review' : 'regression';
|
|
147
|
+
const report = diffDomains(before.content, after.content, { failOn });
|
|
148
|
+
if (args.format === 'json') process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
149
|
+
else if (args.format === 'text') {
|
|
150
|
+
const lines = report.changes.map(
|
|
151
|
+
(change) => `${change.classification.toUpperCase()} ${change.path}`,
|
|
152
|
+
);
|
|
153
|
+
lines.push(
|
|
154
|
+
`Regression: ${report.conformanceRegression}; review required: ${report.requiresReview}`,
|
|
155
|
+
);
|
|
156
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
157
|
+
} else throw new InputError(`Unsupported format ${args.format}.`);
|
|
158
|
+
process.exitCode = report.regression ? 1 : 0;
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const exportCommand = defineCommand({
|
|
163
|
+
meta: { name: 'export', description: 'Export a parsed JSON model or SARIF conformance report.' },
|
|
164
|
+
args: {
|
|
165
|
+
input: { type: 'positional', required: true },
|
|
166
|
+
format: { type: 'string', required: true, description: 'json or sarif' },
|
|
167
|
+
},
|
|
168
|
+
async run({ args }) {
|
|
169
|
+
if (args.format !== 'json' && args.format !== 'sarif')
|
|
170
|
+
throw new InputError('Export format must be json or sarif.');
|
|
171
|
+
const input = await readInput(args.input);
|
|
172
|
+
const report = lint(input.content, { sourceName: input.sourceName });
|
|
173
|
+
if (args.format === 'json' && !report.ok) {
|
|
174
|
+
throw new ConformanceError(
|
|
175
|
+
`JSON model export refused: ${report.summary.errors} conformance error(s) remain.`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
process.stdout.write(
|
|
179
|
+
exportDomain(input.content, { format: args.format, sourceName: input.sourceName }),
|
|
180
|
+
);
|
|
181
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const specCommand = defineCommand({
|
|
186
|
+
meta: { name: 'spec', description: 'Output the bundled domain.md specification.' },
|
|
187
|
+
args: {
|
|
188
|
+
format: { type: 'string', default: 'markdown', description: 'markdown or json' },
|
|
189
|
+
rules: { type: 'boolean', default: false },
|
|
190
|
+
},
|
|
191
|
+
run({ args }) {
|
|
192
|
+
if (args.format === 'markdown') {
|
|
193
|
+
process.stdout.write(getSpecification());
|
|
194
|
+
if (args.rules) process.stdout.write(`\n\n${JSON.stringify(getRules(), null, 2)}\n`);
|
|
195
|
+
} else if (args.format === 'json') {
|
|
196
|
+
process.stdout.write(
|
|
197
|
+
`${JSON.stringify({ specification: getSpecification(), ...(args.rules ? { rules: getRules().rules } : {}) }, null, 2)}\n`,
|
|
198
|
+
);
|
|
199
|
+
} else throw new InputError(`Unsupported format ${args.format}.`);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const schemaCommand = defineCommand({
|
|
204
|
+
meta: { name: 'schema', description: 'Output the matching domain.md JSON Schema.' },
|
|
205
|
+
run() {
|
|
206
|
+
process.stdout.write(getSchemaSource());
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
async function parseParameters(path: string): Promise<Record<string, unknown>> {
|
|
211
|
+
let bytes: Uint8Array;
|
|
212
|
+
if (path === '-') bytes = await stdinBytes(MAX_LINKED_BYTES);
|
|
213
|
+
else {
|
|
214
|
+
const parameterPath = resolve(path);
|
|
215
|
+
const stat = await lstat(parameterPath).catch(() => undefined);
|
|
216
|
+
if (!stat?.isFile() || stat.isSymbolicLink())
|
|
217
|
+
throw new InputError(`Expected a regular parameter file: ${path}`);
|
|
218
|
+
if (stat.size > MAX_LINKED_BYTES)
|
|
219
|
+
throw new InputError(`Parameter file exceeds ${MAX_LINKED_BYTES} bytes: ${path}`);
|
|
220
|
+
bytes = await readFile(parameterPath);
|
|
221
|
+
}
|
|
222
|
+
const source = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
223
|
+
const document = parseDocument(source, { prettyErrors: true, uniqueKeys: true, version: '1.2' });
|
|
224
|
+
if (document.errors.length > 0 || document.warnings.length > 0)
|
|
225
|
+
throw new InputError('Parameter file contains invalid or unsafe YAML/JSON.');
|
|
226
|
+
let unsafe = false;
|
|
227
|
+
let nodes = 0;
|
|
228
|
+
let depth = 0;
|
|
229
|
+
let scalarTooLong = false;
|
|
230
|
+
visitYaml(document, {
|
|
231
|
+
Node(_key, node, parents) {
|
|
232
|
+
const candidate = node as { anchor?: string; tag?: string; value?: unknown };
|
|
233
|
+
nodes += 1;
|
|
234
|
+
depth = Math.max(depth, parents.length + 1);
|
|
235
|
+
if (typeof candidate.value === 'string' && candidate.value.length > MAX_SCALAR_LENGTH)
|
|
236
|
+
scalarTooLong = true;
|
|
237
|
+
if (isAlias(node) || candidate.anchor || candidate.tag?.startsWith('!')) unsafe = true;
|
|
238
|
+
},
|
|
239
|
+
Pair(_key, pair) {
|
|
240
|
+
const key = pair.key as { value?: unknown } | null;
|
|
241
|
+
if (key?.value === '<<') unsafe = true;
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
if (unsafe)
|
|
245
|
+
throw new InputError('Parameter file contains aliases, anchors, merge keys, or custom tags.');
|
|
246
|
+
if (nodes > MAX_YAML_NODES || depth > MAX_YAML_DEPTH || scalarTooLong) {
|
|
247
|
+
throw new InputError('Parameter file exceeds YAML node, depth, or scalar limits.');
|
|
248
|
+
}
|
|
249
|
+
const value: unknown = document.toJS({ maxAliasCount: 0 });
|
|
250
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
251
|
+
throw new InputError('Parameters must be a mapping.');
|
|
252
|
+
return value as Record<string, unknown>;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function safeOutputPath(root: string, relativePath: string): string {
|
|
256
|
+
const target = resolve(root, relativePath);
|
|
257
|
+
const fromRoot = relative(resolve(root), target);
|
|
258
|
+
if (
|
|
259
|
+
fromRoot === '' ||
|
|
260
|
+
fromRoot === '..' ||
|
|
261
|
+
fromRoot.startsWith(`..${sep}`) ||
|
|
262
|
+
isAbsolute(fromRoot)
|
|
263
|
+
) {
|
|
264
|
+
throw new InputError(`Rendered path escapes output root: ${relativePath}`);
|
|
265
|
+
}
|
|
266
|
+
return target;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const initCommand = defineCommand({
|
|
270
|
+
meta: {
|
|
271
|
+
name: 'init',
|
|
272
|
+
description: 'Render a verified protocol template bundle into an authoring draft.',
|
|
273
|
+
},
|
|
274
|
+
args: {
|
|
275
|
+
manifest: { type: 'string', required: true },
|
|
276
|
+
protocol: { type: 'string', required: true },
|
|
277
|
+
derivedType: { type: 'string', required: true },
|
|
278
|
+
params: { type: 'string', required: true, description: 'JSON/YAML path or -' },
|
|
279
|
+
output: { type: 'string', required: true },
|
|
280
|
+
manifestSha256: { type: 'string' },
|
|
281
|
+
manifestCid: { type: 'string' },
|
|
282
|
+
ipfsGateway: { type: 'string' },
|
|
283
|
+
},
|
|
284
|
+
async run({ args }) {
|
|
285
|
+
const output = resolve(args.output);
|
|
286
|
+
if (
|
|
287
|
+
await lstat(output).then(
|
|
288
|
+
() => true,
|
|
289
|
+
() => false,
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
throw new InputError(`Output already exists: ${output}`);
|
|
293
|
+
const manifestUri = /^[A-Za-z][A-Za-z0-9+.-]*:/.test(args.manifest)
|
|
294
|
+
? args.manifest
|
|
295
|
+
: pathToFileURL(resolve(args.manifest)).href;
|
|
296
|
+
const derivedTypes: TemplateDerivedType[] = [
|
|
297
|
+
'dao',
|
|
298
|
+
'organisation',
|
|
299
|
+
'project',
|
|
300
|
+
'asset',
|
|
301
|
+
'deed',
|
|
302
|
+
'investment',
|
|
303
|
+
'oracle',
|
|
304
|
+
'service',
|
|
305
|
+
'dataset',
|
|
306
|
+
'device',
|
|
307
|
+
'place',
|
|
308
|
+
'portfolio',
|
|
309
|
+
'marketplace',
|
|
310
|
+
'pod',
|
|
311
|
+
'claim_collection',
|
|
312
|
+
'custom',
|
|
313
|
+
];
|
|
314
|
+
if (!derivedTypes.includes(args.derivedType as TemplateDerivedType)) {
|
|
315
|
+
throw new InputError(`Unsupported derived type ${args.derivedType}.`);
|
|
316
|
+
}
|
|
317
|
+
const derivedType = args.derivedType as TemplateDerivedType;
|
|
318
|
+
let result;
|
|
319
|
+
try {
|
|
320
|
+
result = await renderTemplateBundle({
|
|
321
|
+
manifestUri,
|
|
322
|
+
expectedProtocol: args.protocol,
|
|
323
|
+
derivedType,
|
|
324
|
+
parameters: await parseParameters(args.params),
|
|
325
|
+
...(args.manifestSha256 ? { manifestSha256: args.manifestSha256 } : {}),
|
|
326
|
+
...(args.manifestCid ? { manifestCid: args.manifestCid } : {}),
|
|
327
|
+
...(args.ipfsGateway ? { ipfsGateway: args.ipfsGateway } : {}),
|
|
328
|
+
});
|
|
329
|
+
} catch (error) {
|
|
330
|
+
if (error instanceof TemplateValidationError) throw new ConformanceError(error.message);
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
const parent = dirname(output);
|
|
334
|
+
await mkdir(parent, { recursive: true });
|
|
335
|
+
const temporary = await mkdtemp(join(parent, `.${basename(output)}-`));
|
|
336
|
+
try {
|
|
337
|
+
for (const file of result.files) {
|
|
338
|
+
const target = safeOutputPath(temporary, file.path);
|
|
339
|
+
await mkdir(dirname(target), { recursive: true });
|
|
340
|
+
await writeFile(target, file.bytes, { flag: 'wx' });
|
|
341
|
+
}
|
|
342
|
+
await writeFile(join(temporary, 'provenance.yaml'), result.provenance, { flag: 'wx' });
|
|
343
|
+
if ((await readdir(temporary)).length === 0)
|
|
344
|
+
throw new InputError('Renderer produced no files.');
|
|
345
|
+
await rename(temporary, output);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
await rm(temporary, { recursive: true, force: true });
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
process.stdout.write(
|
|
351
|
+
`${JSON.stringify(
|
|
352
|
+
{
|
|
353
|
+
state: 'authoring_draft',
|
|
354
|
+
output,
|
|
355
|
+
files: result.files.map((file) => ({
|
|
356
|
+
path: file.path,
|
|
357
|
+
role: file.role,
|
|
358
|
+
sha256: file.sha256,
|
|
359
|
+
})),
|
|
360
|
+
validation: result.report.summary,
|
|
361
|
+
},
|
|
362
|
+
null,
|
|
363
|
+
2,
|
|
364
|
+
)}\n`,
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const main = defineCommand({
|
|
370
|
+
meta: {
|
|
371
|
+
name: 'domain.md',
|
|
372
|
+
version: PACKAGE_VERSION,
|
|
373
|
+
description:
|
|
374
|
+
'Authority-aware specification, validation, diff, export, and template tooling for domain.md.',
|
|
375
|
+
},
|
|
376
|
+
subCommands: {
|
|
377
|
+
lint: lintCommand,
|
|
378
|
+
diff: diffCommand,
|
|
379
|
+
export: exportCommand,
|
|
380
|
+
spec: specCommand,
|
|
381
|
+
schema: schemaCommand,
|
|
382
|
+
init: initCommand,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
async function execute(): Promise<void> {
|
|
387
|
+
const rawArgs = process.argv.slice(2);
|
|
388
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs.length === 0) {
|
|
389
|
+
await showUsage(main);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (rawArgs.length === 1 && (rawArgs[0] === '--version' || rawArgs[0] === '-v')) {
|
|
393
|
+
process.stdout.write(`${PACKAGE_VERSION}\n`);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
await runCommand(main, { rawArgs });
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
execute().catch((error: unknown) => {
|
|
400
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
401
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
402
|
+
process.exitCode = error instanceof ConformanceError ? 1 : 2;
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
export type { Finding };
|