@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
@@ -1,127 +1,127 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getConfig } from './_lib/config.js';
4
-
5
- /**
6
- * Generate TypeScript shim files for all JSON schemas and resources
7
- *
8
- * Creates .ts files that import and re-export .json files using
9
- * the JSON import assertion syntax. This enables proper dependency resolution
10
- * and type inference when importing schemas in TypeScript.
11
- *
12
- * Generates shims for:
13
- * - schemas dir: config.getSchemasDir() (schema files)
14
- * - standalone schemas dir: config.getStandaloneSchemaDir() (standalone schema files)
15
- * - resources dir: config.getResourcesDir() (resource envelope files)
16
- *
17
- * Usage: node ./dist/scripts/generateSchemaShims.js
18
- */
19
-
20
- // PURE: Generate the .ts shim module content for a given .json filename.
21
- function generateShimContent(jsonFile: string, variableName: string): string {
22
- return `import ${variableName} from './${jsonFile}' with { type: 'json' };\nexport default ${variableName};\n`;
23
- }
24
-
25
- // PURE: Map JSON filenames to deterministic shim file contents.
26
- function getShimsForFiles(jsonFiles: string[], variableName: string): Record<string, string> {
27
- const shims: Record<string, string> = {};
28
- for (const jsonFile of jsonFiles) {
29
- const baseName = path.basename(jsonFile, '.json');
30
- const tsFile = `${baseName}.ts`;
31
- shims[tsFile] = generateShimContent(jsonFile, variableName);
32
- }
33
- return shims;
34
- }
35
-
36
- // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
37
- function main() {
38
- try {
39
- const config = getConfig();
40
- const schemasDir = config.getSchemasDir();
41
- const standaloneSchemasDir = config.getStandaloneSchemaDir();
42
- const resourcesDir = config.getResourcesDir();
43
- const generatedResourceTypesDir = config.getNormalizedDir();
44
-
45
- let totalCount = 0;
46
-
47
- // Process schemas directory
48
- if (fs.existsSync(schemasDir)) {
49
- const files = fs.readdirSync(schemasDir);
50
- const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
51
-
52
- const shims = getShimsForFiles(jsonFiles, 'schema');
53
-
54
- for (const [tsFile, content] of Object.entries(shims)) {
55
- const tsPath = path.join(schemasDir, tsFile);
56
- fs.writeFileSync(tsPath, content, 'utf-8');
57
- console.log('Generated ' + tsFile + ' in ' + schemasDir);
58
- totalCount++;
59
- }
60
- console.log('Generated ' + jsonFiles.length + ' TypeScript schema shims in ' + schemasDir);
61
- } else {
62
- console.warn('Schemas directory not found at ' + schemasDir);
63
- }
64
-
65
- // Process standalone schemas directory
66
- if (fs.existsSync(standaloneSchemasDir)) {
67
- const files = fs.readdirSync(standaloneSchemasDir);
68
- const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
69
-
70
- const shims = getShimsForFiles(jsonFiles, 'schema');
71
-
72
- for (const [tsFile, content] of Object.entries(shims)) {
73
- const tsPath = path.join(standaloneSchemasDir, tsFile);
74
- fs.writeFileSync(tsPath, content, 'utf-8');
75
- console.log('Generated ' + tsFile + ' in ' + standaloneSchemasDir);
76
- totalCount++;
77
- }
78
- console.log('Generated ' + jsonFiles.length + ' TypeScript standalone schema shims in ' + standaloneSchemasDir);
79
- } else {
80
- // Standalone schemas are optional
81
- }
82
-
83
- // Process resources directory
84
- if (fs.existsSync(resourcesDir)) {
85
- const files = fs.readdirSync(resourcesDir);
86
- const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
87
-
88
- const shims = getShimsForFiles(jsonFiles, 'resources');
89
-
90
- for (const [tsFile, content] of Object.entries(shims)) {
91
- const tsPath = path.join(resourcesDir, tsFile);
92
- fs.writeFileSync(tsPath, content, 'utf-8');
93
- console.log('Generated ' + tsFile + ' in ' + resourcesDir);
94
- totalCount++;
95
- }
96
- console.log('Generated ' + jsonFiles.length + ' TypeScript resource shims in ' + resourcesDir);
97
- } else {
98
- console.warn('Resources directory not found at ' + resourcesDir);
99
- }
100
-
101
- // Genesis (normalized) shim
102
- try {
103
- const genesisJsonPath = config.getNormalizedSourcePath();
104
- if (fs.existsSync(genesisJsonPath)) {
105
- fs.mkdirSync(generatedResourceTypesDir, { recursive: true });
106
- const tsFile = 'Genesis.ts';
107
- const content = generateShimContent('Genesis.json', 'schema');
108
- const tsPath = path.join(generatedResourceTypesDir, tsFile);
109
-
110
- fs.writeFileSync(tsPath, content, 'utf-8');
111
- console.log('Generated ' + tsFile + ' in ' + generatedResourceTypesDir);
112
- totalCount++;
113
- } else {
114
- console.warn('Genesis source JSON not found at ' + genesisJsonPath + '; skipping Genesis.ts shim');
115
- }
116
- } catch (e) {
117
- console.warn('Failed to generate Genesis.ts shim:', e);
118
- }
119
-
120
- console.log('Generated ' + totalCount + ' total TypeScript shims');
121
- } catch (e) {
122
- console.error(e);
123
- process.exitCode = 1;
124
- }
125
- }
126
-
127
- main();
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getConfig } from './_lib/config.js';
4
+
5
+ /**
6
+ * Generate TypeScript shim files for all JSON schemas and resources
7
+ *
8
+ * Creates .ts files that import and re-export .json files using
9
+ * the JSON import assertion syntax. This enables proper dependency resolution
10
+ * and type inference when importing schemas in TypeScript.
11
+ *
12
+ * Generates shims for:
13
+ * - schemas dir: config.getSchemasDir() (schema files)
14
+ * - standalone schemas dir: config.getStandaloneSchemaDir() (standalone schema files)
15
+ * - resources dir: config.getResourcesDir() (resource envelope files)
16
+ *
17
+ * Usage: node ./dist/scripts/generateSchemaShims.js
18
+ */
19
+
20
+ // PURE: Generate the .ts shim module content for a given .json filename.
21
+ function generateShimContent(jsonFile: string, variableName: string): string {
22
+ return `import ${variableName} from './${jsonFile}' with { type: 'json' };\nexport default ${variableName};\n`;
23
+ }
24
+
25
+ // PURE: Map JSON filenames to deterministic shim file contents.
26
+ function getShimsForFiles(jsonFiles: string[], variableName: string): Record<string, string> {
27
+ const shims: Record<string, string> = {};
28
+ for (const jsonFile of jsonFiles) {
29
+ const baseName = path.basename(jsonFile, '.json');
30
+ const tsFile = `${baseName}.ts`;
31
+ shims[tsFile] = generateShimContent(jsonFile, variableName);
32
+ }
33
+ return shims;
34
+ }
35
+
36
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
37
+ function main() {
38
+ try {
39
+ const config = getConfig();
40
+ const schemasDir = config.getSchemasDir();
41
+ const standaloneSchemasDir = config.getStandaloneSchemaDir();
42
+ const resourcesDir = config.getResourcesDir();
43
+ const generatedResourceTypesDir = config.getNormalizedDir();
44
+
45
+ let totalCount = 0;
46
+
47
+ // Process schemas directory
48
+ if (fs.existsSync(schemasDir)) {
49
+ const files = fs.readdirSync(schemasDir);
50
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
51
+
52
+ const shims = getShimsForFiles(jsonFiles, 'schema');
53
+
54
+ for (const [tsFile, content] of Object.entries(shims)) {
55
+ const tsPath = path.join(schemasDir, tsFile);
56
+ fs.writeFileSync(tsPath, content, 'utf-8');
57
+ console.log('Generated ' + tsFile + ' in ' + schemasDir);
58
+ totalCount++;
59
+ }
60
+ console.log('Generated ' + jsonFiles.length + ' TypeScript schema shims in ' + schemasDir);
61
+ } else {
62
+ console.warn('Schemas directory not found at ' + schemasDir);
63
+ }
64
+
65
+ // Process standalone schemas directory
66
+ if (fs.existsSync(standaloneSchemasDir)) {
67
+ const files = fs.readdirSync(standaloneSchemasDir);
68
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
69
+
70
+ const shims = getShimsForFiles(jsonFiles, 'schema');
71
+
72
+ for (const [tsFile, content] of Object.entries(shims)) {
73
+ const tsPath = path.join(standaloneSchemasDir, tsFile);
74
+ fs.writeFileSync(tsPath, content, 'utf-8');
75
+ console.log('Generated ' + tsFile + ' in ' + standaloneSchemasDir);
76
+ totalCount++;
77
+ }
78
+ console.log('Generated ' + jsonFiles.length + ' TypeScript standalone schema shims in ' + standaloneSchemasDir);
79
+ } else {
80
+ // Standalone schemas are optional
81
+ }
82
+
83
+ // Process resources directory
84
+ if (fs.existsSync(resourcesDir)) {
85
+ const files = fs.readdirSync(resourcesDir);
86
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
87
+
88
+ const shims = getShimsForFiles(jsonFiles, 'resources');
89
+
90
+ for (const [tsFile, content] of Object.entries(shims)) {
91
+ const tsPath = path.join(resourcesDir, tsFile);
92
+ fs.writeFileSync(tsPath, content, 'utf-8');
93
+ console.log('Generated ' + tsFile + ' in ' + resourcesDir);
94
+ totalCount++;
95
+ }
96
+ console.log('Generated ' + jsonFiles.length + ' TypeScript resource shims in ' + resourcesDir);
97
+ } else {
98
+ console.warn('Resources directory not found at ' + resourcesDir);
99
+ }
100
+
101
+ // Genesis (normalized) shim
102
+ try {
103
+ const genesisJsonPath = config.getNormalizedSourcePath();
104
+ if (fs.existsSync(genesisJsonPath)) {
105
+ fs.mkdirSync(generatedResourceTypesDir, { recursive: true });
106
+ const tsFile = 'Genesis.ts';
107
+ const content = generateShimContent('Genesis.json', 'schema');
108
+ const tsPath = path.join(generatedResourceTypesDir, tsFile);
109
+
110
+ fs.writeFileSync(tsPath, content, 'utf-8');
111
+ console.log('Generated ' + tsFile + ' in ' + generatedResourceTypesDir);
112
+ totalCount++;
113
+ } else {
114
+ console.warn('Genesis source JSON not found at ' + genesisJsonPath + '; skipping Genesis.ts shim');
115
+ }
116
+ } catch (e) {
117
+ console.warn('Failed to generate Genesis.ts shim:', e);
118
+ }
119
+
120
+ console.log('Generated ' + totalCount + ' total TypeScript shims');
121
+ } catch (e) {
122
+ console.error(e);
123
+ process.exitCode = 1;
124
+ }
125
+ }
126
+
127
+ main();
@@ -1,185 +1,185 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getConfig } from './_lib/config.js';
4
-
5
- /**
6
- * Create a standalone JSON Schema for a single $defs entry under Genesis.json,
7
- * embedding all direct & transitive local $defs dependencies.
8
- *
9
- * Usage:
10
- * node ./dist/scripts/generateStandaloneSchema.js --name <DefName>
11
- */
12
-
13
- // PURE: Extract a standalone subschema from a flattened Genesis schema, returning warnings instead of logging
14
- function extractStandaloneSchemaLogic(genesis: any, name: string): { schema: any; warnings: string[] } {
15
- const rootDefs: Record<string, any> | undefined = genesis.$defs;
16
- if (!rootDefs || typeof rootDefs !== 'object') {
17
- throw new Error('No $defs object found in Genesis.json');
18
- }
19
-
20
- const target = rootDefs[name];
21
- if (!target) {
22
- throw new Error(`Subschema named '${name}' not found under $defs in Genesis.json`);
23
- }
24
-
25
- const needed = collectLocalDefClosure(target, rootDefs);
26
-
27
- const targetClone = deepClone(target);
28
- const defsOut: Record<string, any> = {};
29
- const warnings: string[] = [];
30
- for (const defName of needed) {
31
- if (defName === name) continue;
32
- const defSchema = rootDefs[defName];
33
- if (defSchema === undefined) {
34
- warnings.push(`Warning: referenced def '${defName}' missing in root $defs (skipped).`);
35
- continue;
36
- }
37
- defsOut[defName] = deepClone(defSchema);
38
- }
39
-
40
- const existingInner = isObject(targetClone.$defs) ? targetClone.$defs : {};
41
- targetClone.$defs = { ...existingInner, ...defsOut };
42
-
43
- return { schema: targetClone, warnings };
44
- }
45
-
46
- // PURE: Strip accidental surrounding quotes from CLI values (PowerShell/cmd).
47
- function stripSurroundingQuotes(value: string): string {
48
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
49
- return value.slice(1, -1);
50
- }
51
- return value;
52
- }
53
-
54
- // PURE: Parse CLI args (no defaults, no filesystem probing).
55
- function parseArgs(args: string[]): { name?: string } {
56
- let name: string | undefined;
57
- for (let i = 0; i < args.length; i++) {
58
- const a = args[i];
59
- if (a === '--name') {
60
- name = args[i + 1];
61
- i++;
62
- } else if (a.startsWith('--name=')) {
63
- name = a.split('=')[1];
64
- }
65
- }
66
- const normalized = typeof name === 'string' ? stripSurroundingQuotes(name) : undefined;
67
- return { name: normalized?.trim() ? normalized : undefined };
68
- }
69
-
70
- // PURE: Narrow unknown values to plain objects.
71
- function isObject(v: any): v is Record<string, any> {
72
- return v !== null && typeof v === 'object' && !Array.isArray(v);
73
- }
74
-
75
- // PURE: Deep-clone JSON-ish values (no mutation of input).
76
- function deepClone<T>(v: T): T {
77
- if (Array.isArray(v)) return v.map((x) => deepClone(x)) as any;
78
- if (isObject(v)) {
79
- const out: Record<string, any> = {};
80
- for (const k of Object.keys(v)) out[k] = deepClone((v as any)[k]);
81
- return out as any;
82
- }
83
- return v;
84
- }
85
-
86
- // PURE: Extract a $defs key from a local JSON pointer ref.
87
- function extractPointerDefName(ref: string): string | null {
88
- if (!ref || !ref.startsWith('#/')) return null;
89
- const parts = ref.slice(2).split('/');
90
- if (parts.length !== 2) return null;
91
- if (parts[0] !== '$defs') return null;
92
- const name = parts[1].replace(/~1/g, '/').replace(/~0/g, '~');
93
- return name;
94
- }
95
-
96
- // PURE: Resolve an internal ref (pointer or anchor) to a root $defs key.
97
- function resolveRefToDefName(ref: string, rootDefs: Record<string, any>): string | null {
98
- if (!ref) return null;
99
- const byPointer = extractPointerDefName(ref);
100
- if (byPointer) return byPointer;
101
-
102
- if (ref.startsWith('#') && !ref.startsWith('#/')) {
103
- const anchor = ref.slice(1);
104
- if (!anchor) return null;
105
- for (const [defName, defSchema] of Object.entries(rootDefs)) {
106
- if (!defSchema || typeof defSchema !== 'object') continue;
107
- const topLevelAnchor = (defSchema as any).$anchor;
108
- const nested = (defSchema as any).nucleusSchema;
109
- const nestedAnchor = nested && typeof nested === 'object' ? nested.$anchor : undefined;
110
- const defAnchor = typeof topLevelAnchor === 'string' ? topLevelAnchor : (typeof nestedAnchor === 'string' ? nestedAnchor : undefined);
111
- if (defAnchor === anchor) {
112
- return defName;
113
- }
114
- }
115
- }
116
-
117
- return null;
118
- }
119
-
120
- // PURE: Compute the transitive closure of local $defs dependencies for a node.
121
- function collectLocalDefClosure(node: any, rootDefs: Record<string, any>): Set<string> {
122
- const needed = new Set<string>();
123
- const queue: string[] = [];
124
-
125
- function visit(n: any) {
126
- if (Array.isArray(n)) {
127
- for (const item of n) visit(item);
128
- return;
129
- }
130
- if (!isObject(n)) return;
131
- if (typeof n.$ref === 'string') {
132
- const name = resolveRefToDefName(n.$ref, rootDefs);
133
- if (name && !needed.has(name)) {
134
- needed.add(name);
135
- queue.push(name);
136
- }
137
- }
138
- for (const val of Object.values(n)) visit(val);
139
- }
140
-
141
- visit(node);
142
-
143
- while (queue.length > 0) {
144
- const name = queue.shift()!;
145
- const def = rootDefs[name];
146
- if (!def) continue;
147
- visit(def);
148
- }
149
-
150
- return needed;
151
- }
152
-
153
- // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
154
- async function main() {
155
- try {
156
- const config = getConfig();
157
- const { name } = parseArgs(process.argv.slice(2));
158
- if (!name) {
159
- throw new Error('Missing --name <DefName> argument');
160
- }
161
-
162
- const schemasDir = config.getSchemasDir();
163
- const genesisPath = path.join(schemasDir, config.getSourceFile());
164
- const outPath = config.getStandaloneSchemaPath(`${name}.json`);
165
-
166
- if (!fs.existsSync(genesisPath)) {
167
- throw new Error(`Genesis.json not found at ${genesisPath}. Run extractSchemasFromResourceTypeShells first.`);
168
- }
169
-
170
- const raw = fs.readFileSync(genesisPath, 'utf-8');
171
- const genesis = JSON.parse(raw);
172
-
173
- const { schema, warnings } = extractStandaloneSchemaLogic(genesis, name);
174
- for (const w of warnings) console.warn(w);
175
-
176
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
177
- fs.writeFileSync(outPath, JSON.stringify(schema, null, 2) + '\n');
178
- console.log(`Created standalone subschema '${name}' -> ${outPath}`);
179
- } catch (error: any) {
180
- console.error(error?.message ?? error);
181
- process.exitCode = 1;
182
- }
183
- }
184
-
185
- void main();
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getConfig } from './_lib/config.js';
4
+
5
+ /**
6
+ * Create a standalone JSON Schema for a single $defs entry under Genesis.json,
7
+ * embedding all direct & transitive local $defs dependencies.
8
+ *
9
+ * Usage:
10
+ * node ./dist/scripts/generateStandaloneSchema.js --name <DefName>
11
+ */
12
+
13
+ // PURE: Extract a standalone subschema from a flattened Genesis schema, returning warnings instead of logging
14
+ function extractStandaloneSchemaLogic(genesis: any, name: string): { schema: any; warnings: string[] } {
15
+ const rootDefs: Record<string, any> | undefined = genesis.$defs;
16
+ if (!rootDefs || typeof rootDefs !== 'object') {
17
+ throw new Error('No $defs object found in Genesis.json');
18
+ }
19
+
20
+ const target = rootDefs[name];
21
+ if (!target) {
22
+ throw new Error(`Subschema named '${name}' not found under $defs in Genesis.json`);
23
+ }
24
+
25
+ const needed = collectLocalDefClosure(target, rootDefs);
26
+
27
+ const targetClone = deepClone(target);
28
+ const defsOut: Record<string, any> = {};
29
+ const warnings: string[] = [];
30
+ for (const defName of needed) {
31
+ if (defName === name) continue;
32
+ const defSchema = rootDefs[defName];
33
+ if (defSchema === undefined) {
34
+ warnings.push(`Warning: referenced def '${defName}' missing in root $defs (skipped).`);
35
+ continue;
36
+ }
37
+ defsOut[defName] = deepClone(defSchema);
38
+ }
39
+
40
+ const existingInner = isObject(targetClone.$defs) ? targetClone.$defs : {};
41
+ targetClone.$defs = { ...existingInner, ...defsOut };
42
+
43
+ return { schema: targetClone, warnings };
44
+ }
45
+
46
+ // PURE: Strip accidental surrounding quotes from CLI values (PowerShell/cmd).
47
+ function stripSurroundingQuotes(value: string): string {
48
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
49
+ return value.slice(1, -1);
50
+ }
51
+ return value;
52
+ }
53
+
54
+ // PURE: Parse CLI args (no defaults, no filesystem probing).
55
+ function parseArgs(args: string[]): { name?: string } {
56
+ let name: string | undefined;
57
+ for (let i = 0; i < args.length; i++) {
58
+ const a = args[i];
59
+ if (a === '--name') {
60
+ name = args[i + 1];
61
+ i++;
62
+ } else if (a.startsWith('--name=')) {
63
+ name = a.split('=')[1];
64
+ }
65
+ }
66
+ const normalized = typeof name === 'string' ? stripSurroundingQuotes(name) : undefined;
67
+ return { name: normalized?.trim() ? normalized : undefined };
68
+ }
69
+
70
+ // PURE: Narrow unknown values to plain objects.
71
+ function isObject(v: any): v is Record<string, any> {
72
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
73
+ }
74
+
75
+ // PURE: Deep-clone JSON-ish values (no mutation of input).
76
+ function deepClone<T>(v: T): T {
77
+ if (Array.isArray(v)) return v.map((x) => deepClone(x)) as any;
78
+ if (isObject(v)) {
79
+ const out: Record<string, any> = {};
80
+ for (const k of Object.keys(v)) out[k] = deepClone((v as any)[k]);
81
+ return out as any;
82
+ }
83
+ return v;
84
+ }
85
+
86
+ // PURE: Extract a $defs key from a local JSON pointer ref.
87
+ function extractPointerDefName(ref: string): string | null {
88
+ if (!ref || !ref.startsWith('#/')) return null;
89
+ const parts = ref.slice(2).split('/');
90
+ if (parts.length !== 2) return null;
91
+ if (parts[0] !== '$defs') return null;
92
+ const name = parts[1].replace(/~1/g, '/').replace(/~0/g, '~');
93
+ return name;
94
+ }
95
+
96
+ // PURE: Resolve an internal ref (pointer or anchor) to a root $defs key.
97
+ function resolveRefToDefName(ref: string, rootDefs: Record<string, any>): string | null {
98
+ if (!ref) return null;
99
+ const byPointer = extractPointerDefName(ref);
100
+ if (byPointer) return byPointer;
101
+
102
+ if (ref.startsWith('#') && !ref.startsWith('#/')) {
103
+ const anchor = ref.slice(1);
104
+ if (!anchor) return null;
105
+ for (const [defName, defSchema] of Object.entries(rootDefs)) {
106
+ if (!defSchema || typeof defSchema !== 'object') continue;
107
+ const topLevelAnchor = (defSchema as any).$anchor;
108
+ const nested = (defSchema as any).nucleusSchema;
109
+ const nestedAnchor = nested && typeof nested === 'object' ? nested.$anchor : undefined;
110
+ const defAnchor = typeof topLevelAnchor === 'string' ? topLevelAnchor : (typeof nestedAnchor === 'string' ? nestedAnchor : undefined);
111
+ if (defAnchor === anchor) {
112
+ return defName;
113
+ }
114
+ }
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ // PURE: Compute the transitive closure of local $defs dependencies for a node.
121
+ function collectLocalDefClosure(node: any, rootDefs: Record<string, any>): Set<string> {
122
+ const needed = new Set<string>();
123
+ const queue: string[] = [];
124
+
125
+ function visit(n: any) {
126
+ if (Array.isArray(n)) {
127
+ for (const item of n) visit(item);
128
+ return;
129
+ }
130
+ if (!isObject(n)) return;
131
+ if (typeof n.$ref === 'string') {
132
+ const name = resolveRefToDefName(n.$ref, rootDefs);
133
+ if (name && !needed.has(name)) {
134
+ needed.add(name);
135
+ queue.push(name);
136
+ }
137
+ }
138
+ for (const val of Object.values(n)) visit(val);
139
+ }
140
+
141
+ visit(node);
142
+
143
+ while (queue.length > 0) {
144
+ const name = queue.shift()!;
145
+ const def = rootDefs[name];
146
+ if (!def) continue;
147
+ visit(def);
148
+ }
149
+
150
+ return needed;
151
+ }
152
+
153
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
154
+ async function main() {
155
+ try {
156
+ const config = getConfig();
157
+ const { name } = parseArgs(process.argv.slice(2));
158
+ if (!name) {
159
+ throw new Error('Missing --name <DefName> argument');
160
+ }
161
+
162
+ const schemasDir = config.getSchemasDir();
163
+ const genesisPath = path.join(schemasDir, config.getSourceFile());
164
+ const outPath = config.getStandaloneSchemaPath(`${name}.json`);
165
+
166
+ if (!fs.existsSync(genesisPath)) {
167
+ throw new Error(`Genesis.json not found at ${genesisPath}. Run extractSchemasFromResourceTypeShells first.`);
168
+ }
169
+
170
+ const raw = fs.readFileSync(genesisPath, 'utf-8');
171
+ const genesis = JSON.parse(raw);
172
+
173
+ const { schema, warnings } = extractStandaloneSchemaLogic(genesis, name);
174
+ for (const w of warnings) console.warn(w);
175
+
176
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
177
+ fs.writeFileSync(outPath, JSON.stringify(schema, null, 2) + '\n');
178
+ console.log(`Created standalone subschema '${name}' -> ${outPath}`);
179
+ } catch (error: any) {
180
+ console.error(error?.message ?? error);
181
+ process.exitCode = 1;
182
+ }
183
+ }
184
+
185
+ void main();