@toolproof-core/schema 1.0.9 → 1.0.11

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.
Files changed (67) hide show
  1. package/dist/generated/artifacts/constants.d.ts +121 -0
  2. package/dist/generated/artifacts/constants.js +121 -0
  3. package/dist/generated/artifacts/mappings.d.ts +23 -0
  4. package/dist/generated/artifacts/mappings.js +23 -0
  5. package/dist/generated/normalized/Genesis.json +117 -78
  6. package/dist/generated/resources/Genesis.json +489 -264
  7. package/dist/generated/schemas/Genesis.json +94 -61
  8. package/dist/generated/schemas/standalone/Job.json +9 -8
  9. package/dist/generated/schemas/standalone/RawStrategy.json +86 -110
  10. package/dist/generated/schemas/standalone/ResourceType.json +4 -4
  11. package/dist/generated/schemas/standalone/RunnableStrategy.json +115 -139
  12. package/dist/generated/schemas/standalone/StrategyRun.json +93 -117
  13. package/dist/generated/types/standalone/Resource_Genesis.d.ts +1 -1
  14. package/dist/generated/types/standalone/Resource_Job.d.ts +1 -1
  15. package/dist/generated/types/standalone/Resource_RawStrategy.d.ts +1 -1
  16. package/dist/generated/types/standalone/Resource_ResourceType.d.ts +1 -1
  17. package/dist/generated/types/standalone/Resource_RunnableStrategy.d.ts +1 -1
  18. package/dist/generated/types/types.d.ts +247 -239
  19. package/dist/index.d.ts +6 -3
  20. package/dist/index.js +5 -2
  21. package/dist/scripts/_lib/config.d.ts +3 -5
  22. package/dist/scripts/_lib/config.js +8 -14
  23. package/dist/scripts/generateConstantsAndMappings.d.ts +31 -0
  24. package/dist/scripts/generateConstantsAndMappings.js +243 -0
  25. package/dist/scripts/generateDependencies.js +1 -1
  26. package/dist/scripts/generateStandaloneType.js +2 -1
  27. package/dist/scripts/generateTerminals.js +2 -2
  28. package/dist/scripts/generateTypes.js +183 -5
  29. package/dist/scripts/wrapResourceTypesWithResourceShells.js +7 -3
  30. package/package.json +9 -10
  31. package/src/Genesis.json +1873 -1833
  32. package/src/generated/artifacts/constants.ts +122 -0
  33. package/src/generated/{dependencies → artifacts}/dependencyMap.json +282 -280
  34. package/src/generated/artifacts/mappings.ts +24 -0
  35. package/src/generated/{dependencies → artifacts}/terminals.json +13 -11
  36. package/src/generated/normalized/Genesis.json +1785 -1746
  37. package/src/generated/resources/Genesis.json +2833 -2608
  38. package/src/generated/schemas/Genesis.json +1348 -1315
  39. package/src/generated/schemas/standalone/Job.json +195 -194
  40. package/src/generated/schemas/standalone/RawStrategy.json +86 -110
  41. package/src/generated/schemas/standalone/ResourceType.json +106 -106
  42. package/src/generated/schemas/standalone/RunnableStrategy.json +645 -669
  43. package/src/generated/schemas/standalone/StrategyRun.json +913 -937
  44. package/src/generated/types/standalone/Resource_Genesis.d.ts +3 -3
  45. package/src/generated/types/standalone/Resource_Job.d.ts +3 -3
  46. package/src/generated/types/standalone/Resource_RawStrategy.d.ts +3 -3
  47. package/src/generated/types/standalone/Resource_ResourceType.d.ts +3 -3
  48. package/src/generated/types/standalone/Resource_RunnableStrategy.d.ts +3 -3
  49. package/src/generated/types/types.d.ts +717 -709
  50. package/src/index.ts +77 -70
  51. package/src/scripts/_lib/config.ts +207 -215
  52. package/src/scripts/extractSchemasFromResourceTypeShells.ts +261 -261
  53. package/src/scripts/generateConstantsAndMappings.ts +309 -0
  54. package/src/scripts/generateDependencies.ts +121 -121
  55. package/src/scripts/generateSchemaShims.ts +127 -127
  56. package/src/scripts/generateStandaloneSchema.ts +185 -185
  57. package/src/scripts/generateStandaloneType.ts +129 -127
  58. package/src/scripts/generateTerminals.ts +73 -73
  59. package/src/scripts/generateTypes.ts +733 -531
  60. package/src/scripts/normalizeAnchorsToPointers.ts +141 -141
  61. package/src/scripts/wrapResourceTypesWithResourceShells.ts +86 -82
  62. package/dist/generated/constants/constants.d.ts +0 -60
  63. package/dist/generated/constants/constants.js +0 -60
  64. package/dist/scripts/generateConstants.d.ts +0 -12
  65. package/dist/scripts/generateConstants.js +0 -179
  66. package/src/generated/constants/constants.ts +0 -61
  67. package/src/scripts/generateConstants.ts +0 -217
@@ -0,0 +1,309 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getConfig } from './_lib/config.js';
4
+
5
+ type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]: JSONValue };
6
+
7
+ export type GeneratedConstants = {
8
+ Names: Record<string, string>;
9
+ Enums: Record<string, Record<string, string>>;
10
+ };
11
+
12
+ export type GeneratedMappings = {
13
+ IdentityNameToIdentityPrefix: Record<string, string>;
14
+ StepKindToStepIdentityPrefix: Record<string, string>;
15
+ };
16
+
17
+ export type DeriveStepKindToStepIdentityPrefixResult = {
18
+ mapping: Record<string, string>;
19
+ missing: Array<{ stepKind: string; identityName: string }>;
20
+ };
21
+
22
+ // PURE: Convert a string enum member into PascalCase.
23
+ // Examples: 'job' -> 'Job', 'graph_start' -> 'GraphStart'
24
+ function toPascalCase(value: string): string {
25
+ return value
26
+ .split(/[^A-Za-z0-9]+/g)
27
+ .filter(Boolean)
28
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
29
+ .join('');
30
+ }
31
+
32
+ // PURE: Attempt to derive a canonical identity prefix from a canonical identity regex pattern.
33
+ function deriveIdentityPrefixFromPattern(pattern: string): string | undefined {
34
+ // Canonical (currently used across schemas): ^PREFIX-.+$
35
+ const match = /^\^([^$]+)\.\+\$$/.exec(pattern);
36
+ if (!match) return undefined;
37
+
38
+ const prefix = match[1];
39
+ if (!prefix || /[`\n\r]/.test(prefix)) return undefined;
40
+
41
+ return prefix;
42
+ }
43
+
44
+ // PURE: Extract all identity prefixes from `$defs/*Identity` string-pattern definitions.
45
+ export function extractIdentityPrefixes(schema: unknown): Record<string, string> {
46
+ if (!schema || typeof schema !== 'object') return {};
47
+
48
+ const defs = (schema as any).$defs;
49
+ if (!defs || typeof defs !== 'object') return {};
50
+
51
+ const defEntries = Object.entries(defs as Record<string, unknown>);
52
+ defEntries.sort(([a], [b]) => a.localeCompare(b));
53
+
54
+ const out: Record<string, string> = {};
55
+
56
+ for (const [defName, defVal] of defEntries) {
57
+ if (!/Identity$/.test(defName)) continue;
58
+ if (!defVal || typeof defVal !== 'object' || Array.isArray(defVal)) continue;
59
+
60
+ const v: any = defVal;
61
+ if (v.type !== 'string') continue;
62
+ if (typeof v.pattern !== 'string') continue;
63
+
64
+ const prefix = deriveIdentityPrefixFromPattern(v.pattern);
65
+ if (!prefix) continue;
66
+
67
+ out[defName] = prefix;
68
+ }
69
+
70
+ return out;
71
+ }
72
+
73
+ // PURE: Extract all subschema names from `$defs/*`.
74
+ // Shape: { JobStep: 'JobStep', StepKind: 'StepKind', ... }
75
+ export function extractSubschemaNames(schema: unknown): Record<string, string> {
76
+ if (!schema || typeof schema !== 'object') return {};
77
+
78
+ const defs = (schema as any).$defs;
79
+ if (!defs || typeof defs !== 'object') return {};
80
+
81
+ const defEntries = Object.entries(defs as Record<string, unknown>);
82
+ defEntries.sort(([a], [b]) => a.localeCompare(b));
83
+
84
+ const out: Record<string, string> = {};
85
+ for (const [defName] of defEntries) {
86
+ if (!defName || /[\n\r`]/.test(defName)) continue;
87
+ out[defName] = defName;
88
+ }
89
+
90
+ return out;
91
+ }
92
+
93
+ // PURE: Extract all string enums from `$defs/*Kind|*Status` definitions.
94
+ // Shape: { StepKind: { job: 'job', ... }, ResourceShellKind: { inputPotential': 'inputPotential', ... } }
95
+ export function extractEnums(schema: unknown): Record<string, Record<string, string>> {
96
+ if (!schema || typeof schema !== 'object') return {};
97
+
98
+ const defs = (schema as any).$defs;
99
+ if (!defs || typeof defs !== 'object') return {};
100
+
101
+ const defEntries = Object.entries(defs as Record<string, unknown>);
102
+ defEntries.sort(([a], [b]) => a.localeCompare(b));
103
+
104
+ const out: Record<string, Record<string, string>> = {};
105
+
106
+ for (const [defName, defVal] of defEntries) {
107
+ if (!/(Kind|Status)$/.test(defName)) continue;
108
+ if (!defVal || typeof defVal !== 'object' || Array.isArray(defVal)) continue;
109
+
110
+ const v: any = defVal;
111
+ if (v.type !== 'string') continue;
112
+ if (!Array.isArray(v.enum) || v.enum.length === 0) continue;
113
+ if (v.enum.some((x: unknown) => typeof x !== 'string')) continue;
114
+
115
+ const members: Record<string, string> = {};
116
+ for (const member of v.enum as readonly string[]) {
117
+ members[member] = member;
118
+ }
119
+
120
+ out[defName] = members;
121
+ }
122
+
123
+ return out;
124
+ }
125
+
126
+ // PURE: Derive a StepKind -> StepIdentityPrefix mapping.
127
+ // Example: { job: 'job' } + IdentityNameToIdentityPrefix['JobStepIdentity']='JOB_STEP-' => { job: 'JOB_STEP-' }
128
+ export function deriveStepKindToStepIdentityPrefix(
129
+ enums: Record<string, Record<string, string>>,
130
+ identityNameToIdentityPrefix: Record<string, string>
131
+ ): DeriveStepKindToStepIdentityPrefixResult {
132
+ const stepKindEnum = enums.StepKind;
133
+ if (!stepKindEnum || typeof stepKindEnum !== 'object') return { mapping: {}, missing: [] };
134
+
135
+ const stepKinds = Object.keys(stepKindEnum).sort((a, b) => a.localeCompare(b));
136
+ const mapping: Record<string, string> = {};
137
+ const missing: Array<{ stepKind: string; identityName: string }> = [];
138
+
139
+ for (const stepKind of stepKinds) {
140
+ const identityName = `${toPascalCase(stepKind)}StepIdentity`;
141
+ const prefix = identityNameToIdentityPrefix[identityName];
142
+ if (!prefix) {
143
+ missing.push({ stepKind, identityName });
144
+ continue;
145
+ }
146
+ mapping[stepKind] = prefix;
147
+ }
148
+
149
+ return { mapping, missing };
150
+ }
151
+
152
+ // PURE: Extract generated CONSTANTS + MAPPINGS from a parsed schema.
153
+ export function extractGeneratedConstantsAndMappings(schema: unknown): {
154
+ CONSTANTS: GeneratedConstants;
155
+ MAPPINGS: GeneratedMappings;
156
+ DIAGNOSTICS: {
157
+ missingStepKindIdentityPrefixes: Array<{ stepKind: string; identityName: string }>;
158
+ };
159
+ } {
160
+ const names = extractSubschemaNames(schema);
161
+ const enums = extractEnums(schema);
162
+ const identityPrefixes = extractIdentityPrefixes(schema);
163
+ const derivedStepKind = deriveStepKindToStepIdentityPrefix(enums, identityPrefixes);
164
+
165
+ return {
166
+ CONSTANTS: {
167
+ Names: names,
168
+ Enums: enums,
169
+ },
170
+ MAPPINGS: {
171
+ IdentityNameToIdentityPrefix: identityPrefixes,
172
+ StepKindToStepIdentityPrefix: derivedStepKind.mapping,
173
+ },
174
+ DIAGNOSTICS: {
175
+ missingStepKindIdentityPrefixes: derivedStepKind.missing,
176
+ },
177
+ };
178
+ }
179
+
180
+ // PURE: Render helpers for TS keys/strings.
181
+ function escapeTsString(value: string): string {
182
+ return value
183
+ .replace(/\\/g, '\\\\')
184
+ .replace(/\r/g, '\\r')
185
+ .replace(/\n/g, '\\n')
186
+ .replace(/\t/g, '\\t')
187
+ .replace(/'/g, "\\'");
188
+ }
189
+
190
+ function renderTsStringLiteral(value: string): string {
191
+ return `'${escapeTsString(value)}'`;
192
+ }
193
+
194
+ function isValidTsIdentifier(key: string): boolean {
195
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
196
+ }
197
+
198
+ function renderTsKey(key: string): string {
199
+ return isValidTsIdentifier(key) ? key : renderTsStringLiteral(key);
200
+ }
201
+
202
+ // PURE: Render constants TypeScript source.
203
+ export function renderConstantsTs(constants: GeneratedConstants): string {
204
+ const nameKeys = Object.keys(constants.Names).sort((a, b) => a.localeCompare(b));
205
+ const enumKeys = Object.keys(constants.Enums).sort((a, b) => a.localeCompare(b));
206
+
207
+ const lines: string[] = [];
208
+ lines.push('const CONSTANTS = {');
209
+ lines.push(' Names: {');
210
+
211
+ for (const key of nameKeys) {
212
+ const value = constants.Names[key] ?? '';
213
+ lines.push(` ${renderTsKey(key)}: ${renderTsStringLiteral(value)},`);
214
+ }
215
+
216
+ lines.push(' },');
217
+ lines.push(' Enums: {');
218
+
219
+ for (const key of enumKeys) {
220
+ const members = constants.Enums[key] ?? {};
221
+ lines.push(` ${renderTsKey(key)}: {`);
222
+
223
+ for (const memberKey of Object.keys(members)) {
224
+ const value = members[memberKey] ?? '';
225
+ lines.push(` ${renderTsKey(memberKey)}: ${renderTsStringLiteral(value)},`);
226
+ }
227
+
228
+ lines.push(' },');
229
+ }
230
+
231
+ lines.push(' }');
232
+ lines.push('} as const;');
233
+ lines.push('');
234
+ lines.push('export default CONSTANTS;');
235
+ lines.push('');
236
+
237
+ return lines.join('\n');
238
+ }
239
+
240
+ // PURE: Render mappings TypeScript source.
241
+ export function renderMappingsTs(mappings: GeneratedMappings): string {
242
+ const identityKeys = Object.keys(mappings.IdentityNameToIdentityPrefix).sort((a, b) => a.localeCompare(b));
243
+ const stepKindKeys = Object.keys(mappings.StepKindToStepIdentityPrefix).sort((a, b) => a.localeCompare(b));
244
+
245
+ const lines: string[] = [];
246
+ lines.push('const MAPPINGS = {');
247
+ lines.push(' IdentityNameToIdentityPrefix: {');
248
+
249
+ for (const key of identityKeys) {
250
+ const value = mappings.IdentityNameToIdentityPrefix[key] ?? '';
251
+ lines.push(` ${renderTsKey(key)}: ${renderTsStringLiteral(value)},`);
252
+ }
253
+
254
+ lines.push(' },');
255
+ lines.push(' StepKindToStepIdentityPrefix: {');
256
+
257
+ for (const key of stepKindKeys) {
258
+ const value = mappings.StepKindToStepIdentityPrefix[key] ?? '';
259
+ lines.push(` ${renderTsKey(key)}: ${renderTsStringLiteral(value)},`);
260
+ }
261
+
262
+ lines.push(' },');
263
+ lines.push('} as const;');
264
+ lines.push('');
265
+ lines.push('export default MAPPINGS;');
266
+ lines.push('');
267
+
268
+ return lines.join('\n');
269
+ }
270
+
271
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
272
+ function main() {
273
+ try {
274
+ const config = getConfig();
275
+
276
+ const inPath = config.getSchemaPath('Genesis.json');
277
+ const outConstantsPath = config.getArtifactsPath('constants.ts');
278
+ const outMappingsPath = config.getArtifactsPath('mappings.ts');
279
+
280
+ if (!fs.existsSync(inPath)) {
281
+ throw new Error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
282
+ }
283
+
284
+ const raw = fs.readFileSync(inPath, 'utf8');
285
+ const doc: JSONValue = JSON.parse(raw);
286
+
287
+ const { CONSTANTS, MAPPINGS, DIAGNOSTICS } = extractGeneratedConstantsAndMappings(doc);
288
+ if (DIAGNOSTICS.missingStepKindIdentityPrefixes.length) {
289
+ const rows = DIAGNOSTICS.missingStepKindIdentityPrefixes
290
+ .map(({ stepKind, identityName }) => `${stepKind} -> ${identityName}`)
291
+ .join(', ');
292
+ throw new Error(`Missing IdentityNameToIdentityPrefix entries required for StepKindToStepIdentityPrefix: ${rows}`);
293
+ }
294
+
295
+ const constantsTs = renderConstantsTs(CONSTANTS);
296
+ const mappingsTs = renderMappingsTs(MAPPINGS);
297
+
298
+ fs.mkdirSync(path.dirname(outConstantsPath), { recursive: true });
299
+ fs.writeFileSync(outConstantsPath, constantsTs, 'utf8');
300
+ fs.writeFileSync(outMappingsPath, mappingsTs, 'utf8');
301
+ console.log(`Wrote constants to ${outConstantsPath}`);
302
+ console.log(`Wrote mappings to ${outMappingsPath}`);
303
+ } catch (error: any) {
304
+ console.error(`Error generating constants/mappings: ${error?.message ?? error}`);
305
+ process.exitCode = 1;
306
+ }
307
+ }
308
+
309
+ main();
@@ -1,121 +1,121 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { getConfig } from "./_lib/config.js";
4
-
5
- type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]: JSONValue };
6
-
7
- // PURE: Decode a single JSON Pointer segment.
8
- function decodeJsonPointerSegment(segment: string): string {
9
- // JSON Pointer decoding: ~1 => / and ~0 => ~
10
- return segment.replace(/~1/g, "/").replace(/~0/g, "~");
11
- }
12
-
13
- // PURE: Collect all `$ref` strings from a JSON-ish tree.
14
- function collectRefs(node: unknown, out: Set<string>): void {
15
- if (Array.isArray(node)) {
16
- for (const item of node) collectRefs(item, out);
17
- return;
18
- }
19
- if (!node || typeof node !== "object") return;
20
-
21
- const obj = node as Record<string, unknown>;
22
- const ref = obj["$ref"];
23
- if (typeof ref === "string") out.add(ref);
24
-
25
- for (const value of Object.values(obj)) {
26
- collectRefs(value, out);
27
- }
28
- }
29
-
30
- // PURE: Resolve an internal ref (pointer or anchor) to a root $defs key, if possible.
31
- function resolveInternalRefToDefKey(ref: string, defKeys: Set<string>, anchorToDef: Record<string, string>): string | null {
32
- if (!ref.startsWith("#")) return null;
33
-
34
- // JSON Pointer: #/$defs/<Name>(/...)
35
- const defsPrefix = "#/$defs/";
36
- if (ref.startsWith(defsPrefix)) {
37
- const rest = ref.slice(defsPrefix.length);
38
- const firstSegment = rest.split("/")[0] ?? "";
39
- const defKey = decodeJsonPointerSegment(firstSegment);
40
- return defKeys.has(defKey) ? defKey : null;
41
- }
42
-
43
- // Anchor ref: #AnchorName
44
- if (!ref.startsWith("#/")) {
45
- const anchor = ref.slice(1);
46
- const mapped = anchorToDef[anchor];
47
- if (mapped && defKeys.has(mapped)) return mapped;
48
- if (defKeys.has(anchor)) return anchor;
49
- }
50
-
51
- return null;
52
- }
53
-
54
- /**
55
- * Pure function that generates a dependency map from a JSON Schema document.
56
- *
57
- * @param doc The source JSON Schema document
58
- * @returns A record mapping definition names to their dependency lists
59
- */
60
- // PURE: Generate a $defs dependency map from a JSON Schema document (no I/O).
61
- function generateDependencyMapLogic(doc: any): Record<string, string[]> {
62
- const defs: Record<string, JSONValue> = doc?.$defs && typeof doc.$defs === "object" ? doc.$defs : {};
63
- const defKeys = new Set(Object.keys(defs));
64
-
65
- // Map anchors to $defs keys (useful if any anchor-style refs remain)
66
- const anchorToDef: Record<string, string> = {};
67
- for (const [defKey, defSchema] of Object.entries(defs)) {
68
- if (!defSchema || typeof defSchema !== "object" || Array.isArray(defSchema)) continue;
69
- const anchor = (defSchema as any).$anchor;
70
- if (typeof anchor === "string" && !(anchor in anchorToDef)) {
71
- anchorToDef[anchor] = defKey;
72
- }
73
- }
74
-
75
- const dependencyMap: Record<string, string[]> = {};
76
-
77
- for (const [defKey, defSchema] of Object.entries(defs)) {
78
- const refs = new Set<string>();
79
- collectRefs(defSchema, refs);
80
-
81
- const deps = new Set<string>();
82
- for (const ref of refs) {
83
- const depKey = resolveInternalRefToDefKey(ref, defKeys, anchorToDef);
84
- if (!depKey) continue;
85
- if (depKey === defKey) continue;
86
- deps.add(depKey);
87
- }
88
-
89
- dependencyMap[defKey] = Array.from(deps);
90
- }
91
-
92
- return dependencyMap;
93
- }
94
-
95
- // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
96
- function main() {
97
- try {
98
- const config = getConfig();
99
-
100
- const inPath = config.getSchemaPath("Genesis.json");
101
- const outPath = config.getDependencyMapPath();
102
-
103
- if (!fs.existsSync(inPath)) {
104
- throw new Error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
105
- }
106
-
107
- const raw = fs.readFileSync(inPath, "utf8");
108
- const doc = JSON.parse(raw);
109
-
110
- const dependencyMap = generateDependencyMapLogic(doc);
111
-
112
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
113
- fs.writeFileSync(outPath, JSON.stringify(dependencyMap, null, 4), "utf8");
114
- console.log(`Wrote dependency map to ${outPath}`);
115
- } catch (error: any) {
116
- console.error(`Error generating dependency map: ${error?.message ?? error}`);
117
- process.exitCode = 1;
118
- }
119
- }
120
-
121
- main();
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { getConfig } from "./_lib/config.js";
4
+
5
+ type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]: JSONValue };
6
+
7
+ // PURE: Decode a single JSON Pointer segment.
8
+ function decodeJsonPointerSegment(segment: string): string {
9
+ // JSON Pointer decoding: ~1 => / and ~0 => ~
10
+ return segment.replace(/~1/g, "/").replace(/~0/g, "~");
11
+ }
12
+
13
+ // PURE: Collect all `$ref` strings from a JSON-ish tree.
14
+ function collectRefs(node: unknown, out: Set<string>): void {
15
+ if (Array.isArray(node)) {
16
+ for (const item of node) collectRefs(item, out);
17
+ return;
18
+ }
19
+ if (!node || typeof node !== "object") return;
20
+
21
+ const obj = node as Record<string, unknown>;
22
+ const ref = obj["$ref"];
23
+ if (typeof ref === "string") out.add(ref);
24
+
25
+ for (const value of Object.values(obj)) {
26
+ collectRefs(value, out);
27
+ }
28
+ }
29
+
30
+ // PURE: Resolve an internal ref (pointer or anchor) to a root $defs key, if possible.
31
+ function resolveInternalRefToDefKey(ref: string, defKeys: Set<string>, anchorToDef: Record<string, string>): string | null {
32
+ if (!ref.startsWith("#")) return null;
33
+
34
+ // JSON Pointer: #/$defs/<Name>(/...)
35
+ const defsPrefix = "#/$defs/";
36
+ if (ref.startsWith(defsPrefix)) {
37
+ const rest = ref.slice(defsPrefix.length);
38
+ const firstSegment = rest.split("/")[0] ?? "";
39
+ const defKey = decodeJsonPointerSegment(firstSegment);
40
+ return defKeys.has(defKey) ? defKey : null;
41
+ }
42
+
43
+ // Anchor ref: #AnchorName
44
+ if (!ref.startsWith("#/")) {
45
+ const anchor = ref.slice(1);
46
+ const mapped = anchorToDef[anchor];
47
+ if (mapped && defKeys.has(mapped)) return mapped;
48
+ if (defKeys.has(anchor)) return anchor;
49
+ }
50
+
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Pure function that generates a dependency map from a JSON Schema document.
56
+ *
57
+ * @param doc The source JSON Schema document
58
+ * @returns A record mapping definition names to their dependency lists
59
+ */
60
+ // PURE: Generate a $defs dependency map from a JSON Schema document (no I/O).
61
+ function generateDependencyMapLogic(doc: any): Record<string, string[]> {
62
+ const defs: Record<string, JSONValue> = doc?.$defs && typeof doc.$defs === "object" ? doc.$defs : {};
63
+ const defKeys = new Set(Object.keys(defs));
64
+
65
+ // Map anchors to $defs keys (useful if any anchor-style refs remain)
66
+ const anchorToDef: Record<string, string> = {};
67
+ for (const [defKey, defSchema] of Object.entries(defs)) {
68
+ if (!defSchema || typeof defSchema !== "object" || Array.isArray(defSchema)) continue;
69
+ const anchor = (defSchema as any).$anchor;
70
+ if (typeof anchor === "string" && !(anchor in anchorToDef)) {
71
+ anchorToDef[anchor] = defKey;
72
+ }
73
+ }
74
+
75
+ const dependencyMap: Record<string, string[]> = {};
76
+
77
+ for (const [defKey, defSchema] of Object.entries(defs)) {
78
+ const refs = new Set<string>();
79
+ collectRefs(defSchema, refs);
80
+
81
+ const deps = new Set<string>();
82
+ for (const ref of refs) {
83
+ const depKey = resolveInternalRefToDefKey(ref, defKeys, anchorToDef);
84
+ if (!depKey) continue;
85
+ if (depKey === defKey) continue;
86
+ deps.add(depKey);
87
+ }
88
+
89
+ dependencyMap[defKey] = Array.from(deps);
90
+ }
91
+
92
+ return dependencyMap;
93
+ }
94
+
95
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
96
+ function main() {
97
+ try {
98
+ const config = getConfig();
99
+
100
+ const inPath = config.getSchemaPath("Genesis.json");
101
+ const outPath = config.getArtifactsPath("dependencyMap.json");
102
+
103
+ if (!fs.existsSync(inPath)) {
104
+ throw new Error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
105
+ }
106
+
107
+ const raw = fs.readFileSync(inPath, "utf8");
108
+ const doc = JSON.parse(raw);
109
+
110
+ const dependencyMap = generateDependencyMapLogic(doc);
111
+
112
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
113
+ fs.writeFileSync(outPath, JSON.stringify(dependencyMap, null, 4), "utf8");
114
+ console.log(`Wrote dependency map to ${outPath}`);
115
+ } catch (error: any) {
116
+ console.error(`Error generating dependency map: ${error?.message ?? error}`);
117
+ process.exitCode = 1;
118
+ }
119
+ }
120
+
121
+ main();