@sprig-and-prose/sprig-universe 0.2.0 → 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/package.json +1 -1
- package/src/ast.js +30 -32
- package/src/cli.js +1 -210
- package/src/graph.js +635 -147
- package/src/ir.js +21 -6
- package/src/parser.js +225 -215
- package/test/fixtures/amaranthine-mini.prose +14 -8
- package/test/fixtures/multi-file-universe-a.prose +9 -4
- package/test/fixtures/multi-file-universe-b.prose +5 -4
- package/test/fixtures/named-duplicate.prose +6 -4
- package/test/fixtures/reference-attachments.prose +19 -0
- package/test/fixtures/reference-commas.prose +15 -0
- package/test/fixtures/reference-inline.prose +14 -0
- package/test/fixtures/reference-raw-url.prose +9 -0
- package/test/fixtures/reference-repo-paths.prose +11 -0
- package/test/fixtures/reference-unknown.prose +7 -0
- package/test/references.test.js +105 -0
- package/test/universe-basic.test.js +21 -166
- package/repositories/sprig-repository-github/index.js +0 -29
package/package.json
CHANGED
package/src/ast.js
CHANGED
|
@@ -23,37 +23,23 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* @typedef {Object}
|
|
26
|
+
* @typedef {Object} ReferenceDecl
|
|
27
27
|
* @property {string} kind - Always 'reference'
|
|
28
|
-
* @property {string}
|
|
29
|
-
* @property {string[]
|
|
30
|
-
* @property {string} [
|
|
28
|
+
* @property {string} [name] - Optional reference name (identifier)
|
|
29
|
+
* @property {string} [repositoryName] - Optional repository name from "in RepoName"
|
|
30
|
+
* @property {string} [url] - Optional raw URL (string literal)
|
|
31
|
+
* @property {string[]} [paths] - Optional array of path strings (string literals)
|
|
32
|
+
* @property {string} [referenceKind] - Optional reference kind (user-facing classification)
|
|
31
33
|
* @property {DescribeBlock} [describe] - Optional describe block
|
|
32
|
-
* @property {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @typedef {Object} NamedReferenceBlock
|
|
37
|
-
* @property {string} kind - Always 'named-reference'
|
|
38
|
-
* @property {string} name - Reference name (identifier)
|
|
39
|
-
* @property {string} repository - Repository name (string literal)
|
|
40
|
-
* @property {string[]} paths - Array of path strings (string literals)
|
|
41
|
-
* @property {string} [referenceKind] - Optional reference kind (e.g., 'api', 'data')
|
|
42
|
-
* @property {DescribeBlock} [describe] - Optional describe block
|
|
43
|
-
* @property {SourceSpan} source - Source span
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @typedef {Object} UsingInReferencesBlock
|
|
48
|
-
* @property {string} kind - Always 'using-in-references'
|
|
49
|
-
* @property {string[]} names - Array of identifier names to reference
|
|
34
|
+
* @property {NoteBlock} [note] - Optional note block
|
|
35
|
+
* @property {TitleBlock} [title] - Optional title block
|
|
50
36
|
* @property {SourceSpan} source - Source span
|
|
51
37
|
*/
|
|
52
38
|
|
|
53
39
|
/**
|
|
54
40
|
* @typedef {Object} ReferencesBlock
|
|
55
41
|
* @property {string} kind - Always 'references'
|
|
56
|
-
* @property {Array<
|
|
42
|
+
* @property {Array<{ name: string, source: SourceSpan }>} items - Array of identifier paths to references
|
|
57
43
|
* @property {SourceSpan} source - Source span
|
|
58
44
|
*/
|
|
59
45
|
|
|
@@ -91,6 +77,13 @@
|
|
|
91
77
|
* @property {SourceSpan} source - Source span
|
|
92
78
|
*/
|
|
93
79
|
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {Object} NoteBlock
|
|
82
|
+
* @property {string} kind - Always 'note'
|
|
83
|
+
* @property {string} raw - Raw text content (without outer braces)
|
|
84
|
+
* @property {SourceSpan} source - Source span
|
|
85
|
+
*/
|
|
86
|
+
|
|
94
87
|
/**
|
|
95
88
|
* @typedef {Object} TitleBlock
|
|
96
89
|
* @property {string} kind - Always 'title'
|
|
@@ -102,8 +95,11 @@
|
|
|
102
95
|
* @typedef {Object} RepositoryDecl
|
|
103
96
|
* @property {string} kind - Always 'repository'
|
|
104
97
|
* @property {string} name - Repository name (identifier)
|
|
105
|
-
* @property {string
|
|
106
|
-
* @property {
|
|
98
|
+
* @property {string} [parentName] - Optional parent name (from "in ParentName")
|
|
99
|
+
* @property {string} [url] - Repository base URL (string literal)
|
|
100
|
+
* @property {DescribeBlock} [describe] - Optional describe block
|
|
101
|
+
* @property {NoteBlock} [note] - Optional note block
|
|
102
|
+
* @property {TitleBlock} [title] - Optional title block
|
|
107
103
|
* @property {SourceSpan} source - Source span
|
|
108
104
|
*/
|
|
109
105
|
|
|
@@ -111,7 +107,7 @@
|
|
|
111
107
|
* @typedef {Object} UniverseDecl
|
|
112
108
|
* @property {string} kind - Always 'universe'
|
|
113
109
|
* @property {string} name - Universe name
|
|
114
|
-
* @property {Array<AnthologyDecl | SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | TitleBlock |
|
|
110
|
+
* @property {Array<AnthologyDecl | SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | TitleBlock | RepositoryDecl | ReferenceDecl | NamedDocumentBlock | UnknownBlock>} body - Body declarations
|
|
115
111
|
* @property {SourceSpan} source - Source span
|
|
116
112
|
*/
|
|
117
113
|
|
|
@@ -119,7 +115,8 @@
|
|
|
119
115
|
* @typedef {Object} AnthologyDecl
|
|
120
116
|
* @property {string} kind - Always 'anthology'
|
|
121
117
|
* @property {string} name - Anthology name
|
|
122
|
-
* @property {
|
|
118
|
+
* @property {string} [parentName] - Optional parent universe name (from "in UniverseName")
|
|
119
|
+
* @property {Array<SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | TitleBlock | ReferencesBlock | DocumentationBlock | RepositoryDecl | ReferenceDecl | UnknownBlock>} body - Body declarations
|
|
123
120
|
* @property {SourceSpan} source - Source span
|
|
124
121
|
*/
|
|
125
122
|
|
|
@@ -128,7 +125,7 @@
|
|
|
128
125
|
* @property {string} kind - Always 'series'
|
|
129
126
|
* @property {string} name - Series name
|
|
130
127
|
* @property {string} [parentName] - Optional parent anthology name (from "in AnthologyName")
|
|
131
|
-
* @property {Array<BookDecl | ChapterDecl | DescribeBlock | TitleBlock | ReferencesBlock | DocumentationBlock | UnknownBlock>} body - Body declarations
|
|
128
|
+
* @property {Array<BookDecl | ChapterDecl | DescribeBlock | TitleBlock | ReferencesBlock | DocumentationBlock | RepositoryDecl | ReferenceDecl | UnknownBlock>} body - Body declarations
|
|
132
129
|
* @property {SourceSpan} source - Source span
|
|
133
130
|
*/
|
|
134
131
|
|
|
@@ -137,7 +134,7 @@
|
|
|
137
134
|
* @property {string} kind - Always 'book'
|
|
138
135
|
* @property {string} name - Book name
|
|
139
136
|
* @property {string} parentName - Parent name (from "in ParentName")
|
|
140
|
-
* @property {Array<ChapterDecl | DescribeBlock | TitleBlock | ReferencesBlock | DocumentationBlock | UnknownBlock>} body - Body declarations
|
|
137
|
+
* @property {Array<ChapterDecl | DescribeBlock | TitleBlock | ReferencesBlock | DocumentationBlock | RepositoryDecl | ReferenceDecl | UnknownBlock>} body - Body declarations
|
|
141
138
|
* @property {SourceSpan} source - Source span
|
|
142
139
|
*/
|
|
143
140
|
|
|
@@ -146,7 +143,7 @@
|
|
|
146
143
|
* @property {string} kind - Always 'chapter'
|
|
147
144
|
* @property {string} name - Chapter name
|
|
148
145
|
* @property {string} parentName - Parent name (from "in ParentName")
|
|
149
|
-
* @property {Array<DescribeBlock | ReferencesBlock | DocumentationBlock | UnknownBlock>} body - Body declarations
|
|
146
|
+
* @property {Array<DescribeBlock | ReferencesBlock | DocumentationBlock | RepositoryDecl | ReferenceDecl | UnknownBlock>} body - Body declarations
|
|
150
147
|
* @property {SourceSpan} source - Source span
|
|
151
148
|
*/
|
|
152
149
|
|
|
@@ -155,7 +152,7 @@
|
|
|
155
152
|
* @property {string} kind - Always 'concept'
|
|
156
153
|
* @property {string} name - Concept name
|
|
157
154
|
* @property {string} [parentName] - Optional parent name (from "in ParentName")
|
|
158
|
-
* @property {Array<DescribeBlock | ReferencesBlock | DocumentationBlock | UnknownBlock>} body - Body declarations
|
|
155
|
+
* @property {Array<DescribeBlock | ReferencesBlock | DocumentationBlock | RepositoryDecl | ReferenceDecl | UnknownBlock>} body - Body declarations
|
|
159
156
|
* @property {SourceSpan} source - Source span
|
|
160
157
|
*/
|
|
161
158
|
|
|
@@ -244,7 +241,7 @@
|
|
|
244
241
|
*/
|
|
245
242
|
|
|
246
243
|
/**
|
|
247
|
-
* @typedef {UniverseDecl | AnthologyDecl | SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | TitleBlock | FromBlock | RelationshipsBlock | ReferencesBlock |
|
|
244
|
+
* @typedef {UniverseDecl | AnthologyDecl | SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | NoteBlock | TitleBlock | FromBlock | RelationshipsBlock | ReferencesBlock | ReferenceDecl | DocumentationBlock | DocumentBlock | NamedDocumentBlock | RepositoryDecl | UnknownBlock | SceneDecl | UsingBlock | ActorDecl | TypeBlock | IdentityBlock | SourceBlock | TransformsBlock | TransformBlock} ASTNode
|
|
248
245
|
*/
|
|
249
246
|
|
|
250
247
|
/**
|
|
@@ -252,6 +249,7 @@
|
|
|
252
249
|
* @property {string} file - File path
|
|
253
250
|
* @property {UniverseDecl[]} universes - Top-level universe declarations
|
|
254
251
|
* @property {SceneDecl[]} scenes - Top-level scene declarations
|
|
252
|
+
* @property {Array<AnthologyDecl | SeriesDecl | BookDecl | ChapterDecl | ConceptDecl | RelatesDecl | DescribeBlock | TitleBlock | RepositoryDecl | ReferenceDecl | ReferencesBlock | DocumentationBlock | NamedDocumentBlock | UnknownBlock>} [topLevelDecls] - Unscoped top-level declarations
|
|
255
253
|
* @property {SourceSpan} [source] - File-level source span
|
|
256
254
|
*/
|
|
257
255
|
|
package/src/cli.js
CHANGED
|
@@ -12,10 +12,9 @@ import { validateScenes } from './validator.js';
|
|
|
12
12
|
import chokidar from 'chokidar';
|
|
13
13
|
import { globSync } from 'glob';
|
|
14
14
|
|
|
15
|
-
// Get the directory where this CLI script is located
|
|
15
|
+
// Get the directory where this CLI script is located
|
|
16
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
17
|
const __dirname = dirname(__filename);
|
|
18
|
-
const PACKAGE_ROOT = resolve(__dirname, '..');
|
|
19
18
|
|
|
20
19
|
const DEFAULT_EXCLUDES = ['.sprig/**', 'dist/**', 'node_modules/**', '.git/**'];
|
|
21
20
|
|
|
@@ -24,7 +23,6 @@ function printGlobalHelp() {
|
|
|
24
23
|
console.log('Commands:');
|
|
25
24
|
console.log(' compile Compile universe and scene files to manifests');
|
|
26
25
|
console.log(' watch Watch universe and scene files and recompile on change');
|
|
27
|
-
console.log(' check:references Validate repository references');
|
|
28
26
|
console.log(' validate Validate scene file sources');
|
|
29
27
|
console.log(' parse Parse files (legacy command)');
|
|
30
28
|
console.log('');
|
|
@@ -79,21 +77,6 @@ function printValidateHelp() {
|
|
|
79
77
|
console.log(' --quiet Suppress observable header output');
|
|
80
78
|
}
|
|
81
79
|
|
|
82
|
-
function printCheckReferencesHelp() {
|
|
83
|
-
console.log('Usage: sprig-universe check:references [--root <path>] [--quiet]');
|
|
84
|
-
console.log('');
|
|
85
|
-
console.log('Validates repository references in the universe manifest.');
|
|
86
|
-
console.log('');
|
|
87
|
-
console.log('Discovery:');
|
|
88
|
-
console.log(' - Walks upward from current directory (or --root) to find universe.prose');
|
|
89
|
-
console.log(' - Loads all **/*.prose files under the discovered root');
|
|
90
|
-
console.log(' - Excludes: .sprig/**, dist/**, node_modules/**, .git/**');
|
|
91
|
-
console.log('');
|
|
92
|
-
console.log('Options:');
|
|
93
|
-
console.log(' --root <path> Override discovery start path (default: current directory)');
|
|
94
|
-
console.log(' --quiet Suppress observable header output');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
80
|
function printParseHelp() {
|
|
98
81
|
console.log('Usage: sprig-universe parse <fileOrDir> [--out <path>]');
|
|
99
82
|
console.log('');
|
|
@@ -253,90 +236,6 @@ function findSceneFiles(dir) {
|
|
|
253
236
|
return files;
|
|
254
237
|
}
|
|
255
238
|
|
|
256
|
-
/**
|
|
257
|
-
* Validate repository kinds exist in repositories directory
|
|
258
|
-
* @param {Record<string, any>} repositories - Repository config
|
|
259
|
-
* @param {string} universeRoot - Universe root directory (unused, kept for API compatibility)
|
|
260
|
-
* @returns {boolean} - True if valid, false otherwise
|
|
261
|
-
*/
|
|
262
|
-
function validateRepositoryKinds(repositories, universeRoot) {
|
|
263
|
-
// Repositories are located in the sprig-universe package directory, not in the universe project
|
|
264
|
-
const repositoriesDir = join(PACKAGE_ROOT, 'repositories');
|
|
265
|
-
const errors = [];
|
|
266
|
-
|
|
267
|
-
for (const [repoName, repoConfig] of Object.entries(repositories)) {
|
|
268
|
-
const kind = repoConfig.kind;
|
|
269
|
-
if (!kind) {
|
|
270
|
-
errors.push(`Repository "${repoName}" has no kind specified`);
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const kindDir = join(repositoriesDir, kind);
|
|
275
|
-
const indexFile = join(kindDir, 'index.js');
|
|
276
|
-
|
|
277
|
-
if (!existsSync(indexFile)) {
|
|
278
|
-
errors.push(`Repository kind "${kind}" not found. Expected: ${indexFile}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (errors.length > 0) {
|
|
283
|
-
console.error('Error: Invalid repository configurations:');
|
|
284
|
-
for (const error of errors) {
|
|
285
|
-
console.error(` - ${error}`);
|
|
286
|
-
}
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Validate repository references
|
|
295
|
-
* @param {any} graph - Parsed graph
|
|
296
|
-
* @param {Record<string, any>} repositories - Repository config
|
|
297
|
-
* @returns {boolean} - True if valid, false otherwise
|
|
298
|
-
*/
|
|
299
|
-
function validateReferences(graph, repositories) {
|
|
300
|
-
const repoKeys = new Set(Object.keys(repositories || {}));
|
|
301
|
-
/** @type {Map<string, { count: number, examples: Array<{ file: string, line: number }> }>} */
|
|
302
|
-
const unknownRepos = new Map();
|
|
303
|
-
|
|
304
|
-
for (const node of Object.values(graph.nodes)) {
|
|
305
|
-
if (!node.references) continue;
|
|
306
|
-
|
|
307
|
-
for (const ref of node.references) {
|
|
308
|
-
if (repoKeys.has(ref.repository)) continue;
|
|
309
|
-
|
|
310
|
-
const source = ref.source;
|
|
311
|
-
const entry = unknownRepos.get(ref.repository) || { count: 0, examples: [] };
|
|
312
|
-
entry.count += 1;
|
|
313
|
-
|
|
314
|
-
if (entry.examples.length < 3 && source?.file && source?.start?.line) {
|
|
315
|
-
entry.examples.push({ file: source.file, line: source.start.line });
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
unknownRepos.set(ref.repository, entry);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (unknownRepos.size === 0) {
|
|
323
|
-
return true;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
console.error('Error: Reference(s) to unknown repositories:');
|
|
327
|
-
for (const [repo, info] of unknownRepos.entries()) {
|
|
328
|
-
const exampleText =
|
|
329
|
-
info.examples.length > 0
|
|
330
|
-
? ` (e.g. ${info.examples
|
|
331
|
-
.map((ex) => `${ex.file}:${ex.line}`)
|
|
332
|
-
.join(', ')})`
|
|
333
|
-
: '';
|
|
334
|
-
console.error(` - ${repo}: ${info.count} occurrence(s)${exampleText}`);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
239
|
/**
|
|
341
240
|
* Main CLI entry point
|
|
342
241
|
*/
|
|
@@ -364,10 +263,6 @@ function main() {
|
|
|
364
263
|
printValidateHelp();
|
|
365
264
|
return;
|
|
366
265
|
}
|
|
367
|
-
if (command === 'check:references') {
|
|
368
|
-
printCheckReferencesHelp();
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
266
|
if (command === 'parse') {
|
|
372
267
|
printParseHelp();
|
|
373
268
|
return;
|
|
@@ -379,11 +274,6 @@ function main() {
|
|
|
379
274
|
return;
|
|
380
275
|
}
|
|
381
276
|
|
|
382
|
-
if (command === 'check:references') {
|
|
383
|
-
handleCheckReferences(commandArgs);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
277
|
if (command === 'validate' || command === 'check') {
|
|
388
278
|
handleValidate(commandArgs);
|
|
389
279
|
return;
|
|
@@ -630,16 +520,6 @@ function compileUniverseWithFiles(universeRoot, files, silent = false) {
|
|
|
630
520
|
return false;
|
|
631
521
|
}
|
|
632
522
|
|
|
633
|
-
// Validate repository kinds exist
|
|
634
|
-
if (!validateRepositoryKinds(graph.repositories || {}, universeRoot)) {
|
|
635
|
-
return false;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// Validate references
|
|
639
|
-
if (!validateReferences(graph, graph.repositories || {})) {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
523
|
// Get series names for status message
|
|
644
524
|
const seriesNames = Object.values(graph.nodes)
|
|
645
525
|
.filter((node) => node.kind === 'series')
|
|
@@ -715,7 +595,6 @@ function compileUniverseWithFiles(universeRoot, files, silent = false) {
|
|
|
715
595
|
*/
|
|
716
596
|
function compileUniverse(config, configPath, silent = false) {
|
|
717
597
|
const universeConfig = config.universe;
|
|
718
|
-
const repositories = config.repositories || {};
|
|
719
598
|
|
|
720
599
|
if (!universeConfig) {
|
|
721
600
|
console.error('Error: Missing "universe" section in config');
|
|
@@ -756,11 +635,6 @@ function compileUniverse(config, configPath, silent = false) {
|
|
|
756
635
|
try {
|
|
757
636
|
const graph = parseFiles(fileContents);
|
|
758
637
|
|
|
759
|
-
// Validate references
|
|
760
|
-
if (!validateReferences(graph, repositories)) {
|
|
761
|
-
return false;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
638
|
// Get series names for status message
|
|
765
639
|
const seriesNames = Object.values(graph.nodes)
|
|
766
640
|
.filter((node) => node.kind === 'series')
|
|
@@ -770,7 +644,6 @@ function compileUniverse(config, configPath, silent = false) {
|
|
|
770
644
|
// Add repositories and metadata to manifest
|
|
771
645
|
const manifest = {
|
|
772
646
|
...graph,
|
|
773
|
-
repositories,
|
|
774
647
|
generatedAt: new Date().toISOString(),
|
|
775
648
|
};
|
|
776
649
|
|
|
@@ -1358,88 +1231,6 @@ function handleWatch(args) {
|
|
|
1358
1231
|
});
|
|
1359
1232
|
}
|
|
1360
1233
|
|
|
1361
|
-
/**
|
|
1362
|
-
* Handle check:references command
|
|
1363
|
-
* @param {string[]} args
|
|
1364
|
-
*/
|
|
1365
|
-
function handleCheckReferences(args) {
|
|
1366
|
-
const quiet = hasQuietFlag(args);
|
|
1367
|
-
const rootStart = resolveRootPath(args);
|
|
1368
|
-
|
|
1369
|
-
// Discover universe root
|
|
1370
|
-
const universeRoot = discoverUniverseRoot(rootStart);
|
|
1371
|
-
if (!universeRoot) {
|
|
1372
|
-
console.error(`Error: Could not find universe.prose marker. Searched upward from: ${rootStart}`);
|
|
1373
|
-
process.exit(1);
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
// Load prose files using new discovery mechanism
|
|
1377
|
-
const proseFiles = loadProseFiles(universeRoot);
|
|
1378
|
-
if (proseFiles.length === 0) {
|
|
1379
|
-
console.error(`Error: No .prose files found under root: ${universeRoot}`);
|
|
1380
|
-
process.exit(1);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
// Parse files to get universe info for header
|
|
1384
|
-
const fileContents = proseFiles.map((file) => ({
|
|
1385
|
-
file,
|
|
1386
|
-
text: readFileSync(file, 'utf-8'),
|
|
1387
|
-
}));
|
|
1388
|
-
|
|
1389
|
-
let graph;
|
|
1390
|
-
try {
|
|
1391
|
-
graph = parseFiles(fileContents);
|
|
1392
|
-
} catch (error) {
|
|
1393
|
-
console.error(`Error parsing files: ${error.message}`);
|
|
1394
|
-
process.exit(1);
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
// Validate exactly one universe
|
|
1398
|
-
const validation = validateUniverseCount(graph);
|
|
1399
|
-
if (!validation.valid) {
|
|
1400
|
-
console.error(`Error: ${validation.error}`);
|
|
1401
|
-
process.exit(1);
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// Print observable header unless quiet
|
|
1405
|
-
if (!quiet) {
|
|
1406
|
-
printObservableHeader(validation.universeName, universeRoot, proseFiles.length);
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
// Validate repository kinds exist
|
|
1410
|
-
if (!validateRepositoryKinds(graph.repositories || {}, universeRoot)) {
|
|
1411
|
-
console.error('❌ Repository kind validation failed');
|
|
1412
|
-
process.exit(1);
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
// Validate references
|
|
1416
|
-
if (!validateReferences(graph, graph.repositories || {})) {
|
|
1417
|
-
console.error('❌ Reference validation failed');
|
|
1418
|
-
process.exit(1);
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
// Check for parsing errors
|
|
1422
|
-
const hasErrors = graph.diagnostics.some((d) => d.severity === 'error');
|
|
1423
|
-
if (hasErrors) {
|
|
1424
|
-
process.exit(1);
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
// Display warnings
|
|
1428
|
-
const warnings = graph.diagnostics.filter((d) => d.severity === 'warning');
|
|
1429
|
-
if (warnings.length > 0) {
|
|
1430
|
-
console.warn(`\n⚠️ ${warnings.length} warning(s):`);
|
|
1431
|
-
for (const warning of warnings) {
|
|
1432
|
-
const source = warning.source
|
|
1433
|
-
? `${warning.source.file}:${warning.source.start.line}:${warning.source.start.col}`
|
|
1434
|
-
: 'unknown location';
|
|
1435
|
-
console.warn(` ${source}: ${warning.message}`);
|
|
1436
|
-
}
|
|
1437
|
-
console.warn('');
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
console.log('✅ All references are valid');
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
1234
|
/**
|
|
1444
1235
|
* Handle parse command (legacy)
|
|
1445
1236
|
* @param {string[]} args
|