@hstm-labs/forge-spec-parser 0.1.2 → 0.1.4
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/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +18 -3
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +27 -3
- package/dist/loader.js.map +1 -1
- package/dist/merge.d.ts +23 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +56 -0
- package/dist/merge.js.map +1 -0
- package/dist/resolve.d.ts +21 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +70 -0
- package/dist/resolve.js.map +1 -0
- package/dist/validate-stage.d.ts +24 -4
- package/dist/validate-stage.d.ts.map +1 -1
- package/dist/validate-stage.js +23 -7
- package/dist/validate-stage.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export type { SpecFormat, ParsedSpecification, SpecMetadata, SpecSection, SpecRe
|
|
|
2
2
|
export type { SpecParserPlugin } from './parser-plugin.js';
|
|
3
3
|
export { ParserRegistry } from './registry.js';
|
|
4
4
|
export { detectFormat } from './detect.js';
|
|
5
|
-
export { loadSpecFile, parseSpec } from './loader.js';
|
|
5
|
+
export { loadSpecFile, parseSpec, parseSpecPath } from './loader.js';
|
|
6
|
+
export { resolveSpecFiles } from './resolve.js';
|
|
7
|
+
export { mergeSpecs } from './merge.js';
|
|
6
8
|
export { MarkdownParserPlugin } from './parsers/markdown-parser.js';
|
|
7
9
|
export { YamlParserPlugin } from './parsers/yaml-parser.js';
|
|
8
10
|
export { JsonParserPlugin } from './parsers/json-parser.js';
|
|
@@ -14,5 +16,6 @@ export { checkCompleteness } from './validators/completeness-checker.js';
|
|
|
14
16
|
export type { CompletenessResult } from './validators/completeness-checker.js';
|
|
15
17
|
export { validateCrossReferences } from './validators/cross-reference-validator.js';
|
|
16
18
|
export { validateSpec } from './validators/spec-validator.js';
|
|
17
|
-
export { executeValidateStage } from './validate-stage.js';
|
|
19
|
+
export { executeValidateStage, executeValidateStageWithSpec } from './validate-stage.js';
|
|
20
|
+
export type { ValidateStageResult } from './validate-stage.js';
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,YAAY,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAG9D,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACzF,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,11 @@ export { ParserRegistry } from './registry.js';
|
|
|
4
4
|
// Format detection
|
|
5
5
|
export { detectFormat } from './detect.js';
|
|
6
6
|
// File loading and parse entry point
|
|
7
|
-
export { loadSpecFile, parseSpec } from './loader.js';
|
|
7
|
+
export { loadSpecFile, parseSpec, parseSpecPath } from './loader.js';
|
|
8
|
+
// Spec path resolution (file or directory)
|
|
9
|
+
export { resolveSpecFiles } from './resolve.js';
|
|
10
|
+
// Multi-file merge
|
|
11
|
+
export { mergeSpecs } from './merge.js';
|
|
8
12
|
// Parser plugins
|
|
9
13
|
export { MarkdownParserPlugin } from './parsers/markdown-parser.js';
|
|
10
14
|
export { YamlParserPlugin } from './parsers/yaml-parser.js';
|
|
@@ -19,5 +23,5 @@ export { checkCompleteness } from './validators/completeness-checker.js';
|
|
|
19
23
|
export { validateCrossReferences } from './validators/cross-reference-validator.js';
|
|
20
24
|
export { validateSpec } from './validators/spec-validator.js';
|
|
21
25
|
// Validate stage integration
|
|
22
|
-
export { executeValidateStage } from './validate-stage.js';
|
|
26
|
+
export { executeValidateStage, executeValidateStageWithSpec } from './validate-stage.js';
|
|
23
27
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAmB1C,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qCAAqC;AACrC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAmB1C,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qCAAqC;AACrC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAErE,2CAA2C;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,mBAAmB;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,iBAAiB;AACjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,2BAA2B;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAU9D,aAAa;AACb,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAEzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,6BAA6B;AAC7B,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/loader.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Specification file loader and primary parse entry point.
|
|
3
3
|
*
|
|
4
|
-
* Provides {@link loadSpecFile} for reading raw file content
|
|
5
|
-
* {@link parseSpec}
|
|
6
|
-
*
|
|
4
|
+
* Provides {@link loadSpecFile} for reading raw file content,
|
|
5
|
+
* {@link parseSpec} for parsing a single file, and
|
|
6
|
+
* {@link parseSpecPath} as the high-level API that handles both
|
|
7
|
+
* file and directory `specPath` values.
|
|
7
8
|
*/
|
|
8
9
|
import type { ParsedSpecification } from './types.js';
|
|
9
10
|
import type { ParserRegistry } from './registry.js';
|
|
@@ -28,4 +29,18 @@ export declare function loadSpecFile(filePath: string): string;
|
|
|
28
29
|
* @throws {@link ForgeError} with code `FORGE-SPEC-001` if format detection, file reading, or parsing fails
|
|
29
30
|
*/
|
|
30
31
|
export declare function parseSpec(filePath: string, registry: ParserRegistry): ParsedSpecification;
|
|
32
|
+
/**
|
|
33
|
+
* Parse a specification path that may be a single file or a directory.
|
|
34
|
+
*
|
|
35
|
+
* When `specPath` is a directory, all supported spec files within it
|
|
36
|
+
* (recursively) are parsed and merged into a single
|
|
37
|
+
* {@link ParsedSpecification}.
|
|
38
|
+
*
|
|
39
|
+
* @param specPath - Path to a spec file or directory containing spec files
|
|
40
|
+
* @param registry - Parser registry with registered format plugins
|
|
41
|
+
* @returns Parsed (and possibly merged) specification
|
|
42
|
+
* @throws {@link ForgeError} `FORGE-SPEC-003` if path does not exist or directory is empty
|
|
43
|
+
* @throws {@link ForgeError} `FORGE-SPEC-001` if any file cannot be parsed
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseSpecPath(specPath: string, registry: ParserRegistry): ParsedSpecification;
|
|
31
46
|
//# sourceMappingURL=loader.d.ts.map
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,GACvB,mBAAmB,CAKrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,GACvB,mBAAmB,CASrB"}
|
package/dist/loader.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Specification file loader and primary parse entry point.
|
|
3
3
|
*
|
|
4
|
-
* Provides {@link loadSpecFile} for reading raw file content
|
|
5
|
-
* {@link parseSpec}
|
|
6
|
-
*
|
|
4
|
+
* Provides {@link loadSpecFile} for reading raw file content,
|
|
5
|
+
* {@link parseSpec} for parsing a single file, and
|
|
6
|
+
* {@link parseSpecPath} as the high-level API that handles both
|
|
7
|
+
* file and directory `specPath` values.
|
|
7
8
|
*/
|
|
8
9
|
import fs from 'node:fs';
|
|
9
10
|
import { ForgeError, ErrorCodes } from '@hstm-labs/forge-common';
|
|
10
11
|
import { detectFormat } from './detect.js';
|
|
12
|
+
import { resolveSpecFiles } from './resolve.js';
|
|
13
|
+
import { mergeSpecs } from './merge.js';
|
|
11
14
|
/**
|
|
12
15
|
* Read the content of a specification file from disk.
|
|
13
16
|
*
|
|
@@ -42,4 +45,25 @@ export function parseSpec(filePath, registry) {
|
|
|
42
45
|
const content = loadSpecFile(filePath);
|
|
43
46
|
return plugin.parse(content, filePath);
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Parse a specification path that may be a single file or a directory.
|
|
50
|
+
*
|
|
51
|
+
* When `specPath` is a directory, all supported spec files within it
|
|
52
|
+
* (recursively) are parsed and merged into a single
|
|
53
|
+
* {@link ParsedSpecification}.
|
|
54
|
+
*
|
|
55
|
+
* @param specPath - Path to a spec file or directory containing spec files
|
|
56
|
+
* @param registry - Parser registry with registered format plugins
|
|
57
|
+
* @returns Parsed (and possibly merged) specification
|
|
58
|
+
* @throws {@link ForgeError} `FORGE-SPEC-003` if path does not exist or directory is empty
|
|
59
|
+
* @throws {@link ForgeError} `FORGE-SPEC-001` if any file cannot be parsed
|
|
60
|
+
*/
|
|
61
|
+
export function parseSpecPath(specPath, registry) {
|
|
62
|
+
const files = resolveSpecFiles(specPath);
|
|
63
|
+
if (files.length === 1) {
|
|
64
|
+
return parseSpec(files[0], registry);
|
|
65
|
+
}
|
|
66
|
+
const specs = files.map((f) => parseSpec(f, registry));
|
|
67
|
+
return mergeSpecs(specs, specPath);
|
|
68
|
+
}
|
|
45
69
|
//# sourceMappingURL=loader.js.map
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAClB,UAAU,CAAC,IAAI,CAAC,aAAa,EAC7B,sCAAsC,QAAQ,KAAK;YACjD,qDAAqD,EACvD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAC/C,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CACvB,QAAgB,EAChB,QAAwB;IAExB,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,QAAwB;IAExB,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IACvD,OAAO,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-file specification merger.
|
|
3
|
+
*
|
|
4
|
+
* Combines multiple {@link ParsedSpecification} objects into a single
|
|
5
|
+
* unified specification. Used when `specPath` points to a directory
|
|
6
|
+
* containing multiple spec files.
|
|
7
|
+
*/
|
|
8
|
+
import type { ParsedSpecification } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Merge multiple parsed specifications into one.
|
|
11
|
+
*
|
|
12
|
+
* - Metadata: titles are joined; `sourcePath` reflects the directory.
|
|
13
|
+
* - Sections, requirements, workflows: concatenated in order.
|
|
14
|
+
* - Entities: deduplicated by name (first occurrence wins).
|
|
15
|
+
* - API style: first non-undefined value wins.
|
|
16
|
+
*
|
|
17
|
+
* @param specs - Array of parsed specifications (must have at least one)
|
|
18
|
+
* @param sourcePath - Path to attribute as the merged spec's source
|
|
19
|
+
* @returns A single merged specification
|
|
20
|
+
* @throws {Error} if `specs` is empty
|
|
21
|
+
*/
|
|
22
|
+
export declare function mergeSpecs(specs: ParsedSpecification[], sourcePath: string): ParsedSpecification;
|
|
23
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,YAAY,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,mBAAmB,EAAE,EAC5B,UAAU,EAAE,MAAM,GACjB,mBAAmB,CAyCrB"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-file specification merger.
|
|
3
|
+
*
|
|
4
|
+
* Combines multiple {@link ParsedSpecification} objects into a single
|
|
5
|
+
* unified specification. Used when `specPath` points to a directory
|
|
6
|
+
* containing multiple spec files.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Merge multiple parsed specifications into one.
|
|
10
|
+
*
|
|
11
|
+
* - Metadata: titles are joined; `sourcePath` reflects the directory.
|
|
12
|
+
* - Sections, requirements, workflows: concatenated in order.
|
|
13
|
+
* - Entities: deduplicated by name (first occurrence wins).
|
|
14
|
+
* - API style: first non-undefined value wins.
|
|
15
|
+
*
|
|
16
|
+
* @param specs - Array of parsed specifications (must have at least one)
|
|
17
|
+
* @param sourcePath - Path to attribute as the merged spec's source
|
|
18
|
+
* @returns A single merged specification
|
|
19
|
+
* @throws {Error} if `specs` is empty
|
|
20
|
+
*/
|
|
21
|
+
export function mergeSpecs(specs, sourcePath) {
|
|
22
|
+
if (specs.length === 0) {
|
|
23
|
+
throw new Error('Cannot merge zero specifications.');
|
|
24
|
+
}
|
|
25
|
+
const first = specs[0];
|
|
26
|
+
if (specs.length === 1) {
|
|
27
|
+
return first;
|
|
28
|
+
}
|
|
29
|
+
const metadata = {
|
|
30
|
+
title: specs.map((s) => s.metadata.title).join(' + '),
|
|
31
|
+
sourcePath,
|
|
32
|
+
format: first.metadata.format,
|
|
33
|
+
description: specs
|
|
34
|
+
.map((s) => s.metadata.description)
|
|
35
|
+
.filter(Boolean)
|
|
36
|
+
.join('; ') || undefined,
|
|
37
|
+
};
|
|
38
|
+
const seenEntities = new Set();
|
|
39
|
+
const entities = specs.flatMap((s) => s.entities).filter((e) => {
|
|
40
|
+
if (seenEntities.has(e.name)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
seenEntities.add(e.name);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
const apiStyle = specs.find((s) => s.apiStyle !== undefined)?.apiStyle;
|
|
47
|
+
return {
|
|
48
|
+
metadata,
|
|
49
|
+
sections: specs.flatMap((s) => s.sections),
|
|
50
|
+
requirements: specs.flatMap((s) => s.requirements),
|
|
51
|
+
entities,
|
|
52
|
+
workflows: specs.flatMap((s) => s.workflows),
|
|
53
|
+
...(apiStyle !== undefined ? { apiStyle } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CACxB,KAA4B,EAC5B,UAAkB;IAElB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAExB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAiB;QAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QACrD,UAAU;QACV,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;QAC7B,WAAW,EACT,KAAK;aACF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;aAClC,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS;KAC7B,CAAC;IAEF,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC7D,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,QAAQ,CAAC;IAEvE,OAAO;QACL,QAAQ;QACR,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1C,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;QAClD,QAAQ;QACR,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5C,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec path resolution — handles both file and directory `specPath` values.
|
|
3
|
+
*
|
|
4
|
+
* When `specPath` is a directory, recursively scans for files with supported
|
|
5
|
+
* extensions (`.md`, `.yaml`, `.yml`, `.json`) and returns them in sorted
|
|
6
|
+
* order for deterministic processing.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a `specPath` to one or more specification file paths.
|
|
10
|
+
*
|
|
11
|
+
* - If `specPath` is a file, returns it as a single-element array.
|
|
12
|
+
* - If `specPath` is a directory, recursively scans for files with
|
|
13
|
+
* supported extensions and returns them sorted alphabetically.
|
|
14
|
+
*
|
|
15
|
+
* @param specPath - Absolute or relative path to a spec file or directory
|
|
16
|
+
* @returns Sorted array of resolved spec file paths
|
|
17
|
+
* @throws {@link ForgeError} `FORGE-SPEC-003` if the path does not exist or
|
|
18
|
+
* the directory contains no supported spec files
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveSpecFiles(specPath: string): string[];
|
|
21
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAgC3D"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec path resolution — handles both file and directory `specPath` values.
|
|
3
|
+
*
|
|
4
|
+
* When `specPath` is a directory, recursively scans for files with supported
|
|
5
|
+
* extensions (`.md`, `.yaml`, `.yml`, `.json`) and returns them in sorted
|
|
6
|
+
* order for deterministic processing.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { ForgeError, ErrorCodes } from '@hstm-labs/forge-common';
|
|
11
|
+
/** File extensions accepted as specification files. */
|
|
12
|
+
const SUPPORTED_EXTENSIONS = new Set([
|
|
13
|
+
'.md',
|
|
14
|
+
'.yaml',
|
|
15
|
+
'.yml',
|
|
16
|
+
'.json',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a `specPath` to one or more specification file paths.
|
|
20
|
+
*
|
|
21
|
+
* - If `specPath` is a file, returns it as a single-element array.
|
|
22
|
+
* - If `specPath` is a directory, recursively scans for files with
|
|
23
|
+
* supported extensions and returns them sorted alphabetically.
|
|
24
|
+
*
|
|
25
|
+
* @param specPath - Absolute or relative path to a spec file or directory
|
|
26
|
+
* @returns Sorted array of resolved spec file paths
|
|
27
|
+
* @throws {@link ForgeError} `FORGE-SPEC-003` if the path does not exist or
|
|
28
|
+
* the directory contains no supported spec files
|
|
29
|
+
*/
|
|
30
|
+
export function resolveSpecFiles(specPath) {
|
|
31
|
+
const stat = fs.statSync(specPath, { throwIfNoEntry: false });
|
|
32
|
+
if (!stat) {
|
|
33
|
+
throw new ForgeError(ErrorCodes.SPEC.NOT_FOUND, `Specification path '${specPath}' does not exist. ` +
|
|
34
|
+
`Verify the specPath in forge.config.json and retry.`);
|
|
35
|
+
}
|
|
36
|
+
if (stat.isFile()) {
|
|
37
|
+
return [specPath];
|
|
38
|
+
}
|
|
39
|
+
if (stat.isDirectory()) {
|
|
40
|
+
const files = scanDirectory(specPath);
|
|
41
|
+
if (files.length === 0) {
|
|
42
|
+
throw new ForgeError(ErrorCodes.SPEC.NOT_FOUND, `No specification files found in directory '${specPath}'. ` +
|
|
43
|
+
`Add .md, .yaml, .yml, or .json files to the directory and retry.`);
|
|
44
|
+
}
|
|
45
|
+
return files.sort();
|
|
46
|
+
}
|
|
47
|
+
throw new ForgeError(ErrorCodes.SPEC.NOT_FOUND, `Specification path '${specPath}' is not a file or directory. ` +
|
|
48
|
+
`Verify the specPath in forge.config.json and retry.`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Recursively scan a directory for files with supported spec extensions.
|
|
52
|
+
*/
|
|
53
|
+
function scanDirectory(dirPath) {
|
|
54
|
+
const results = [];
|
|
55
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
results.push(...scanDirectory(fullPath));
|
|
60
|
+
}
|
|
61
|
+
else if (entry.isFile()) {
|
|
62
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
63
|
+
if (SUPPORTED_EXTENSIONS.has(ext)) {
|
|
64
|
+
results.push(fullPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEjE,uDAAuD;AACvD,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IACxD,KAAK;IACL,OAAO;IACP,MAAM;IACN,OAAO;CACR,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IAE9D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,UAAU,CAClB,UAAU,CAAC,IAAI,CAAC,SAAS,EACzB,uBAAuB,QAAQ,oBAAoB;YACjD,qDAAqD,CACxD,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,UAAU,CAClB,UAAU,CAAC,IAAI,CAAC,SAAS,EACzB,8CAA8C,QAAQ,KAAK;gBACzD,kEAAkE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,IAAI,UAAU,CAClB,UAAU,CAAC,IAAI,CAAC,SAAS,EACzB,uBAAuB,QAAQ,gCAAgC;QAC7D,qDAAqD,CACxD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YACnD,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/validate-stage.d.ts
CHANGED
|
@@ -5,14 +5,34 @@
|
|
|
5
5
|
* This function will be called by the pipeline runner when executing the
|
|
6
6
|
* `validate` stage of a generation run.
|
|
7
7
|
*/
|
|
8
|
+
import type { ParsedSpecification } from './types.js';
|
|
8
9
|
import type { ValidationReport } from './validation-types.js';
|
|
10
|
+
/** Result of the validate stage containing both the report and parsed spec. */
|
|
11
|
+
export interface ValidateStageResult {
|
|
12
|
+
/** Validation report with findings and completeness score. */
|
|
13
|
+
report: ValidationReport;
|
|
14
|
+
/** The parsed (and possibly merged) specification. */
|
|
15
|
+
parsedSpec: ParsedSpecification;
|
|
16
|
+
}
|
|
9
17
|
/**
|
|
10
|
-
* Execute the validate pipeline stage: parse a specification
|
|
11
|
-
* validate the result.
|
|
18
|
+
* Execute the validate pipeline stage: parse a specification path
|
|
19
|
+
* (file or directory) and validate the result.
|
|
12
20
|
*
|
|
13
|
-
* @param specPath - Path to
|
|
21
|
+
* @param specPath - Path to a specification file or directory
|
|
14
22
|
* @returns Validation report for the parsed specification
|
|
15
|
-
* @throws {@link ForgeError} if the
|
|
23
|
+
* @throws {@link ForgeError} if the path cannot be resolved, read, or parsed
|
|
16
24
|
*/
|
|
17
25
|
export declare function executeValidateStage(specPath: string): ValidationReport;
|
|
26
|
+
/**
|
|
27
|
+
* Execute the validate pipeline stage and return both the validation
|
|
28
|
+
* report and parsed specification.
|
|
29
|
+
*
|
|
30
|
+
* Avoids double-parsing when the caller also needs the parsed spec
|
|
31
|
+
* (e.g. the core pipeline `ValidateStage`).
|
|
32
|
+
*
|
|
33
|
+
* @param specPath - Path to a specification file or directory
|
|
34
|
+
* @returns Validation report and parsed specification
|
|
35
|
+
* @throws {@link ForgeError} if the path cannot be resolved, read, or parsed
|
|
36
|
+
*/
|
|
37
|
+
export declare function executeValidateStageWithSpec(specPath: string): ValidateStageResult;
|
|
18
38
|
//# sourceMappingURL=validate-stage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-stage.d.ts","sourceRoot":"","sources":["../src/validate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAK9D;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GACf,gBAAgB,
|
|
1
|
+
{"version":3,"file":"validate-stage.d.ts","sourceRoot":"","sources":["../src/validate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAK9D,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,MAAM,EAAE,gBAAgB,CAAC;IACzB,sDAAsD;IACtD,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAGlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,GACf,mBAAmB,CAKrB"}
|
package/dist/validate-stage.js
CHANGED
|
@@ -6,19 +6,35 @@
|
|
|
6
6
|
* `validate` stage of a generation run.
|
|
7
7
|
*/
|
|
8
8
|
import { createDefaultRegistry } from './default-registry.js';
|
|
9
|
-
import {
|
|
9
|
+
import { parseSpecPath } from './loader.js';
|
|
10
10
|
import { validateSpec } from './validators/spec-validator.js';
|
|
11
11
|
/**
|
|
12
|
-
* Execute the validate pipeline stage: parse a specification
|
|
13
|
-
* validate the result.
|
|
12
|
+
* Execute the validate pipeline stage: parse a specification path
|
|
13
|
+
* (file or directory) and validate the result.
|
|
14
14
|
*
|
|
15
|
-
* @param specPath - Path to
|
|
15
|
+
* @param specPath - Path to a specification file or directory
|
|
16
16
|
* @returns Validation report for the parsed specification
|
|
17
|
-
* @throws {@link ForgeError} if the
|
|
17
|
+
* @throws {@link ForgeError} if the path cannot be resolved, read, or parsed
|
|
18
18
|
*/
|
|
19
19
|
export function executeValidateStage(specPath) {
|
|
20
|
+
const { report } = executeValidateStageWithSpec(specPath);
|
|
21
|
+
return report;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Execute the validate pipeline stage and return both the validation
|
|
25
|
+
* report and parsed specification.
|
|
26
|
+
*
|
|
27
|
+
* Avoids double-parsing when the caller also needs the parsed spec
|
|
28
|
+
* (e.g. the core pipeline `ValidateStage`).
|
|
29
|
+
*
|
|
30
|
+
* @param specPath - Path to a specification file or directory
|
|
31
|
+
* @returns Validation report and parsed specification
|
|
32
|
+
* @throws {@link ForgeError} if the path cannot be resolved, read, or parsed
|
|
33
|
+
*/
|
|
34
|
+
export function executeValidateStageWithSpec(specPath) {
|
|
20
35
|
const registry = createDefaultRegistry();
|
|
21
|
-
const
|
|
22
|
-
|
|
36
|
+
const parsedSpec = parseSpecPath(specPath, registry);
|
|
37
|
+
const report = validateSpec(parsedSpec);
|
|
38
|
+
return { report, parsedSpec };
|
|
23
39
|
}
|
|
24
40
|
//# sourceMappingURL=validate-stage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-stage.js","sourceRoot":"","sources":["../src/validate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"validate-stage.js","sourceRoot":"","sources":["../src/validate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAU9D;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB;IAEhB,MAAM,EAAE,MAAM,EAAE,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAgB;IAEhB,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hstm-labs/forge-spec-parser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"prepublishOnly": "npm run build"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@hstm-labs/forge-common": "0.1.
|
|
19
|
+
"@hstm-labs/forge-common": "0.1.4",
|
|
20
20
|
"remark-frontmatter": "^5.0.0",
|
|
21
21
|
"remark-gfm": "^4.0.1",
|
|
22
22
|
"remark-parse": "^11.0.0",
|