@toolproof-core/schema 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/types/standalone/Resource_Genesis.d.ts +3 -0
- package/dist/generated/types/standalone/Resource_Job.d.ts +3 -0
- package/dist/generated/types/standalone/Resource_RawStrategy.d.ts +3 -0
- package/dist/generated/types/standalone/Resource_ResourceType.d.ts +3 -0
- package/dist/generated/types/standalone/Resource_RunnableStrategy.d.ts +3 -0
- package/dist/generated/types/types.d.ts +487 -548
- package/dist/scripts/_lib/config.d.ts +12 -6
- package/dist/scripts/_lib/config.js +33 -15
- package/dist/scripts/{extractSchemas.js → extractSchemasFromResourceTypeShells.js} +14 -27
- package/dist/scripts/generateDependencies.js +2 -2
- package/dist/scripts/generateSchemaShims.js +22 -4
- package/dist/scripts/{extractSubSchemaWithDefs.js → generateStandaloneSchema.js} +12 -34
- package/dist/scripts/{generateResourceTypeType.js → generateStandaloneType.js} +23 -14
- package/dist/scripts/generateTypes.js +1 -1
- package/dist/scripts/{rewriteAnchors.js → normalizeAnchorsToPointers.js} +2 -2
- package/dist/scripts/{generateResourceShells.js → wrapResourceTypesWithResourceShells.js} +12 -34
- package/package.json +8 -8
- package/src/Genesis.json +44 -87
- package/src/generated/{dependencies.json → dependencyMap.json} +207 -214
- package/src/generated/{resourceTypes → normalized}/Genesis.json +44 -87
- package/src/generated/resources/Genesis.json +415 -478
- package/src/generated/schemas/Genesis.json +1026 -1057
- package/src/generated/schemas/{Goal.json → standalone/Goal.json} +34 -34
- package/src/generated/schemas/{Job.json → standalone/Job.json} +85 -85
- package/src/generated/schemas/{RawStrategy.json → standalone/RawStrategy.json} +238 -263
- package/src/generated/schemas/{ResourceType.json → standalone/ResourceType.json} +60 -60
- package/src/generated/schemas/{RunnableStrategy.json → standalone/RunnableStrategy.json} +296 -321
- package/src/generated/schemas/{StrategyRun.json → standalone/StrategyRun.json} +390 -421
- package/src/generated/types/standalone/Resource_Genesis.d.ts +3 -0
- package/src/generated/types/standalone/Resource_Job.d.ts +3 -0
- package/src/generated/types/standalone/Resource_RawStrategy.d.ts +3 -0
- package/src/generated/types/standalone/Resource_ResourceType.d.ts +3 -0
- package/src/generated/types/standalone/Resource_RunnableStrategy.d.ts +3 -0
- package/src/generated/types/types.d.ts +487 -548
- package/src/generated/types/types.js +1 -1
- package/src/scripts/_lib/config.ts +42 -18
- package/src/scripts/extractSchemasFromResourceTypeShells.ts +218 -0
- package/src/scripts/generateDependencies.ts +2 -2
- package/src/scripts/generateSchemaShims.ts +24 -4
- package/src/scripts/{extractSubSchemaWithDefs.ts → generateStandaloneSchema.ts} +13 -34
- package/src/scripts/{generateResourceTypeType.ts → generateStandaloneType.ts} +23 -14
- package/src/scripts/generateTypes.ts +1 -1
- package/src/scripts/{rewriteAnchors.ts → normalizeAnchorsToPointers.ts} +3 -3
- package/src/scripts/{generateResourceShells.ts → wrapResourceTypesWithResourceShells.ts} +13 -34
- package/dist/generated/types/Resource_Genesis.d.ts +0 -3
- package/dist/generated/types/Resource_Job.d.ts +0 -3
- package/dist/generated/types/Resource_RawStrategy.d.ts +0 -3
- package/dist/generated/types/Resource_ResourceType.d.ts +0 -3
- package/dist/generated/types/Resource_RunnableStrategy.d.ts +0 -3
- package/src/Roadmap.json +0 -102
- package/src/generated/types/Resource_Genesis.d.ts +0 -3
- package/src/generated/types/Resource_Job.d.ts +0 -3
- package/src/generated/types/Resource_RawStrategy.d.ts +0 -3
- package/src/generated/types/Resource_ResourceType.d.ts +0 -3
- package/src/generated/types/Resource_RunnableStrategy.d.ts +0 -3
- package/src/scripts/extractSchemas.ts +0 -229
- /package/dist/generated/types/{Resource_Genesis.js → standalone/Resource_Genesis.js} +0 -0
- /package/dist/generated/types/{Resource_Job.js → standalone/Resource_Job.js} +0 -0
- /package/dist/generated/types/{Resource_RawStrategy.js → standalone/Resource_RawStrategy.js} +0 -0
- /package/dist/generated/types/{Resource_ResourceType.js → standalone/Resource_ResourceType.js} +0 -0
- /package/dist/generated/types/{Resource_RunnableStrategy.js → standalone/Resource_RunnableStrategy.js} +0 -0
- /package/dist/scripts/{extractSchemas.d.ts → extractSchemasFromResourceTypeShells.d.ts} +0 -0
- /package/dist/scripts/{extractSubSchemaWithDefs.d.ts → generateStandaloneSchema.d.ts} +0 -0
- /package/dist/scripts/{generateResourceShells.d.ts → generateStandaloneType.d.ts} +0 -0
- /package/dist/scripts/{generateResourceTypeType.d.ts → normalizeAnchorsToPointers.d.ts} +0 -0
- /package/dist/scripts/{rewriteAnchors.d.ts → wrapResourceTypesWithResourceShells.d.ts} +0 -0
- /package/src/generated/{resourceTypes → normalized}/Genesis.ts +0 -0
- /package/src/generated/schemas/{Goal.ts → standalone/Goal.ts} +0 -0
- /package/src/generated/schemas/{Job.ts → standalone/Job.ts} +0 -0
- /package/src/generated/schemas/{RawStrategy.ts → standalone/RawStrategy.ts} +0 -0
- /package/src/generated/schemas/{ResourceType.ts → standalone/ResourceType.ts} +0 -0
- /package/src/generated/schemas/{RunnableStrategy.ts → standalone/RunnableStrategy.ts} +0 -0
- /package/src/generated/schemas/{StrategyRun.ts → standalone/StrategyRun.ts} +0 -0
- /package/src/generated/types/{Resource_Genesis.js → standalone/Resource_Genesis.js} +0 -0
- /package/src/generated/types/{Resource_Job.js → standalone/Resource_Job.js} +0 -0
- /package/src/generated/types/{Resource_RawStrategy.js → standalone/Resource_RawStrategy.js} +0 -0
- /package/src/generated/types/{Resource_ResourceType.js → standalone/Resource_ResourceType.js} +0 -0
- /package/src/generated/types/{Resource_RunnableStrategy.js → standalone/Resource_RunnableStrategy.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
export {}
|
|
1
|
+
export {}
|
|
@@ -23,11 +23,11 @@ export class SchemaConfig {
|
|
|
23
23
|
private readonly sourceDir: string;
|
|
24
24
|
private readonly sourceFile: string;
|
|
25
25
|
private readonly normalizedDir: string;
|
|
26
|
-
private readonly
|
|
26
|
+
private readonly schemasDir: string;
|
|
27
|
+
private readonly resourcesDir: string;
|
|
28
|
+
private readonly dependencyMapPath: string;
|
|
27
29
|
private readonly typesSrcDir: string;
|
|
28
30
|
private readonly typesDistDir: string;
|
|
29
|
-
private readonly generatedResourcesDir: string;
|
|
30
|
-
private readonly dependencyMapPath: string;
|
|
31
31
|
|
|
32
32
|
// Schema metadata
|
|
33
33
|
private readonly baseUrl: string;
|
|
@@ -38,14 +38,14 @@ export class SchemaConfig {
|
|
|
38
38
|
this.root = getEnv('TP_SCHEMA_ROOT', process.cwd());
|
|
39
39
|
this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/');
|
|
40
40
|
this.sourceFile = getEnv('TP_SCHEMA_SOURCE_FILE', 'Genesis.json');
|
|
41
|
-
// Intermediate, generated artifact produced by
|
|
41
|
+
// Intermediate, generated artifact produced by normalizeAnchorsToPointers.
|
|
42
42
|
// This should NOT live next to the source-of-truth schemas.
|
|
43
|
-
this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/generated/
|
|
44
|
-
this.
|
|
43
|
+
this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/generated/normalized');
|
|
44
|
+
this.schemasDir = getEnv('TP_SCHEMA_SCHEMAS_DIR', 'src/generated/schemas');
|
|
45
|
+
this.resourcesDir = getEnv('TP_SCHEMA_RESOURCES_DIR', 'src/generated/resources');
|
|
46
|
+
this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencyMap.json');
|
|
45
47
|
this.typesSrcDir = getEnv('TP_SCHEMA_TYPES_SRC_DIR', 'src/generated/types');
|
|
46
48
|
this.typesDistDir = getEnv('TP_SCHEMA_TYPES_DIST_DIR', 'dist/generated/types');
|
|
47
|
-
this.generatedResourcesDir = getEnv('TP_SCHEMA_RESOURCES_DIR', 'src/generated/resources');
|
|
48
|
-
this.dependencyMapPath = getEnv('TP_SCHEMA_DEPENDENCY_MAP_PATH', 'src/generated/dependencies.json');
|
|
49
49
|
this.baseUrl = getEnv('TP_SCHEMA_BASE_URL', 'https://schemas.toolproof.com');
|
|
50
50
|
this.version = getEnv('TP_SCHEMA_VERSION', 'v0');
|
|
51
51
|
}
|
|
@@ -86,14 +86,22 @@ export class SchemaConfig {
|
|
|
86
86
|
return path.join(this.getNormalizedDir(), this.getNormalizedSourceFile());
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
return path.isAbsolute(this.
|
|
91
|
-
? this.
|
|
92
|
-
: path.join(this.root, this.
|
|
89
|
+
getSchemasDir(): string {
|
|
90
|
+
return path.isAbsolute(this.schemasDir)
|
|
91
|
+
? this.schemasDir
|
|
92
|
+
: path.join(this.root, this.schemasDir);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getSchemaPath(filename: string): string {
|
|
96
|
+
return path.join(this.getSchemasDir(), filename);
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
return path.join(this.
|
|
99
|
+
getSchemasStandaloneDir(): string {
|
|
100
|
+
return path.join(this.getSchemasDir(), 'standalone');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getSchemaStandalonePath(filename: string): string {
|
|
104
|
+
return path.join(this.getSchemasStandaloneDir(), filename);
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
getTypesSrcDir(): string {
|
|
@@ -116,10 +124,26 @@ export class SchemaConfig {
|
|
|
116
124
|
return path.join(this.getTypesDistDir(), filename);
|
|
117
125
|
}
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
return path.
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
getTypesStandaloneSrcDir(): string {
|
|
128
|
+
return path.join(this.getTypesSrcDir(), 'standalone');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getTypesStandaloneDistDir(): string {
|
|
132
|
+
return path.join(this.getTypesDistDir(), 'standalone');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getTypesStandaloneSrcPath(filename: string): string {
|
|
136
|
+
return path.join(this.getTypesStandaloneSrcDir(), filename);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getTypesStandaloneDistPath(filename: string): string {
|
|
140
|
+
return path.join(this.getTypesStandaloneDistDir(), filename);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getResourcesDir(): string {
|
|
144
|
+
return path.isAbsolute(this.resourcesDir)
|
|
145
|
+
? this.resourcesDir
|
|
146
|
+
: path.join(this.root, this.resourcesDir);
|
|
123
147
|
}
|
|
124
148
|
|
|
125
149
|
getDependencyMapPath(): string {
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { getConfig } from "./_lib/config.js";
|
|
5
|
+
|
|
6
|
+
type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]: JSONValue };
|
|
7
|
+
|
|
8
|
+
interface ExtractOptions {
|
|
9
|
+
inPath: string;
|
|
10
|
+
outPath: string;
|
|
11
|
+
topLevelId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseArgs(): ExtractOptions {
|
|
15
|
+
const config = getConfig();
|
|
16
|
+
const argv = process.argv.slice(2);
|
|
17
|
+
let inPath = "";
|
|
18
|
+
let outPath = "";
|
|
19
|
+
let topLevelId: string | undefined;
|
|
20
|
+
for (let i = 0; i < argv.length; i++) {
|
|
21
|
+
const a = argv[i];
|
|
22
|
+
if (a === "--in" && i + 1 < argv.length) inPath = argv[++i];
|
|
23
|
+
else if (a === "--out" && i + 1 < argv.length) outPath = argv[++i];
|
|
24
|
+
else if (a === "--id" && i + 1 < argv.length) {
|
|
25
|
+
let v = argv[++i];
|
|
26
|
+
// Strip accidental surrounding quotes from PowerShell/cmd
|
|
27
|
+
if ((v.startsWith("'") && v.endsWith("'")) || (v.startsWith('"') && v.endsWith('"'))) {
|
|
28
|
+
v = v.slice(1, -1);
|
|
29
|
+
}
|
|
30
|
+
topLevelId = v;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Use config defaults if not provided via CLI
|
|
35
|
+
if (!inPath) {
|
|
36
|
+
// Use generated/normalized version with anchor refs rewritten to pointers
|
|
37
|
+
inPath = config.getNormalizedSourcePath();
|
|
38
|
+
}
|
|
39
|
+
if (!outPath) {
|
|
40
|
+
outPath = config.getSchemaPath(config.getSourceFile());
|
|
41
|
+
}
|
|
42
|
+
if (!topLevelId) {
|
|
43
|
+
topLevelId = config.getSchemaId('Genesis');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Resolve to absolute paths from project root
|
|
47
|
+
const cwd = config.getRoot();
|
|
48
|
+
const wasInRelative = !path.isAbsolute(inPath);
|
|
49
|
+
const wasOutRelative = !path.isAbsolute(outPath);
|
|
50
|
+
if (wasInRelative) inPath = path.join(cwd, inPath);
|
|
51
|
+
if (wasOutRelative) outPath = path.join(cwd, outPath);
|
|
52
|
+
// Fallback: resolve relative to script directory if not found
|
|
53
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
if (!fs.existsSync(inPath) && wasInRelative) inPath = path.resolve(scriptDir, inPath);
|
|
55
|
+
const outDir = path.dirname(outPath);
|
|
56
|
+
if (!fs.existsSync(outDir) && wasOutRelative) {
|
|
57
|
+
// Try making directory relative to script dir
|
|
58
|
+
const altOut = path.resolve(scriptDir, outPath);
|
|
59
|
+
const altOutDir = path.dirname(altOut);
|
|
60
|
+
if (!fs.existsSync(path.dirname(outPath))) {
|
|
61
|
+
// Prefer creating outDir at cwd location if possible; otherwise fallback below when writing
|
|
62
|
+
} else {
|
|
63
|
+
outPath = altOut;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { inPath, outPath, topLevelId };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ATTENTION: we don't want heuristic behavior.
|
|
70
|
+
// Heuristic: determine if a node is a Type shell
|
|
71
|
+
function isTypeShell(node: any): boolean {
|
|
72
|
+
return (
|
|
73
|
+
node && typeof node === "object" && !Array.isArray(node) &&
|
|
74
|
+
// Treat any object that has an 'nucleusSchema' AND 'identity' as a Type shell
|
|
75
|
+
// This prevents false positives where 'nucleusSchema' is just a regular schema property
|
|
76
|
+
node.nucleusSchema && typeof node.nucleusSchema === "object" &&
|
|
77
|
+
node.identity && typeof node.identity === "string"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Merge $defs into target, without overwriting existing keys unless identical
|
|
82
|
+
function mergeDefs(target: Record<string, JSONValue>, source?: any, label?: string) {
|
|
83
|
+
if (!source || typeof source !== "object") return;
|
|
84
|
+
const src = (source as any)["$defs"];
|
|
85
|
+
if (!src || typeof src !== "object") return;
|
|
86
|
+
for (const [k, v] of Object.entries(src)) {
|
|
87
|
+
if (!(k in target)) {
|
|
88
|
+
target[k] = v as JSONValue;
|
|
89
|
+
} else {
|
|
90
|
+
// Best-effort: if duplicate key, require structural equality; otherwise, namespace
|
|
91
|
+
const existing = JSON.stringify(target[k]);
|
|
92
|
+
const incoming = JSON.stringify(v);
|
|
93
|
+
if (existing !== incoming) {
|
|
94
|
+
const altKey = `${k}__from_${(label || "defs").replace(/[^A-Za-z0-9_]+/g, "_")}`;
|
|
95
|
+
if (!(altKey in target)) target[altKey] = v as JSONValue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Deeply traverse an object replacing any Type shell with its nucleusSchema,
|
|
102
|
+
// and hoist its inner $defs to topDefs. Prevent infinite recursion with a visited set.
|
|
103
|
+
function unwrapTypes(node: JSONValue, topDefs: Record<string, JSONValue>, labelPath: string[] = [], visited = new Set<any>()): JSONValue {
|
|
104
|
+
if (node && typeof node === "object") {
|
|
105
|
+
if (visited.has(node)) return node; // avoid cycles
|
|
106
|
+
visited.add(node);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (isTypeShell(node)) {
|
|
110
|
+
const env = node as any;
|
|
111
|
+
const inner = env.nucleusSchema;
|
|
112
|
+
// Hoist inner $defs before stripping
|
|
113
|
+
mergeDefs(topDefs, inner, labelPath.join("_"));
|
|
114
|
+
// Return the inner schema itself, after also unwrapping any nested shells it may contain
|
|
115
|
+
const unwrappedInner = unwrapTypes(inner as JSONValue, topDefs, labelPath.concat([String(env.identity || "env")]), visited);
|
|
116
|
+
return unwrappedInner;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (Array.isArray(node)) {
|
|
120
|
+
return node.map((v, i) => unwrapTypes(v, topDefs, labelPath.concat([String(i)]), visited)) as JSONValue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (node && typeof node === "object") {
|
|
124
|
+
const out: Record<string, JSONValue> = {};
|
|
125
|
+
for (const [k, v] of Object.entries(node)) {
|
|
126
|
+
if (k === "$defs" && v && typeof v === "object" && !Array.isArray(v)) {
|
|
127
|
+
// Process nested $defs: unwrap each entry value if it's a Type shell
|
|
128
|
+
const defsOut: Record<string, JSONValue> = {};
|
|
129
|
+
for (const [dk, dv] of Object.entries(v as any)) {
|
|
130
|
+
const unwrapped = unwrapTypes(dv as JSONValue, topDefs, labelPath.concat(["$defs", dk]), visited);
|
|
131
|
+
defsOut[dk] = unwrapped;
|
|
132
|
+
}
|
|
133
|
+
out[k] = defsOut;
|
|
134
|
+
} else {
|
|
135
|
+
out[k] = unwrapTypes(v as JSONValue, topDefs, labelPath.concat([k]), visited);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return node;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Pure function that takes a schema document and options, and returns the flattened schema.
|
|
146
|
+
* Performs no I/O operations.
|
|
147
|
+
*/
|
|
148
|
+
function extractSchemaLogic(doc: any, topLevelId?: string): any {
|
|
149
|
+
if (!doc || typeof doc !== "object" || !doc.nucleusSchema) {
|
|
150
|
+
throw new Error("Input must be a Type JSON with an nucleusSchema at the top level");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const topSchema = (doc as any).nucleusSchema;
|
|
154
|
+
|
|
155
|
+
// Collect $defs so that any '#/$defs/...' pointers can be resolved from the root.
|
|
156
|
+
const outDefs: Record<string, JSONValue> = {};
|
|
157
|
+
// Seed with top-level $defs (if any) before unwrapping
|
|
158
|
+
mergeDefs(outDefs, topSchema, "top");
|
|
159
|
+
|
|
160
|
+
// Unwrap the entire top schema tree so that any nested Type shells become raw schemas
|
|
161
|
+
const flattened = unwrapTypes(topSchema as JSONValue, outDefs, ["nucleusSchema"]);
|
|
162
|
+
|
|
163
|
+
// Assemble output: force $schema, optionally set $id, hoist collected $defs
|
|
164
|
+
let base: any;
|
|
165
|
+
if (flattened && typeof flattened === "object" && !Array.isArray(flattened)) {
|
|
166
|
+
base = { ...(flattened as any) };
|
|
167
|
+
} else {
|
|
168
|
+
// If flattened is not an object (should be rare for a top-level schema), wrap it
|
|
169
|
+
base = { const: flattened };
|
|
170
|
+
}
|
|
171
|
+
// Assemble, but avoid duplicating $id: if the flattened base already has $id, prefer it.
|
|
172
|
+
const output: Record<string, JSONValue> = {
|
|
173
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
174
|
+
...base,
|
|
175
|
+
};
|
|
176
|
+
if (topLevelId && !(output as any).$id) {
|
|
177
|
+
(output as any).$id = topLevelId;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Enforce presence of $id: schema must declare an absolute identity.
|
|
181
|
+
if (!(output as any).$id) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
"Flattened schema must define $id. Provide it via CLI --id or include $id in the source nucleusSchema."
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Hoist collected defs into output.$defs, taking care not to clobber any existing
|
|
188
|
+
if (!("$defs" in output)) output.$defs = {} as any;
|
|
189
|
+
const finalDefs: Record<string, JSONValue> = (output.$defs as any) || {};
|
|
190
|
+
for (const [k, v] of Object.entries(outDefs)) {
|
|
191
|
+
if (!(k in finalDefs)) finalDefs[k] = v;
|
|
192
|
+
}
|
|
193
|
+
output.$defs = finalDefs as any;
|
|
194
|
+
|
|
195
|
+
// Preserve natural key ordering (do not reorder for readability)
|
|
196
|
+
return output;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function main() {
|
|
200
|
+
const { inPath, outPath, topLevelId } = parseArgs();
|
|
201
|
+
|
|
202
|
+
if (!fs.existsSync(inPath)) {
|
|
203
|
+
console.error(`Input file not found at ${inPath}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const raw = fs.readFileSync(inPath, "utf8");
|
|
208
|
+
const doc = JSON.parse(raw);
|
|
209
|
+
|
|
210
|
+
// Core logic is now in a pure function
|
|
211
|
+
const ordered = extractSchemaLogic(doc, topLevelId);
|
|
212
|
+
|
|
213
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
214
|
+
fs.writeFileSync(outPath, JSON.stringify(ordered, null, 4), "utf8");
|
|
215
|
+
console.log(`Wrote flattened schema to ${outPath}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main();
|
|
@@ -91,11 +91,11 @@ function generateDependencyMapLogic(doc: any): Record<string, string[]> {
|
|
|
91
91
|
async function main() {
|
|
92
92
|
const config = getConfig();
|
|
93
93
|
|
|
94
|
-
const inPath = config.
|
|
94
|
+
const inPath = config.getSchemaPath("Genesis.json");
|
|
95
95
|
const outPath = config.getDependencyMapPath();
|
|
96
96
|
|
|
97
97
|
if (!fs.existsSync(inPath)) {
|
|
98
|
-
console.error(`Genesis schema not found at ${inPath}. Run
|
|
98
|
+
console.error(`Genesis schema not found at ${inPath}. Run extractSchemasFromResourceTypeShells first.`);
|
|
99
99
|
process.exit(1);
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -10,8 +10,9 @@ import { getConfig } from './_lib/config.js';
|
|
|
10
10
|
* and type inference when importing schemas in TypeScript.
|
|
11
11
|
*
|
|
12
12
|
* Generates shims for:
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
13
|
+
* - schemas dir: config.getSchemasDir() (schema files)
|
|
14
|
+
* - standalone schemas dir: config.getSchemasStandaloneDir() (standalone schema files)
|
|
15
|
+
* - resources dir: config.getResourcesDir() (resource envelope files)
|
|
15
16
|
*
|
|
16
17
|
* Usage: node ./dist/scripts/generateSchemaShims.js
|
|
17
18
|
*/
|
|
@@ -45,8 +46,9 @@ function getShimsForFiles(jsonFiles: string[], variableName: string): Record<str
|
|
|
45
46
|
|
|
46
47
|
async function main() {
|
|
47
48
|
const config = getConfig();
|
|
48
|
-
const schemasDir = config.
|
|
49
|
-
const
|
|
49
|
+
const schemasDir = config.getSchemasDir();
|
|
50
|
+
const standaloneSchemasDir = config.getSchemasStandaloneDir();
|
|
51
|
+
const resourcesDir = config.getResourcesDir();
|
|
50
52
|
const generatedResourceTypesDir = config.getNormalizedDir();
|
|
51
53
|
|
|
52
54
|
let totalCount = 0;
|
|
@@ -69,6 +71,24 @@ async function main() {
|
|
|
69
71
|
console.warn(`Schemas directory not found at ${schemasDir}`);
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
// Process standalone schemas directory
|
|
75
|
+
if (fs.existsSync(standaloneSchemasDir)) {
|
|
76
|
+
const files = fs.readdirSync(standaloneSchemasDir);
|
|
77
|
+
const jsonFiles = files.filter(f => f.endsWith('.json') && !f.startsWith('.'));
|
|
78
|
+
|
|
79
|
+
const shims = getShimsForFiles(jsonFiles, 'schema');
|
|
80
|
+
|
|
81
|
+
for (const [tsFile, content] of Object.entries(shims)) {
|
|
82
|
+
const tsPath = path.join(standaloneSchemasDir, tsFile);
|
|
83
|
+
fs.writeFileSync(tsPath, content, 'utf-8');
|
|
84
|
+
console.log(`Generated ${tsFile} in ${standaloneSchemasDir}`);
|
|
85
|
+
totalCount++;
|
|
86
|
+
}
|
|
87
|
+
console.log(`Generated ${jsonFiles.length} TypeScript standalone schema shims in ${standaloneSchemasDir}`);
|
|
88
|
+
} else {
|
|
89
|
+
// Standalone schemas are optional
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
// Process resources directory
|
|
73
93
|
if (fs.existsSync(resourcesDir)) {
|
|
74
94
|
const files = fs.readdirSync(resourcesDir);
|
|
@@ -3,26 +3,18 @@ import path from 'path';
|
|
|
3
3
|
import { getConfig } from './_lib/config.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* object holding all direct & transitive local $defs dependencies (by #/$defs/... $ref).
|
|
9
|
-
*
|
|
10
|
-
* Genesis.json itself is left unchanged.
|
|
6
|
+
* Create a standalone JSON Schema for a single $defs entry under Genesis.json,
|
|
7
|
+
* embedding all direct & transitive local $defs dependencies.
|
|
11
8
|
*
|
|
12
9
|
* Usage:
|
|
13
|
-
* node ./dist/scripts/
|
|
14
|
-
*
|
|
15
|
-
* Writes: src/genesis/generated/schemas/<DefName>.json
|
|
10
|
+
* node ./dist/scripts/generateStandaloneSchema.js --name <DefName>
|
|
16
11
|
*/
|
|
12
|
+
|
|
17
13
|
/**
|
|
18
|
-
* Pure function to extract a subschema and its dependencies from a genesis
|
|
14
|
+
* Pure function to extract a subschema and its dependencies from a flattened genesis schema.
|
|
19
15
|
* Performs no I/O operations.
|
|
20
|
-
*
|
|
21
|
-
* @param genesis The root Genesis schema object
|
|
22
|
-
* @param name The name of the definition to extract
|
|
23
|
-
* @returns The standalone subschema
|
|
24
16
|
*/
|
|
25
|
-
function
|
|
17
|
+
function extractStandaloneSubschemaLogic(genesis: any, name: string): any {
|
|
26
18
|
const rootDefs: Record<string, any> | undefined = genesis.$defs;
|
|
27
19
|
if (!rootDefs || typeof rootDefs !== 'object') {
|
|
28
20
|
throw new Error('No $defs object found in Genesis.json');
|
|
@@ -33,14 +25,11 @@ function extractSubSchemaLogic(genesis: any, name: string): any {
|
|
|
33
25
|
throw new Error(`Subschema named '${name}' not found under $defs in Genesis.json`);
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
// Collect transitive local $defs names referenced by target
|
|
37
28
|
const needed = collectLocalDefClosure(target, rootDefs);
|
|
38
29
|
|
|
39
|
-
// Build output schema: clone target and attach collected subset as $defs
|
|
40
30
|
const targetClone = deepClone(target);
|
|
41
31
|
const defsOut: Record<string, any> = {};
|
|
42
32
|
for (const defName of needed) {
|
|
43
|
-
// Avoid including the target itself inside its own $defs (mirrors previous pattern)
|
|
44
33
|
if (defName === name) continue;
|
|
45
34
|
const defSchema = rootDefs[defName];
|
|
46
35
|
if (defSchema === undefined) {
|
|
@@ -50,7 +39,6 @@ function extractSubSchemaLogic(genesis: any, name: string): any {
|
|
|
50
39
|
defsOut[defName] = deepClone(defSchema);
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
// Merge any pre-existing inner $defs of targetClone (if present) giving precedence to collected ones
|
|
54
42
|
const existingInner = isObject(targetClone.$defs) ? targetClone.$defs : {};
|
|
55
43
|
targetClone.$defs = { ...existingInner, ...defsOut };
|
|
56
44
|
|
|
@@ -65,12 +53,12 @@ async function main() {
|
|
|
65
53
|
process.exit(1);
|
|
66
54
|
}
|
|
67
55
|
|
|
68
|
-
const schemasDir = config.
|
|
56
|
+
const schemasDir = config.getSchemasDir();
|
|
69
57
|
const genesisPath = path.join(schemasDir, config.getSourceFile());
|
|
70
|
-
const outPath = config.
|
|
58
|
+
const outPath = config.getSchemaStandalonePath(`${name}.json`);
|
|
71
59
|
|
|
72
60
|
if (!fs.existsSync(genesisPath)) {
|
|
73
|
-
console.error(`Genesis.json not found at ${genesisPath}
|
|
61
|
+
console.error(`Genesis.json not found at ${genesisPath}. Run extractSchemasFromResourceTypeShells first.`);
|
|
74
62
|
process.exit(1);
|
|
75
63
|
}
|
|
76
64
|
|
|
@@ -78,20 +66,17 @@ async function main() {
|
|
|
78
66
|
const genesis = JSON.parse(raw);
|
|
79
67
|
|
|
80
68
|
try {
|
|
81
|
-
|
|
82
|
-
const result = extractSubSchemaLogic(genesis, name);
|
|
69
|
+
const result = extractStandaloneSubschemaLogic(genesis, name);
|
|
83
70
|
|
|
84
|
-
// I/O: Write the result
|
|
85
71
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
86
72
|
fs.writeFileSync(outPath, JSON.stringify(result, null, 2) + '\n');
|
|
87
|
-
console.log(`
|
|
73
|
+
console.log(`Created standalone subschema '${name}' -> ${outPath}`);
|
|
88
74
|
} catch (error: any) {
|
|
89
75
|
console.error(error.message);
|
|
90
76
|
process.exit(1);
|
|
91
77
|
}
|
|
92
78
|
}
|
|
93
79
|
|
|
94
|
-
// ---- Helpers ----
|
|
95
80
|
function parseArgs(args: string[]): { name?: string } {
|
|
96
81
|
let name: string | undefined;
|
|
97
82
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -121,30 +106,24 @@ function deepClone<T>(v: T): T {
|
|
|
121
106
|
}
|
|
122
107
|
|
|
123
108
|
function extractPointerDefName(ref: string): string | null {
|
|
124
|
-
// Accept refs like '#/$defs/Name' only (single-level under $defs)
|
|
125
109
|
if (!ref || !ref.startsWith('#/')) return null;
|
|
126
|
-
const parts = ref.slice(2).split('/');
|
|
110
|
+
const parts = ref.slice(2).split('/');
|
|
127
111
|
if (parts.length !== 2) return null;
|
|
128
112
|
if (parts[0] !== '$defs') return null;
|
|
129
|
-
// Decode JSON Pointer tokens for the def name
|
|
130
113
|
const name = parts[1].replace(/~1/g, '/').replace(/~0/g, '~');
|
|
131
114
|
return name;
|
|
132
115
|
}
|
|
133
116
|
|
|
134
117
|
function resolveRefToDefName(ref: string, rootDefs: Record<string, any>): string | null {
|
|
135
118
|
if (!ref) return null;
|
|
136
|
-
// Case 1: JSON Pointer into $defs: '#/$defs/Name'
|
|
137
119
|
const byPointer = extractPointerDefName(ref);
|
|
138
120
|
if (byPointer) return byPointer;
|
|
139
121
|
|
|
140
|
-
// Case 2: Anchor ref: '#Name' -> find a def whose nucleusSchema.$anchor equals 'Name'
|
|
141
122
|
if (ref.startsWith('#') && !ref.startsWith('#/')) {
|
|
142
123
|
const anchor = ref.slice(1);
|
|
143
124
|
if (!anchor) return null;
|
|
144
125
|
for (const [defName, defSchema] of Object.entries(rootDefs)) {
|
|
145
126
|
if (!defSchema || typeof defSchema !== 'object') continue;
|
|
146
|
-
// Flattened Genesis has defs as the extraction schema itself (with $anchor at the top level).
|
|
147
|
-
// Unflattened may have { nucleusSchema: { $anchor } } envelopes. Support both.
|
|
148
127
|
const topLevelAnchor = (defSchema as any).$anchor;
|
|
149
128
|
const nested = (defSchema as any).nucleusSchema;
|
|
150
129
|
const nestedAnchor = nested && typeof nested === 'object' ? nested.$anchor : undefined;
|
|
@@ -183,7 +162,7 @@ function collectLocalDefClosure(node: any, rootDefs: Record<string, any>): Set<s
|
|
|
183
162
|
while (queue.length > 0) {
|
|
184
163
|
const name = queue.shift()!;
|
|
185
164
|
const def = rootDefs[name];
|
|
186
|
-
if (!def) continue;
|
|
165
|
+
if (!def) continue;
|
|
187
166
|
visit(def);
|
|
188
167
|
}
|
|
189
168
|
|
|
@@ -3,10 +3,10 @@ import path from 'path';
|
|
|
3
3
|
import { getConfig } from './_lib/config.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Generate a typed Resource variant where `
|
|
6
|
+
* Generate a typed Resource variant where `nucleus` is typed to a specific schema
|
|
7
7
|
* extracted under the configured output directory.
|
|
8
8
|
*
|
|
9
|
-
* Usage: node ./dist/scripts/
|
|
9
|
+
* Usage: node ./dist/scripts/generateStandaloneType.js --name Job
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* Pure function to generate the typed Resource D.TS content.
|
|
@@ -14,11 +14,11 @@ import { getConfig } from './_lib/config.js';
|
|
|
14
14
|
* @param name The name of the schema
|
|
15
15
|
* @returns The TypeScript D.TS file content
|
|
16
16
|
*/
|
|
17
|
-
function
|
|
17
|
+
function generateStandaloneTypeLogic(name: string): string {
|
|
18
18
|
const header = '// Auto-generated strict composite type. Do not edit.\n';
|
|
19
19
|
const ts =
|
|
20
|
-
`import type {
|
|
21
|
-
`export type Resource_${name} =
|
|
20
|
+
`import type { ShellMaterializedBase, ${name} as NucleusSchema } from '../types.js';\n` +
|
|
21
|
+
`export type Resource_${name} = ShellMaterializedBase & { nucleus: NucleusSchema };\n`;
|
|
22
22
|
return header + ts;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -30,10 +30,19 @@ async function main() {
|
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const inPath =
|
|
33
|
+
const inPath =
|
|
34
|
+
name === 'Genesis'
|
|
35
|
+
? config.getSchemaPath('Genesis.json')
|
|
36
|
+
: config.getSchemaStandalonePath(`${name}.json`);
|
|
34
37
|
|
|
35
38
|
if (!fs.existsSync(inPath)) {
|
|
36
|
-
|
|
39
|
+
if (name === 'Genesis') {
|
|
40
|
+
console.error(`Schema file not found: ${inPath}`);
|
|
41
|
+
console.error('Run extractSchemasFromResourceTypeShells first.');
|
|
42
|
+
} else {
|
|
43
|
+
console.error(`Standalone schema file not found: ${inPath}`);
|
|
44
|
+
console.error(`Run generateStandaloneSchema -- --name ${name} first.`);
|
|
45
|
+
}
|
|
37
46
|
process.exit(1);
|
|
38
47
|
}
|
|
39
48
|
|
|
@@ -55,7 +64,7 @@ async function main() {
|
|
|
55
64
|
console.warn(`Warning: nucleusSchema usually has type: 'object' but this schema has type: '${parsed.type}'. Proceeding.`);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
const tsContent =
|
|
67
|
+
const tsContent = generateStandaloneTypeLogic(name);
|
|
59
68
|
const jsContent = 'export {}\n';
|
|
60
69
|
|
|
61
70
|
// Output setup
|
|
@@ -63,11 +72,11 @@ async function main() {
|
|
|
63
72
|
const outJsName = `Resource_${name}.js`;
|
|
64
73
|
|
|
65
74
|
// Process src output
|
|
66
|
-
const outDir = config.
|
|
75
|
+
const outDir = config.getTypesStandaloneSrcDir();
|
|
67
76
|
fs.mkdirSync(outDir, { recursive: true });
|
|
68
77
|
|
|
69
|
-
const outPath = config.
|
|
70
|
-
const outJsPath = config.
|
|
78
|
+
const outPath = config.getTypesStandaloneSrcPath(outName);
|
|
79
|
+
const outJsPath = config.getTypesStandaloneSrcPath(outJsName);
|
|
71
80
|
|
|
72
81
|
fs.writeFileSync(outPath, tsContent, 'utf8');
|
|
73
82
|
console.log(`Wrote ${outPath}`);
|
|
@@ -78,11 +87,11 @@ async function main() {
|
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
// Process dist output
|
|
81
|
-
const distLibDir = config.
|
|
90
|
+
const distLibDir = config.getTypesStandaloneDistDir();
|
|
82
91
|
fs.mkdirSync(distLibDir, { recursive: true });
|
|
83
92
|
|
|
84
|
-
const distDtsPath = config.
|
|
85
|
-
const distJsPath = config.
|
|
93
|
+
const distDtsPath = config.getTypesStandaloneDistPath(outName);
|
|
94
|
+
const distJsPath = config.getTypesStandaloneDistPath(outJsName);
|
|
86
95
|
|
|
87
96
|
fs.writeFileSync(distDtsPath, tsContent, 'utf8');
|
|
88
97
|
fs.writeFileSync(distJsPath, jsContent, 'utf8');
|
|
@@ -5,7 +5,7 @@ import { getConfig } from './_lib/config.js';
|
|
|
5
5
|
|
|
6
6
|
const config = getConfig();
|
|
7
7
|
const projectRoot = config.getRoot();
|
|
8
|
-
const inputDir = config.
|
|
8
|
+
const inputDir = config.getSchemasDir();
|
|
9
9
|
// We emit under src/genesis/generated/types and dist/genesis/generated/types
|
|
10
10
|
const srcLibTypesDir = config.getTypesSrcDir();
|
|
11
11
|
const srcLibOutputPath = config.getTypesSrcPath('types.d.ts');
|
|
@@ -12,7 +12,7 @@ import { getConfig } from './_lib/config.js';
|
|
|
12
12
|
* The rewritten file is saved under src/genesis/generated/resourceTypes/Genesis.json
|
|
13
13
|
* for use by other scripts.
|
|
14
14
|
*
|
|
15
|
-
* Usage: node ./dist/scripts/
|
|
15
|
+
* Usage: node ./dist/scripts/normalizeAnchorsToPointers.js
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]: JSONValue };
|
|
@@ -25,7 +25,7 @@ type JSONValue = null | boolean | number | string | JSONValue[] | { [k: string]:
|
|
|
25
25
|
* @param input The schema object to process
|
|
26
26
|
* @returns A new schema object with rewritten references
|
|
27
27
|
*/
|
|
28
|
-
function
|
|
28
|
+
function normalizeAnchorsToPointersToPointers(input: any): any {
|
|
29
29
|
if (!input || typeof input !== "object") return input;
|
|
30
30
|
|
|
31
31
|
// Deep clone the input to ensure the function is pure (no side effects on input)
|
|
@@ -109,7 +109,7 @@ async function main() {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Rewrite anchors in the nucleusSchema (pure function call)
|
|
112
|
-
genesis.nucleusSchema =
|
|
112
|
+
genesis.nucleusSchema = normalizeAnchorsToPointersToPointers(genesis.nucleusSchema);
|
|
113
113
|
|
|
114
114
|
// Write normalized version
|
|
115
115
|
fs.mkdirSync(path.dirname(normalizedPath), { recursive: true });
|