@toolproof-core/schema 1.0.3 → 1.0.5

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 (57) hide show
  1. package/dist/generated/normalized/Genesis.json +16 -265
  2. package/dist/generated/resources/Genesis.json +18 -305
  3. package/dist/generated/schemas/Genesis.json +14 -152
  4. package/dist/generated/schemas/standalone/Goal.json +0 -33
  5. package/dist/generated/schemas/standalone/Job.json +0 -42
  6. package/dist/generated/schemas/standalone/RawStrategy.json +14 -52
  7. package/dist/generated/schemas/standalone/ResourceType.json +0 -34
  8. package/dist/generated/schemas/standalone/RunnableStrategy.json +14 -57
  9. package/dist/generated/schemas/standalone/StrategyRun.json +14 -71
  10. package/dist/generated/types/standalone/Resource_Genesis.d.ts +1 -1
  11. package/dist/generated/types/standalone/Resource_Job.d.ts +1 -1
  12. package/dist/generated/types/standalone/Resource_RawStrategy.d.ts +1 -1
  13. package/dist/generated/types/standalone/Resource_ResourceType.d.ts +1 -1
  14. package/dist/generated/types/standalone/Resource_RunnableStrategy.d.ts +1 -1
  15. package/dist/generated/types/types.d.ts +119 -1126
  16. package/dist/index.d.ts +1 -4
  17. package/dist/index.js +0 -2
  18. package/dist/scripts/_lib/config.d.ts +6 -6
  19. package/dist/scripts/_lib/config.js +10 -12
  20. package/dist/scripts/extractSchemasFromResourceTypeShells.js +109 -103
  21. package/dist/scripts/generateDependencies.js +15 -14
  22. package/dist/scripts/generateSchemaShims.js +77 -85
  23. package/dist/scripts/generateStandaloneSchema.js +47 -37
  24. package/dist/scripts/generateStandaloneType.js +85 -79
  25. package/dist/scripts/generateTypes.js +350 -470
  26. package/dist/scripts/normalizeAnchorsToPointers.d.ts +1 -1
  27. package/dist/scripts/normalizeAnchorsToPointers.js +61 -33
  28. package/dist/scripts/wrapResourceTypesWithResourceShells.js +14 -16
  29. package/package.json +7 -8
  30. package/src/Genesis.json +1837 -1999
  31. package/src/generated/{dependencyMap.json → dependencies/dependencyMap.json} +9 -19
  32. package/src/generated/normalized/Genesis.json +16 -265
  33. package/src/generated/resources/Genesis.json +18 -305
  34. package/src/generated/schemas/Genesis.json +14 -152
  35. package/src/generated/schemas/standalone/Goal.json +0 -33
  36. package/src/generated/schemas/standalone/Job.json +0 -42
  37. package/src/generated/schemas/standalone/RawStrategy.json +14 -52
  38. package/src/generated/schemas/standalone/ResourceType.json +0 -34
  39. package/src/generated/schemas/standalone/RunnableStrategy.json +14 -57
  40. package/src/generated/schemas/standalone/StrategyRun.json +14 -71
  41. package/src/generated/types/standalone/Resource_Genesis.d.ts +1 -1
  42. package/src/generated/types/standalone/Resource_Job.d.ts +1 -1
  43. package/src/generated/types/standalone/Resource_RawStrategy.d.ts +1 -1
  44. package/src/generated/types/standalone/Resource_ResourceType.d.ts +1 -1
  45. package/src/generated/types/standalone/Resource_RunnableStrategy.d.ts +1 -1
  46. package/src/generated/types/types.d.ts +119 -1126
  47. package/src/index.ts +66 -93
  48. package/src/scripts/_lib/config.ts +203 -205
  49. package/src/scripts/extractSchemasFromResourceTypeShells.ts +261 -218
  50. package/src/scripts/generateDependencies.ts +121 -120
  51. package/src/scripts/generateSchemaShims.ts +127 -135
  52. package/src/scripts/generateStandaloneSchema.ts +185 -175
  53. package/src/scripts/generateStandaloneType.ts +127 -119
  54. package/src/scripts/generateTypes.ts +532 -615
  55. package/src/scripts/normalizeAnchorsToPointers.ts +141 -123
  56. package/src/scripts/wrapResourceTypesWithResourceShells.ts +82 -84
  57. package/dist/generated/dependencyMap.json +0 -292
package/dist/index.d.ts CHANGED
@@ -1,13 +1,10 @@
1
- import { JobStepIdentity, BranchStepIdentity, WhileStepIdentity, ForStepIdentity } from './generated/types/types.js';
2
1
  export { default as SchemaGenesis } from './generated/schemas/Genesis.js';
3
2
  export { default as SchemaJob } from './generated/schemas/standalone/Job.js';
4
3
  export { default as SchemaStrategyRun } from './generated/schemas/standalone/StrategyRun.js';
5
4
  export { default as ResourceTypeGenesis } from './generated/resources/Genesis.js';
6
- export { default as dependencies } from './generated/dependencyMap.json';
7
5
  export type { Resource_Genesis as Resource_GenesisJson } from './generated/types/standalone/Resource_Genesis.js';
8
6
  export type { Resource_ResourceType as Resource_ResourceTypeJson } from './generated/types/standalone/Resource_ResourceType.js';
9
7
  export type { Resource_Job as Resource_JobJson } from './generated/types/standalone/Resource_Job.js';
10
8
  export type { Resource_RawStrategy as Resource_RawStrategyJson } from './generated/types/standalone/Resource_RawStrategy.js';
11
9
  export type { Resource_RunnableStrategy as Resource_RunnableStrategyJson } from './generated/types/standalone/Resource_RunnableStrategy.js';
12
- export type StepIdentityJson = JobStepIdentity | BranchStepIdentity | WhileStepIdentity | ForStepIdentity;
13
- export type { DocumentationFacet as DocumentationFacetJson, NucleusFacet as NucleusFacetJson, ResourceTypeIdentity as ResourceTypeIdentityJson, ResourceType as ResourceTypeJson, ResourceRoleIdentity as ResourceRoleIdentityJson, ResourceRoleValue as ResourceRoleValueJson, Conditional as ConditionalJson, RoleMap as RoleMapJson, Roles as RolesJson, RoleBindings as RoleBindingsJson, ResourceIdentity as ResourceIdentityJson, JobStepIdentity as JobStepIdentityJson, BranchStepIdentity as BranchStepIdentityJson, WhileStepIdentity as WhileStepIdentityJson, ForStepIdentity as ForStepIdentityJson, JobStep as JobStepJson, BranchStep as BranchStepJson, WhileStep as WhileStepJson, ForStep as ForStepJson, StepKind as StepKindJson, Step as StepJson, CreationContext as CreationContextJson, ResourceMissing as ResourceMissingJson, Resource as ResourceJson, StrategyState as StrategyStateJson, StepsFacet as StepsFacetJson, RawStrategy as RawStrategyJson, RunnableStrategyIdentity as RunnableStrategyIdentityJson, RunnableStrategyStatus as RunnableStrategyStatusJson, RunnableStrategy as RunnableStrategyJson, RunnableStrategyUpdate as RunnableStrategyUpdateJson, Domain as DomainJson, JobIdentity as JobIdentityJson, Job as JobJson, JsonData as JsonDataJson, StrategyThreadIdentity as StrategyThreadIdentityJson, StrategyThreadMap as StrategyThreadMapJson, Timestamp as TimestampJson, Goal as GoalJson, } from './generated/types/types.js';
10
+ export type { DocumentationFacet as DocumentationFacetJson, NucleusFacet as NucleusFacetJson, ResourceTypeIdentity as ResourceTypeIdentityJson, ResourceType as ResourceTypeJson, ResourceRoleIdentity as ResourceRoleIdentityJson, ResourceRoleValue as ResourceRoleValueJson, Conditional as ConditionalJson, RoleMap as RoleMapJson, Roles as RolesJson, RoleBindings as RoleBindingsJson, ResourceIdentity as ResourceIdentityJson, JobStepIdentity as JobStepIdentityJson, BranchStepIdentity as BranchStepIdentityJson, WhileStepIdentity as WhileStepIdentityJson, ForStepIdentity as ForStepIdentityJson, JobStep as JobStepJson, BranchStep as BranchStepJson, WhileStep as WhileStepJson, ForStep as ForStepJson, StepKind as StepKindJson, StepIdentity as StepIdentityJson, Step as StepJson, CreationContext as CreationContextJson, ResourceMissing as ResourceMissingJson, Resource as ResourceJson, StrategyState as StrategyStateJson, StepsFacet as StepsFacetJson, RawStrategy as RawStrategyJson, RunnableStrategyIdentity as RunnableStrategyIdentityJson, RunnableStrategyStatus as RunnableStrategyStatusJson, RunnableStrategy as RunnableStrategyJson, RunnableStrategyUpdate as RunnableStrategyUpdateJson, JobIdentity as JobIdentityJson, Job as JobJson, JsonData as JsonDataJson, StrategyThreadIdentity as StrategyThreadIdentityJson, StrategyThreadMap as StrategyThreadMapJson, JobStepSocket as JobStepSocketJson, Timestamp as TimestampJson, Goal as GoalJson, } from './generated/types/types.js';
package/dist/index.js CHANGED
@@ -3,5 +3,3 @@ export { default as SchemaGenesis } from './generated/schemas/Genesis.js';
3
3
  export { default as SchemaJob } from './generated/schemas/standalone/Job.js';
4
4
  export { default as SchemaStrategyRun } from './generated/schemas/standalone/StrategyRun.js';
5
5
  export { default as ResourceTypeGenesis } from './generated/resources/Genesis.js';
6
- export { default as dependencies } from './generated/dependencyMap.json';
7
- // export { isResourceRoleIdentityJson, isStrategyThreadIdentityJson } from './identityGuards.js';
@@ -28,16 +28,16 @@ export declare class SchemaConfig {
28
28
  getNormalizedSourcePath(): string;
29
29
  getSchemasDir(): string;
30
30
  getSchemaPath(filename: string): string;
31
- getSchemasStandaloneDir(): string;
32
- getSchemaStandalonePath(filename: string): string;
31
+ getStandaloneSchemaDir(): string;
32
+ getStandaloneSchemaPath(filename: string): string;
33
33
  getTypesSrcDir(): string;
34
34
  getTypesDistDir(): string;
35
35
  getTypesSrcPath(filename: string): string;
36
36
  getTypesDistPath(filename: string): string;
37
- getTypesStandaloneSrcDir(): string;
38
- getTypesStandaloneDistDir(): string;
39
- getTypesStandaloneSrcPath(filename: string): string;
40
- getTypesStandaloneDistPath(filename: string): string;
37
+ getStandaloneTypeSrcDir(): string;
38
+ getStandaloneTypeDistDir(): string;
39
+ getStandaloneTypeSrcPath(filename: string): string;
40
+ getStandaloneTypeDistPath(filename: string): string;
41
41
  getResourcesDir(): string;
42
42
  getDependencyMapPath(): string;
43
43
  getBaseUrl(): string;
@@ -20,12 +20,10 @@ export class SchemaConfig {
20
20
  this.root = getEnv('TP_SCHEMA_ROOT', process.cwd());
21
21
  this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/');
22
22
  this.sourceFile = getEnv('TP_SCHEMA_SOURCE_FILE', 'Genesis.json');
23
- // Intermediate, generated artifact produced by normalizeAnchorsToPointers.
24
- // This should NOT live next to the source-of-truth schemas.
25
23
  this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/generated/normalized');
26
24
  this.schemasDir = getEnv('TP_SCHEMA_SCHEMAS_DIR', 'src/generated/schemas');
27
25
  this.resourcesDir = getEnv('TP_SCHEMA_RESOURCES_DIR', 'src/generated/resources');
28
- this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencyMap.json');
26
+ this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencies/dependencyMap.json');
29
27
  this.typesSrcDir = getEnv('TP_SCHEMA_TYPES_SRC_DIR', 'src/generated/types');
30
28
  this.typesDistDir = getEnv('TP_SCHEMA_TYPES_DIST_DIR', 'dist/generated/types');
31
29
  this.baseUrl = getEnv('TP_SCHEMA_BASE_URL', 'https://schemas.toolproof.com');
@@ -68,11 +66,11 @@ export class SchemaConfig {
68
66
  getSchemaPath(filename) {
69
67
  return path.join(this.getSchemasDir(), filename);
70
68
  }
71
- getSchemasStandaloneDir() {
69
+ getStandaloneSchemaDir() {
72
70
  return path.join(this.getSchemasDir(), 'standalone');
73
71
  }
74
- getSchemaStandalonePath(filename) {
75
- return path.join(this.getSchemasStandaloneDir(), filename);
72
+ getStandaloneSchemaPath(filename) {
73
+ return path.join(this.getStandaloneSchemaDir(), filename);
76
74
  }
77
75
  getTypesSrcDir() {
78
76
  return path.isAbsolute(this.typesSrcDir)
@@ -90,17 +88,17 @@ export class SchemaConfig {
90
88
  getTypesDistPath(filename) {
91
89
  return path.join(this.getTypesDistDir(), filename);
92
90
  }
93
- getTypesStandaloneSrcDir() {
91
+ getStandaloneTypeSrcDir() {
94
92
  return path.join(this.getTypesSrcDir(), 'standalone');
95
93
  }
96
- getTypesStandaloneDistDir() {
94
+ getStandaloneTypeDistDir() {
97
95
  return path.join(this.getTypesDistDir(), 'standalone');
98
96
  }
99
- getTypesStandaloneSrcPath(filename) {
100
- return path.join(this.getTypesStandaloneSrcDir(), filename);
97
+ getStandaloneTypeSrcPath(filename) {
98
+ return path.join(this.getStandaloneTypeSrcDir(), filename);
101
99
  }
102
- getTypesStandaloneDistPath(filename) {
103
- return path.join(this.getTypesStandaloneDistDir(), filename);
100
+ getStandaloneTypeDistPath(filename) {
101
+ return path.join(this.getStandaloneTypeDistDir(), filename);
104
102
  }
105
103
  getResourcesDir() {
106
104
  return path.isAbsolute(this.resourcesDir)
@@ -1,12 +1,10 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { fileURLToPath } from "url";
4
3
  import { getConfig } from "./_lib/config.js";
5
- function parseArgs() {
6
- const config = getConfig();
7
- const argv = process.argv.slice(2);
8
- let inPath = "";
9
- let outPath = "";
4
+ // PURE: Parse CLI args (no defaults, no filesystem probing).
5
+ function parseArgs(argv) {
6
+ let inPath;
7
+ let outPath;
10
8
  let topLevelId;
11
9
  for (let i = 0; i < argv.length; i++) {
12
10
  const a = argv[i];
@@ -23,45 +21,23 @@ function parseArgs() {
23
21
  topLevelId = v;
24
22
  }
25
23
  }
26
- // Use config defaults if not provided via CLI
27
- if (!inPath) {
28
- // Use generated/normalized version with anchor refs rewritten to pointers
29
- inPath = config.getNormalizedSourcePath();
30
- }
31
- if (!outPath) {
32
- outPath = config.getSchemaPath(config.getSourceFile());
33
- }
34
- if (!topLevelId) {
35
- topLevelId = config.getSchemaId('Genesis');
36
- }
37
- // Resolve to absolute paths from project root
38
- const cwd = config.getRoot();
39
- const wasInRelative = !path.isAbsolute(inPath);
40
- const wasOutRelative = !path.isAbsolute(outPath);
41
- if (wasInRelative)
42
- inPath = path.join(cwd, inPath);
43
- if (wasOutRelative)
44
- outPath = path.join(cwd, outPath);
45
- // Fallback: resolve relative to script directory if not found
46
- const scriptDir = path.dirname(fileURLToPath(import.meta.url));
47
- if (!fs.existsSync(inPath) && wasInRelative)
48
- inPath = path.resolve(scriptDir, inPath);
49
- const outDir = path.dirname(outPath);
50
- if (!fs.existsSync(outDir) && wasOutRelative) {
51
- // Try making directory relative to script dir
52
- const altOut = path.resolve(scriptDir, outPath);
53
- const altOutDir = path.dirname(altOut);
54
- if (!fs.existsSync(path.dirname(outPath))) {
55
- // Prefer creating outDir at cwd location if possible; otherwise fallback below when writing
56
- }
57
- else {
58
- outPath = altOut;
59
- }
60
- }
61
24
  return { inPath, outPath, topLevelId };
62
25
  }
63
- // ATTENTION: we don't want heuristic behavior.
64
- // Heuristic: determine if a node is a Type shell
26
+ // PURE: Apply defaults for any missing values.
27
+ function applyDefaults(args, defaults) {
28
+ return {
29
+ inPath: args.inPath?.trim() ? args.inPath : defaults.inPath,
30
+ outPath: args.outPath?.trim() ? args.outPath : defaults.outPath,
31
+ topLevelId: args.topLevelId?.trim() ? args.topLevelId : defaults.topLevelId
32
+ };
33
+ }
34
+ // PURE: Resolve relative paths against a root directory (no heuristics).
35
+ function resolvePaths(options, rootDir) {
36
+ const inPath = path.isAbsolute(options.inPath) ? options.inPath : path.join(rootDir, options.inPath);
37
+ const outPath = path.isAbsolute(options.outPath) ? options.outPath : path.join(rootDir, options.outPath);
38
+ return { ...options, inPath, outPath };
39
+ }
40
+ // PURE: Determine whether a node is a Type shell.
65
41
  function isTypeShell(node) {
66
42
  return (node && typeof node === "object" && !Array.isArray(node) &&
67
43
  // Treat any object that has an 'nucleusSchema' AND 'identity' as a Type shell
@@ -69,84 +45,99 @@ function isTypeShell(node) {
69
45
  node.nucleusSchema && typeof node.nucleusSchema === "object" &&
70
46
  node.identity && typeof node.identity === "string");
71
47
  }
72
- // Merge $defs into target, without overwriting existing keys unless identical
73
- function mergeDefs(target, source, label) {
48
+ // PURE: Sanitize labels for use in deterministic collision keys.
49
+ function sanitizeLabel(label) {
50
+ return (label || "defs").replace(/[^A-Za-z0-9_]+/g, "_");
51
+ }
52
+ // PURE: Extract a `$defs` object from a schema node, if present.
53
+ function getDefsObject(source) {
74
54
  if (!source || typeof source !== "object")
75
- return;
55
+ return undefined;
76
56
  const src = source["$defs"];
77
- if (!src || typeof src !== "object")
78
- return;
79
- for (const [k, v] of Object.entries(src)) {
80
- if (!(k in target)) {
81
- target[k] = v;
82
- }
83
- else {
84
- // Best-effort: if duplicate key, require structural equality; otherwise, namespace
85
- const existing = JSON.stringify(target[k]);
86
- const incoming = JSON.stringify(v);
87
- if (existing !== incoming) {
88
- const altKey = `${k}__from_${(label || "defs").replace(/[^A-Za-z0-9_]+/g, "_")}`;
89
- if (!(altKey in target))
90
- target[altKey] = v;
91
- }
57
+ if (!src || typeof src !== "object" || Array.isArray(src))
58
+ return undefined;
59
+ return src;
60
+ }
61
+ // PURE: Merge defs without mutating inputs; collisions are namespaced deterministically.
62
+ function mergeDefs(existing, incoming, label) {
63
+ if (!incoming)
64
+ return { ...existing };
65
+ const merged = { ...existing };
66
+ for (const [k, v] of Object.entries(incoming)) {
67
+ if (!(k in merged)) {
68
+ merged[k] = v;
69
+ continue;
92
70
  }
93
- }
71
+ const current = merged[k];
72
+ const same = JSON.stringify(current) === JSON.stringify(v);
73
+ if (same)
74
+ continue;
75
+ const altKey = `${k}__from_${sanitizeLabel(label)}`;
76
+ if (!(altKey in merged))
77
+ merged[altKey] = v;
78
+ }
79
+ return merged;
94
80
  }
95
- // Deeply traverse an object replacing any Type shell with its nucleusSchema,
96
- // and hoist its inner $defs to topDefs. Prevent infinite recursion with a visited set.
97
- function unwrapTypes(node, topDefs, labelPath = [], visited = new Set()) {
98
- if (node && typeof node === "object") {
99
- if (visited.has(node))
100
- return node; // avoid cycles
101
- visited.add(node);
102
- }
81
+ // PURE: Unwrap Type shells and collect any encountered inner `$defs` hoist events.
82
+ function traverseTypes(node, labelPath = []) {
103
83
  if (isTypeShell(node)) {
104
84
  const env = node;
105
85
  const inner = env.nucleusSchema;
106
- // Hoist inner $defs before stripping
107
- mergeDefs(topDefs, inner, labelPath.join("_"));
108
- // Return the inner schema itself, after also unwrapping any nested shells it may contain
109
- const unwrappedInner = unwrapTypes(inner, topDefs, labelPath.concat([String(env.identity || "env")]), visited);
110
- return unwrappedInner;
86
+ const label = labelPath.join("_");
87
+ const innerDefs = getDefsObject(inner);
88
+ const innerResult = traverseTypes(inner, labelPath.concat([String(env.identity || "env")]));
89
+ const events = [];
90
+ if (innerDefs)
91
+ events.push({ defs: innerDefs, label });
92
+ events.push(...innerResult.hoistEvents);
93
+ return { value: innerResult.value, hoistEvents: events };
111
94
  }
112
95
  if (Array.isArray(node)) {
113
- return node.map((v, i) => unwrapTypes(v, topDefs, labelPath.concat([String(i)]), visited));
96
+ const events = [];
97
+ const mapped = node.map((v, i) => {
98
+ const r = traverseTypes(v, labelPath.concat([String(i)]));
99
+ events.push(...r.hoistEvents);
100
+ return r.value;
101
+ });
102
+ return { value: mapped, hoistEvents: events };
114
103
  }
115
104
  if (node && typeof node === "object") {
105
+ const events = [];
116
106
  const out = {};
117
107
  for (const [k, v] of Object.entries(node)) {
118
108
  if (k === "$defs" && v && typeof v === "object" && !Array.isArray(v)) {
119
- // Process nested $defs: unwrap each entry value if it's a Type shell
120
109
  const defsOut = {};
121
110
  for (const [dk, dv] of Object.entries(v)) {
122
- const unwrapped = unwrapTypes(dv, topDefs, labelPath.concat(["$defs", dk]), visited);
123
- defsOut[dk] = unwrapped;
111
+ const r = traverseTypes(dv, labelPath.concat(["$defs", dk]));
112
+ events.push(...r.hoistEvents);
113
+ defsOut[dk] = r.value;
124
114
  }
125
115
  out[k] = defsOut;
116
+ continue;
126
117
  }
127
- else {
128
- out[k] = unwrapTypes(v, topDefs, labelPath.concat([k]), visited);
129
- }
118
+ const r = traverseTypes(v, labelPath.concat([k]));
119
+ events.push(...r.hoistEvents);
120
+ out[k] = r.value;
130
121
  }
131
- return out;
122
+ return { value: out, hoistEvents: events };
132
123
  }
133
- return node;
124
+ return { value: node, hoistEvents: [] };
134
125
  }
135
- /**
136
- * Pure function that takes a schema document and options, and returns the flattened schema.
137
- * Performs no I/O operations.
138
- */
126
+ // PURE: Flatten a Type shell JSON into a single JSON Schema document.
139
127
  function extractSchemaLogic(doc, topLevelId) {
140
128
  if (!doc || typeof doc !== "object" || !doc.nucleusSchema) {
141
129
  throw new Error("Input must be a Type JSON with an nucleusSchema at the top level");
142
130
  }
143
131
  const topSchema = doc.nucleusSchema;
144
132
  // Collect $defs so that any '#/$defs/...' pointers can be resolved from the root.
145
- const outDefs = {};
146
- // Seed with top-level $defs (if any) before unwrapping
147
- mergeDefs(outDefs, topSchema, "top");
133
+ const seededDefs = mergeDefs({}, getDefsObject(topSchema), "top");
148
134
  // Unwrap the entire top schema tree so that any nested Type shells become raw schemas
149
- const flattened = unwrapTypes(topSchema, outDefs, ["nucleusSchema"]);
135
+ const traversal = traverseTypes(topSchema, ["nucleusSchema"]);
136
+ const flattened = traversal.value;
137
+ let outDefs = seededDefs;
138
+ for (const ev of traversal.hoistEvents) {
139
+ outDefs = mergeDefs(outDefs, ev.defs, ev.label);
140
+ }
150
141
  // Assemble output: force $schema, optionally set $id, hoist collected $defs
151
142
  let base;
152
143
  if (flattened && typeof flattened === "object" && !Array.isArray(flattened)) {
@@ -180,18 +171,33 @@ function extractSchemaLogic(doc, topLevelId) {
180
171
  // Preserve natural key ordering (do not reorder for readability)
181
172
  return output;
182
173
  }
174
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
183
175
  function main() {
184
- const { inPath, outPath, topLevelId } = parseArgs();
185
- if (!fs.existsSync(inPath)) {
186
- console.error(`Input file not found at ${inPath}`);
187
- process.exit(1);
176
+ try {
177
+ const config = getConfig();
178
+ const defaults = {
179
+ // Use generated/normalized version with anchor refs rewritten to pointers
180
+ inPath: config.getNormalizedSourcePath(),
181
+ outPath: config.getSchemaPath(config.getSourceFile()),
182
+ topLevelId: config.getSchemaId("Genesis")
183
+ };
184
+ const cli = parseArgs(process.argv.slice(2));
185
+ const opts = resolvePaths(applyDefaults(cli, defaults), config.getRoot());
186
+ const { inPath, outPath, topLevelId } = opts;
187
+ if (!fs.existsSync(inPath)) {
188
+ throw new Error(`Input file not found at ${inPath}`);
189
+ }
190
+ const raw = fs.readFileSync(inPath, "utf8");
191
+ const doc = JSON.parse(raw);
192
+ // Core logic is now in a pure function
193
+ const extracted = extractSchemaLogic(doc, topLevelId);
194
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
195
+ fs.writeFileSync(outPath, JSON.stringify(extracted, null, 4), "utf8");
196
+ console.log(`Wrote flattened schema to ${outPath}`);
197
+ }
198
+ catch (e) {
199
+ console.error(e);
200
+ process.exitCode = 1;
188
201
  }
189
- const raw = fs.readFileSync(inPath, "utf8");
190
- const doc = JSON.parse(raw);
191
- // Core logic is now in a pure function
192
- const ordered = extractSchemaLogic(doc, topLevelId);
193
- fs.mkdirSync(path.dirname(outPath), { recursive: true });
194
- fs.writeFileSync(outPath, JSON.stringify(ordered, null, 4), "utf8");
195
- console.log(`Wrote flattened schema to ${outPath}`);
196
202
  }
197
203
  main();
@@ -1,10 +1,12 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import { getConfig } from "./_lib/config.js";
4
+ // PURE: Decode a single JSON Pointer segment.
4
5
  function decodeJsonPointerSegment(segment) {
5
6
  // JSON Pointer decoding: ~1 => / and ~0 => ~
6
7
  return segment.replace(/~1/g, "/").replace(/~0/g, "~");
7
8
  }
9
+ // PURE: Collect all `$ref` strings from a JSON-ish tree.
8
10
  function collectRefs(node, out) {
9
11
  if (Array.isArray(node)) {
10
12
  for (const item of node)
@@ -21,6 +23,7 @@ function collectRefs(node, out) {
21
23
  collectRefs(value, out);
22
24
  }
23
25
  }
26
+ // PURE: Resolve an internal ref (pointer or anchor) to a root $defs key, if possible.
24
27
  function resolveInternalRefToDefKey(ref, defKeys, anchorToDef) {
25
28
  if (!ref.startsWith("#"))
26
29
  return null;
@@ -49,6 +52,7 @@ function resolveInternalRefToDefKey(ref, defKeys, anchorToDef) {
49
52
  * @param doc The source JSON Schema document
50
53
  * @returns A record mapping definition names to their dependency lists
51
54
  */
55
+ // PURE: Generate a $defs dependency map from a JSON Schema document (no I/O).
52
56
  function generateDependencyMapLogic(doc) {
53
57
  const defs = doc?.$defs && typeof doc.$defs === "object" ? doc.$defs : {};
54
58
  const defKeys = new Set(Object.keys(defs));
@@ -79,15 +83,15 @@ function generateDependencyMapLogic(doc) {
79
83
  }
80
84
  return dependencyMap;
81
85
  }
82
- async function main() {
83
- const config = getConfig();
84
- const inPath = config.getSchemaPath("Genesis.json");
85
- const outPath = config.getDependencyMapPath();
86
- if (!fs.existsSync(inPath)) {
87
- console.error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
88
- process.exit(1);
89
- }
86
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
87
+ function main() {
90
88
  try {
89
+ const config = getConfig();
90
+ const inPath = config.getSchemaPath("Genesis.json");
91
+ const outPath = config.getDependencyMapPath();
92
+ if (!fs.existsSync(inPath)) {
93
+ throw new Error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
94
+ }
91
95
  const raw = fs.readFileSync(inPath, "utf8");
92
96
  const doc = JSON.parse(raw);
93
97
  const dependencyMap = generateDependencyMapLogic(doc);
@@ -96,11 +100,8 @@ async function main() {
96
100
  console.log(`Wrote dependency map to ${outPath}`);
97
101
  }
98
102
  catch (error) {
99
- console.error(`Error generating dependency map: ${error.message}`);
100
- process.exit(1);
103
+ console.error(`Error generating dependency map: ${error?.message ?? error}`);
104
+ process.exitCode = 1;
101
105
  }
102
106
  }
103
- main().catch((e) => {
104
- console.error(e);
105
- process.exit(1);
106
- });
107
+ main();
@@ -10,28 +10,16 @@ import { getConfig } from './_lib/config.js';
10
10
  *
11
11
  * Generates shims for:
12
12
  * - schemas dir: config.getSchemasDir() (schema files)
13
- * - standalone schemas dir: config.getSchemasStandaloneDir() (standalone schema files)
13
+ * - standalone schemas dir: config.getStandaloneSchemaDir() (standalone schema files)
14
14
  * - resources dir: config.getResourcesDir() (resource envelope files)
15
15
  *
16
16
  * Usage: node ./dist/scripts/generateSchemaShims.js
17
17
  */
18
- /**
19
- * Pure function to generate the content of a TypeScript shim file.
20
- *
21
- * @param jsonFile The name of the JSON file to import
22
- * @param variableName The name of the variable to use for the import
23
- * @returns The TypeScript file content
24
- */
18
+ // PURE: Generate the .ts shim module content for a given .json filename.
25
19
  function generateShimContent(jsonFile, variableName) {
26
20
  return `import ${variableName} from './${jsonFile}' with { type: 'json' };\nexport default ${variableName};\n`;
27
21
  }
28
- /**
29
- * Pure function to map a list of JSON files to their corresponding TypeScript shim files and contents.
30
- *
31
- * @param jsonFiles List of JSON filenames
32
- * @param variableName The variable name to use in the shim
33
- * @returns A record mapping TS filenames to their contents
34
- */
22
+ // PURE: Map JSON filenames to deterministic shim file contents.
35
23
  function getShimsForFiles(jsonFiles, variableName) {
36
24
  const shims = {};
37
25
  for (const jsonFile of jsonFiles) {
@@ -41,83 +29,87 @@ function getShimsForFiles(jsonFiles, variableName) {
41
29
  }
42
30
  return shims;
43
31
  }
44
- async function main() {
45
- const config = getConfig();
46
- const schemasDir = config.getSchemasDir();
47
- const standaloneSchemasDir = config.getSchemasStandaloneDir();
48
- const resourcesDir = config.getResourcesDir();
49
- const generatedResourceTypesDir = config.getNormalizedDir();
50
- let totalCount = 0;
51
- // Process schemas directory
52
- if (fs.existsSync(schemasDir)) {
53
- const files = fs.readdirSync(schemasDir);
54
- const jsonFiles = files.filter(f => f.endsWith('.json') && !f.startsWith('.'));
55
- const shims = getShimsForFiles(jsonFiles, 'schema');
56
- for (const [tsFile, content] of Object.entries(shims)) {
57
- const tsPath = path.join(schemasDir, tsFile);
58
- fs.writeFileSync(tsPath, content, 'utf-8');
59
- console.log(`Generated ${tsFile} in ${schemasDir}`);
60
- totalCount++;
32
+ // IMPURE: Script entrypoint (config + filesystem I/O + console + process exit code).
33
+ function main() {
34
+ try {
35
+ const config = getConfig();
36
+ const schemasDir = config.getSchemasDir();
37
+ const standaloneSchemasDir = config.getStandaloneSchemaDir();
38
+ const resourcesDir = config.getResourcesDir();
39
+ const generatedResourceTypesDir = config.getNormalizedDir();
40
+ let totalCount = 0;
41
+ // Process schemas directory
42
+ if (fs.existsSync(schemasDir)) {
43
+ const files = fs.readdirSync(schemasDir);
44
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
45
+ const shims = getShimsForFiles(jsonFiles, 'schema');
46
+ for (const [tsFile, content] of Object.entries(shims)) {
47
+ const tsPath = path.join(schemasDir, tsFile);
48
+ fs.writeFileSync(tsPath, content, 'utf-8');
49
+ console.log('Generated ' + tsFile + ' in ' + schemasDir);
50
+ totalCount++;
51
+ }
52
+ console.log('Generated ' + jsonFiles.length + ' TypeScript schema shims in ' + schemasDir);
61
53
  }
62
- console.log(`Generated ${jsonFiles.length} TypeScript schema shims in ${schemasDir}`);
63
- }
64
- else {
65
- console.warn(`Schemas directory not found at ${schemasDir}`);
66
- }
67
- // Process standalone schemas directory
68
- if (fs.existsSync(standaloneSchemasDir)) {
69
- const files = fs.readdirSync(standaloneSchemasDir);
70
- const jsonFiles = files.filter(f => f.endsWith('.json') && !f.startsWith('.'));
71
- const shims = getShimsForFiles(jsonFiles, 'schema');
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++;
54
+ else {
55
+ console.warn('Schemas directory not found at ' + schemasDir);
77
56
  }
78
- console.log(`Generated ${jsonFiles.length} TypeScript standalone schema shims in ${standaloneSchemasDir}`);
79
- }
80
- else {
81
- // Standalone schemas are optional
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
- const shims = getShimsForFiles(jsonFiles, 'resources');
88
- for (const [tsFile, content] of Object.entries(shims)) {
89
- const tsPath = path.join(resourcesDir, tsFile);
90
- fs.writeFileSync(tsPath, content, 'utf-8');
91
- console.log(`Generated ${tsFile} in ${resourcesDir}`);
92
- totalCount++;
57
+ // Process standalone schemas directory
58
+ if (fs.existsSync(standaloneSchemasDir)) {
59
+ const files = fs.readdirSync(standaloneSchemasDir);
60
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
61
+ const shims = getShimsForFiles(jsonFiles, 'schema');
62
+ for (const [tsFile, content] of Object.entries(shims)) {
63
+ const tsPath = path.join(standaloneSchemasDir, tsFile);
64
+ fs.writeFileSync(tsPath, content, 'utf-8');
65
+ console.log('Generated ' + tsFile + ' in ' + standaloneSchemasDir);
66
+ totalCount++;
67
+ }
68
+ console.log('Generated ' + jsonFiles.length + ' TypeScript standalone schema shims in ' + standaloneSchemasDir);
93
69
  }
94
- console.log(`Generated ${jsonFiles.length} TypeScript resource shims in ${resourcesDir}`);
95
- }
96
- else {
97
- console.warn(`Resources directory not found at ${resourcesDir}`);
98
- }
99
- // Genesis (normalized) shim
100
- try {
101
- const genesisJsonPath = config.getNormalizedSourcePath();
102
- if (fs.existsSync(genesisJsonPath)) {
103
- fs.mkdirSync(generatedResourceTypesDir, { recursive: true });
104
- const tsFile = 'Genesis.ts';
105
- const content = generateShimContent('Genesis.json', 'schema');
106
- const tsPath = path.join(generatedResourceTypesDir, tsFile);
107
- fs.writeFileSync(tsPath, content, 'utf-8');
108
- console.log(`Generated ${tsFile} in ${generatedResourceTypesDir}`);
109
- totalCount++;
70
+ else {
71
+ // Standalone schemas are optional
72
+ }
73
+ // Process resources directory
74
+ if (fs.existsSync(resourcesDir)) {
75
+ const files = fs.readdirSync(resourcesDir);
76
+ const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
77
+ const shims = getShimsForFiles(jsonFiles, 'resources');
78
+ for (const [tsFile, content] of Object.entries(shims)) {
79
+ const tsPath = path.join(resourcesDir, tsFile);
80
+ fs.writeFileSync(tsPath, content, 'utf-8');
81
+ console.log('Generated ' + tsFile + ' in ' + resourcesDir);
82
+ totalCount++;
83
+ }
84
+ console.log('Generated ' + jsonFiles.length + ' TypeScript resource shims in ' + resourcesDir);
110
85
  }
111
86
  else {
112
- console.warn(`Genesis source JSON not found at ${genesisJsonPath}; skipping Genesis.ts shim`);
87
+ console.warn('Resources directory not found at ' + resourcesDir);
88
+ }
89
+ // Genesis (normalized) shim
90
+ try {
91
+ const genesisJsonPath = config.getNormalizedSourcePath();
92
+ if (fs.existsSync(genesisJsonPath)) {
93
+ fs.mkdirSync(generatedResourceTypesDir, { recursive: true });
94
+ const tsFile = 'Genesis.ts';
95
+ const content = generateShimContent('Genesis.json', 'schema');
96
+ const tsPath = path.join(generatedResourceTypesDir, tsFile);
97
+ fs.writeFileSync(tsPath, content, 'utf-8');
98
+ console.log('Generated ' + tsFile + ' in ' + generatedResourceTypesDir);
99
+ totalCount++;
100
+ }
101
+ else {
102
+ console.warn('Genesis source JSON not found at ' + genesisJsonPath + '; skipping Genesis.ts shim');
103
+ }
104
+ }
105
+ catch (e) {
106
+ console.warn('Failed to generate Genesis.ts shim:', e);
113
107
  }
108
+ console.log('Generated ' + totalCount + ' total TypeScript shims');
114
109
  }
115
110
  catch (e) {
116
- console.warn('Failed to generate Genesis.ts shim:', e);
111
+ console.error(e);
112
+ process.exitCode = 1;
117
113
  }
118
- console.log(`Generated ${totalCount} total TypeScript shims`);
119
114
  }
120
- main().catch((e) => {
121
- console.error(e);
122
- process.exit(1);
123
- });
115
+ main();