@toolproof-core/schema 1.0.3 → 1.0.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.
Files changed (70) hide show
  1. package/dist/generated/normalized/Genesis.json +17 -9
  2. package/dist/generated/resources/Genesis.json +19 -11
  3. package/dist/generated/schemas/Genesis.json +15 -7
  4. package/dist/generated/schemas/standalone/RawStrategy.json +15 -7
  5. package/dist/generated/schemas/standalone/RunnableStrategy.json +15 -7
  6. package/dist/generated/schemas/standalone/StrategyRun.json +15 -7
  7. package/dist/generated/{types → typesTS}/standalone/Resource_Genesis.d.ts +1 -1
  8. package/{src/generated/types → dist/generated/typesTS}/standalone/Resource_Job.d.ts +1 -1
  9. package/dist/generated/{types → typesTS}/standalone/Resource_RawStrategy.d.ts +1 -1
  10. package/{src/generated/types → dist/generated/typesTS}/standalone/Resource_ResourceType.d.ts +1 -1
  11. package/dist/generated/{types → typesTS}/standalone/Resource_RunnableStrategy.d.ts +1 -1
  12. package/dist/generated/typesTS/typesTS.d.ts +745 -0
  13. package/dist/index.d.ts +6 -9
  14. package/dist/index.js +0 -2
  15. package/dist/scripts/_lib/config.d.ts +10 -10
  16. package/dist/scripts/_lib/config.js +23 -23
  17. package/dist/scripts/extractSchemasFromResourceTypeShells.js +109 -103
  18. package/dist/scripts/generateDependencies.js +15 -14
  19. package/dist/scripts/generateSchemaShims.js +76 -84
  20. package/dist/scripts/generateStandaloneSchema.js +47 -37
  21. package/dist/scripts/generateStandaloneTypeTS.js +108 -0
  22. package/dist/scripts/generateTypesTS.js +430 -0
  23. package/dist/scripts/normalizeAnchorsToPointers.js +37 -30
  24. package/dist/scripts/wrapResourceTypesWithResourceShells.js +14 -16
  25. package/package.json +11 -12
  26. package/src/Genesis.json +2007 -1999
  27. package/{dist/generated → src/generated/dependencies}/dependencyMap.json +8 -7
  28. package/src/generated/normalized/Genesis.json +17 -9
  29. package/src/generated/resources/Genesis.json +19 -11
  30. package/src/generated/schemas/Genesis.json +15 -7
  31. package/src/generated/schemas/standalone/RawStrategy.json +15 -7
  32. package/src/generated/schemas/standalone/RunnableStrategy.json +15 -7
  33. package/src/generated/schemas/standalone/StrategyRun.json +15 -7
  34. package/src/generated/{types → typesTS}/standalone/Resource_Genesis.d.ts +1 -1
  35. package/{dist/generated/types → src/generated/typesTS}/standalone/Resource_Job.d.ts +1 -1
  36. package/src/generated/{types → typesTS}/standalone/Resource_RawStrategy.d.ts +1 -1
  37. package/{dist/generated/types → src/generated/typesTS}/standalone/Resource_ResourceType.d.ts +1 -1
  38. package/src/generated/{types → typesTS}/standalone/Resource_RunnableStrategy.d.ts +1 -1
  39. package/src/generated/typesTS/typesTS.d.ts +745 -0
  40. package/src/index.ts +67 -93
  41. package/src/scripts/_lib/config.ts +205 -205
  42. package/src/scripts/extractSchemasFromResourceTypeShells.ts +261 -218
  43. package/src/scripts/generateDependencies.ts +121 -120
  44. package/src/scripts/generateSchemaShims.ts +127 -135
  45. package/src/scripts/generateStandaloneSchema.ts +185 -175
  46. package/src/scripts/generateStandaloneTypeTS.ts +127 -0
  47. package/src/scripts/generateTypesTS.ts +532 -0
  48. package/src/scripts/normalizeAnchorsToPointers.ts +115 -123
  49. package/src/scripts/wrapResourceTypesWithResourceShells.ts +82 -84
  50. package/dist/generated/types/types.d.ts +0 -1723
  51. package/dist/scripts/generateStandaloneType.js +0 -102
  52. package/dist/scripts/generateTypes.js +0 -550
  53. package/src/generated/dependencyMap.json +0 -292
  54. package/src/generated/types/types.d.ts +0 -1723
  55. package/src/scripts/generateStandaloneType.ts +0 -119
  56. package/src/scripts/generateTypes.ts +0 -615
  57. /package/dist/generated/{types → typesTS}/standalone/Resource_Genesis.js +0 -0
  58. /package/dist/generated/{types → typesTS}/standalone/Resource_Job.js +0 -0
  59. /package/dist/generated/{types → typesTS}/standalone/Resource_RawStrategy.js +0 -0
  60. /package/dist/generated/{types → typesTS}/standalone/Resource_ResourceType.js +0 -0
  61. /package/dist/generated/{types → typesTS}/standalone/Resource_RunnableStrategy.js +0 -0
  62. /package/dist/generated/{types/types.js → typesTS/typesTS.js} +0 -0
  63. /package/dist/scripts/{generateStandaloneType.d.ts → generateStandaloneTypeTS.d.ts} +0 -0
  64. /package/dist/scripts/{generateTypes.d.ts → generateTypesTS.d.ts} +0 -0
  65. /package/src/generated/{types → typesTS}/standalone/Resource_Genesis.js +0 -0
  66. /package/src/generated/{types → typesTS}/standalone/Resource_Job.js +0 -0
  67. /package/src/generated/{types → typesTS}/standalone/Resource_RawStrategy.js +0 -0
  68. /package/src/generated/{types → typesTS}/standalone/Resource_ResourceType.js +0 -0
  69. /package/src/generated/{types → typesTS}/standalone/Resource_RunnableStrategy.js +0 -0
  70. /package/src/generated/{types/types.js → typesTS/typesTS.js} +0 -0
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
- export type { Resource_Genesis as Resource_GenesisJson } from './generated/types/standalone/Resource_Genesis.js';
8
- export type { Resource_ResourceType as Resource_ResourceTypeJson } from './generated/types/standalone/Resource_ResourceType.js';
9
- export type { Resource_Job as Resource_JobJson } from './generated/types/standalone/Resource_Job.js';
10
- export type { Resource_RawStrategy as Resource_RawStrategyJson } from './generated/types/standalone/Resource_RawStrategy.js';
11
- 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';
5
+ export type { Resource_Genesis as Resource_GenesisJson } from './generated/typesTS/standalone/Resource_Genesis.js';
6
+ export type { Resource_ResourceType as Resource_ResourceTypeJson } from './generated/typesTS/standalone/Resource_ResourceType.js';
7
+ export type { Resource_Job as Resource_JobJson } from './generated/typesTS/standalone/Resource_Job.js';
8
+ export type { Resource_RawStrategy as Resource_RawStrategyJson } from './generated/typesTS/standalone/Resource_RawStrategy.js';
9
+ export type { Resource_RunnableStrategy as Resource_RunnableStrategyJson } from './generated/typesTS/standalone/Resource_RunnableStrategy.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, Domain as DomainJson, 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/typesTS/typesTS.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';
@@ -14,8 +14,8 @@ export declare class SchemaConfig {
14
14
  private readonly schemasDir;
15
15
  private readonly resourcesDir;
16
16
  private readonly dependencyMapPath;
17
- private readonly typesSrcDir;
18
- private readonly typesDistDir;
17
+ private readonly typesTsSrcDir;
18
+ private readonly typesTsDistDir;
19
19
  private readonly baseUrl;
20
20
  private readonly version;
21
21
  constructor();
@@ -30,14 +30,14 @@ export declare class SchemaConfig {
30
30
  getSchemaPath(filename: string): string;
31
31
  getSchemasStandaloneDir(): string;
32
32
  getSchemaStandalonePath(filename: string): string;
33
- getTypesSrcDir(): string;
34
- getTypesDistDir(): string;
35
- getTypesSrcPath(filename: string): string;
36
- getTypesDistPath(filename: string): string;
37
- getTypesStandaloneSrcDir(): string;
38
- getTypesStandaloneDistDir(): string;
39
- getTypesStandaloneSrcPath(filename: string): string;
40
- getTypesStandaloneDistPath(filename: string): string;
33
+ getTypesTsSrcDir(): string;
34
+ getTypesTsDistDir(): string;
35
+ getTypesTsSrcPath(filename: string): string;
36
+ getTypesTsDistPath(filename: string): string;
37
+ getStandaloneTypeTsSrcDir(): string;
38
+ getStandaloneTypeTsDistDir(): string;
39
+ getStandaloneTypeTsSrcPath(filename: string): string;
40
+ getStandaloneTypeTsDistPath(filename: string): string;
41
41
  getResourcesDir(): string;
42
42
  getDependencyMapPath(): string;
43
43
  getBaseUrl(): string;
@@ -25,9 +25,9 @@ export class SchemaConfig {
25
25
  this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/generated/normalized');
26
26
  this.schemasDir = getEnv('TP_SCHEMA_SCHEMAS_DIR', 'src/generated/schemas');
27
27
  this.resourcesDir = getEnv('TP_SCHEMA_RESOURCES_DIR', 'src/generated/resources');
28
- this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencyMap.json');
29
- this.typesSrcDir = getEnv('TP_SCHEMA_TYPES_SRC_DIR', 'src/generated/types');
30
- this.typesDistDir = getEnv('TP_SCHEMA_TYPES_DIST_DIR', 'dist/generated/types');
28
+ this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencies/dependencyMap.json');
29
+ this.typesTsSrcDir = getEnv('TP_SCHEMA_TYPES_TS_SRC_DIR', 'src/generated/typesTS');
30
+ this.typesTsDistDir = getEnv('TP_SCHEMA_TYPES_TS_DIST_DIR', 'dist/generated/typesTS');
31
31
  this.baseUrl = getEnv('TP_SCHEMA_BASE_URL', 'https://schemas.toolproof.com');
32
32
  this.version = getEnv('TP_SCHEMA_VERSION', 'v0');
33
33
  }
@@ -74,33 +74,33 @@ export class SchemaConfig {
74
74
  getSchemaStandalonePath(filename) {
75
75
  return path.join(this.getSchemasStandaloneDir(), filename);
76
76
  }
77
- getTypesSrcDir() {
78
- return path.isAbsolute(this.typesSrcDir)
79
- ? this.typesSrcDir
80
- : path.join(this.root, this.typesSrcDir);
77
+ getTypesTsSrcDir() {
78
+ return path.isAbsolute(this.typesTsSrcDir)
79
+ ? this.typesTsSrcDir
80
+ : path.join(this.root, this.typesTsSrcDir);
81
81
  }
82
- getTypesDistDir() {
83
- return path.isAbsolute(this.typesDistDir)
84
- ? this.typesDistDir
85
- : path.join(this.root, this.typesDistDir);
82
+ getTypesTsDistDir() {
83
+ return path.isAbsolute(this.typesTsDistDir)
84
+ ? this.typesTsDistDir
85
+ : path.join(this.root, this.typesTsDistDir);
86
86
  }
87
- getTypesSrcPath(filename) {
88
- return path.join(this.getTypesSrcDir(), filename);
87
+ getTypesTsSrcPath(filename) {
88
+ return path.join(this.getTypesTsSrcDir(), filename);
89
89
  }
90
- getTypesDistPath(filename) {
91
- return path.join(this.getTypesDistDir(), filename);
90
+ getTypesTsDistPath(filename) {
91
+ return path.join(this.getTypesTsDistDir(), filename);
92
92
  }
93
- getTypesStandaloneSrcDir() {
94
- return path.join(this.getTypesSrcDir(), 'standalone');
93
+ getStandaloneTypeTsSrcDir() {
94
+ return path.join(this.getTypesTsSrcDir(), 'standalone');
95
95
  }
96
- getTypesStandaloneDistDir() {
97
- return path.join(this.getTypesDistDir(), 'standalone');
96
+ getStandaloneTypeTsDistDir() {
97
+ return path.join(this.getTypesTsDistDir(), 'standalone');
98
98
  }
99
- getTypesStandaloneSrcPath(filename) {
100
- return path.join(this.getTypesStandaloneSrcDir(), filename);
99
+ getStandaloneTypeTsSrcPath(filename) {
100
+ return path.join(this.getStandaloneTypeTsSrcDir(), filename);
101
101
  }
102
- getTypesStandaloneDistPath(filename) {
103
- return path.join(this.getTypesStandaloneDistDir(), filename);
102
+ getStandaloneTypeTsDistPath(filename) {
103
+ return path.join(this.getStandaloneTypeTsDistDir(), filename);
104
104
  }
105
105
  getResourcesDir() {
106
106
  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();