@toolproof-core/schema 1.0.0 → 1.0.2
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 +15 -14
- package/src/Genesis.json +1999 -2042
- 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_Genesis.js +1 -0
- package/src/generated/types/standalone/Resource_Job.d.ts +3 -0
- package/src/generated/types/standalone/Resource_Job.js +1 -0
- package/src/generated/types/standalone/Resource_RawStrategy.d.ts +3 -0
- package/src/generated/types/standalone/Resource_RawStrategy.js +1 -0
- package/src/generated/types/standalone/Resource_ResourceType.d.ts +3 -0
- package/src/generated/types/standalone/Resource_ResourceType.js +1 -0
- package/src/generated/types/standalone/Resource_RunnableStrategy.d.ts +3 -0
- package/src/generated/types/standalone/Resource_RunnableStrategy.js +1 -0
- package/src/generated/types/types.d.ts +487 -548
- package/src/generated/types/types.js +1 -1
- package/src/scripts/_lib/config.ts +205 -181
- package/src/scripts/extractSchemasFromResourceTypeShells.ts +218 -0
- package/src/scripts/generateDependencies.ts +120 -120
- package/src/scripts/generateSchemaShims.ts +135 -115
- package/src/scripts/{extractSubSchemaWithDefs.ts → generateStandaloneSchema.ts} +175 -196
- package/src/scripts/{generateResourceTypeType.ts → generateStandaloneType.ts} +119 -110
- package/src/scripts/generateTypes.ts +614 -614
- package/src/scripts/{rewriteAnchors.ts → normalizeAnchorsToPointers.ts} +123 -123
- package/src/scripts/{generateResourceShells.ts → wrapResourceTypesWithResourceShells.ts} +84 -105
- 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_Genesis.js +0 -1
- package/src/generated/types/Resource_Job.d.ts +0 -3
- package/src/generated/types/Resource_Job.js +0 -1
- package/src/generated/types/Resource_RawStrategy.d.ts +0 -3
- package/src/generated/types/Resource_RawStrategy.js +0 -1
- package/src/generated/types/Resource_ResourceType.d.ts +0 -3
- package/src/generated/types/Resource_ResourceType.js +0 -1
- package/src/generated/types/Resource_RunnableStrategy.d.ts +0 -3
- package/src/generated/types/Resource_RunnableStrategy.js +0 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
export {}
|
|
1
|
+
export {}
|
|
@@ -1,181 +1,205 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration for schema generation scripts
|
|
3
|
-
* All paths are configurable via environment variables
|
|
4
|
-
* Provides sensible defaults for standard project structure
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import path from 'path';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Get environment variable with optional default
|
|
11
|
-
*/
|
|
12
|
-
function getEnv(name: string, defaultValue: string): string {
|
|
13
|
-
const value = process.env[name];
|
|
14
|
-
return value || defaultValue;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Schema configuration with required environment variables
|
|
19
|
-
*/
|
|
20
|
-
export class SchemaConfig {
|
|
21
|
-
// Base paths
|
|
22
|
-
private readonly root: string;
|
|
23
|
-
private readonly sourceDir: string;
|
|
24
|
-
private readonly sourceFile: string;
|
|
25
|
-
private readonly normalizedDir: string;
|
|
26
|
-
private readonly
|
|
27
|
-
private readonly
|
|
28
|
-
private readonly
|
|
29
|
-
private readonly
|
|
30
|
-
private readonly
|
|
31
|
-
|
|
32
|
-
// Schema metadata
|
|
33
|
-
private readonly baseUrl: string;
|
|
34
|
-
private readonly version: string;
|
|
35
|
-
|
|
36
|
-
constructor() {
|
|
37
|
-
// Environment variables with sensible defaults
|
|
38
|
-
this.root = getEnv('TP_SCHEMA_ROOT', process.cwd());
|
|
39
|
-
this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/');
|
|
40
|
-
this.sourceFile = getEnv('TP_SCHEMA_SOURCE_FILE', 'Genesis.json');
|
|
41
|
-
// Intermediate, generated artifact produced by
|
|
42
|
-
// This should NOT live next to the source-of-truth schemas.
|
|
43
|
-
this.normalizedDir = getEnv('TP_SCHEMA_NORMALIZED_DIR', 'src/generated/
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
48
|
-
this.
|
|
49
|
-
this.baseUrl = getEnv('TP_SCHEMA_BASE_URL', 'https://schemas.toolproof.com');
|
|
50
|
-
this.version = getEnv('TP_SCHEMA_VERSION', 'v0');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Path getters
|
|
54
|
-
getRoot(): string {
|
|
55
|
-
return this.root;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
getSourceDir(): string {
|
|
59
|
-
return path.isAbsolute(this.sourceDir)
|
|
60
|
-
? this.sourceDir
|
|
61
|
-
: path.join(this.root, this.sourceDir);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getSourceFile(): string {
|
|
65
|
-
return this.sourceFile;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
getSourcePath(): string {
|
|
69
|
-
return path.join(this.getSourceDir(), this.sourceFile);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
getNormalizedDir(): string {
|
|
73
|
-
return path.isAbsolute(this.normalizedDir)
|
|
74
|
-
? this.normalizedDir
|
|
75
|
-
: path.join(this.root, this.normalizedDir);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
getNormalizedSourceFile(): string {
|
|
79
|
-
// We keep the same basename (Genesis.json) in the generated folder.
|
|
80
|
-
// The source-of-truth Genesis.json lives under `TP_SCHEMA_SOURCE_DIR`.
|
|
81
|
-
// The generated/normalized Genesis.json lives under `TP_SCHEMA_NORMALIZED_DIR`.
|
|
82
|
-
return this.sourceFile;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
getNormalizedSourcePath(): string {
|
|
86
|
-
return path.join(this.getNormalizedDir(), this.getNormalizedSourceFile());
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return path.isAbsolute(this.
|
|
91
|
-
? this.
|
|
92
|
-
: path.join(this.root, this.
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return path.join(this.
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return path.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return path.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for schema generation scripts
|
|
3
|
+
* All paths are configurable via environment variables
|
|
4
|
+
* Provides sensible defaults for standard project structure
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get environment variable with optional default
|
|
11
|
+
*/
|
|
12
|
+
function getEnv(name: string, defaultValue: string): string {
|
|
13
|
+
const value = process.env[name];
|
|
14
|
+
return value || defaultValue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Schema configuration with required environment variables
|
|
19
|
+
*/
|
|
20
|
+
export class SchemaConfig {
|
|
21
|
+
// Base paths
|
|
22
|
+
private readonly root: string;
|
|
23
|
+
private readonly sourceDir: string;
|
|
24
|
+
private readonly sourceFile: string;
|
|
25
|
+
private readonly normalizedDir: string;
|
|
26
|
+
private readonly schemasDir: string;
|
|
27
|
+
private readonly resourcesDir: string;
|
|
28
|
+
private readonly dependencyMapPath: string;
|
|
29
|
+
private readonly typesSrcDir: string;
|
|
30
|
+
private readonly typesDistDir: string;
|
|
31
|
+
|
|
32
|
+
// Schema metadata
|
|
33
|
+
private readonly baseUrl: string;
|
|
34
|
+
private readonly version: string;
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
// Environment variables with sensible defaults
|
|
38
|
+
this.root = getEnv('TP_SCHEMA_ROOT', process.cwd());
|
|
39
|
+
this.sourceDir = getEnv('TP_SCHEMA_SOURCE_DIR', 'src/');
|
|
40
|
+
this.sourceFile = getEnv('TP_SCHEMA_SOURCE_FILE', 'Genesis.json');
|
|
41
|
+
// Intermediate, generated artifact produced by normalizeAnchorsToPointers.
|
|
42
|
+
// This should NOT live next to the source-of-truth schemas.
|
|
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');
|
|
47
|
+
this.typesSrcDir = getEnv('TP_SCHEMA_TYPES_SRC_DIR', 'src/generated/types');
|
|
48
|
+
this.typesDistDir = getEnv('TP_SCHEMA_TYPES_DIST_DIR', 'dist/generated/types');
|
|
49
|
+
this.baseUrl = getEnv('TP_SCHEMA_BASE_URL', 'https://schemas.toolproof.com');
|
|
50
|
+
this.version = getEnv('TP_SCHEMA_VERSION', 'v0');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Path getters
|
|
54
|
+
getRoot(): string {
|
|
55
|
+
return this.root;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getSourceDir(): string {
|
|
59
|
+
return path.isAbsolute(this.sourceDir)
|
|
60
|
+
? this.sourceDir
|
|
61
|
+
: path.join(this.root, this.sourceDir);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getSourceFile(): string {
|
|
65
|
+
return this.sourceFile;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getSourcePath(): string {
|
|
69
|
+
return path.join(this.getSourceDir(), this.sourceFile);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getNormalizedDir(): string {
|
|
73
|
+
return path.isAbsolute(this.normalizedDir)
|
|
74
|
+
? this.normalizedDir
|
|
75
|
+
: path.join(this.root, this.normalizedDir);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getNormalizedSourceFile(): string {
|
|
79
|
+
// We keep the same basename (Genesis.json) in the generated folder.
|
|
80
|
+
// The source-of-truth Genesis.json lives under `TP_SCHEMA_SOURCE_DIR`.
|
|
81
|
+
// The generated/normalized Genesis.json lives under `TP_SCHEMA_NORMALIZED_DIR`.
|
|
82
|
+
return this.sourceFile;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getNormalizedSourcePath(): string {
|
|
86
|
+
return path.join(this.getNormalizedDir(), this.getNormalizedSourceFile());
|
|
87
|
+
}
|
|
88
|
+
|
|
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);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getSchemasStandaloneDir(): string {
|
|
100
|
+
return path.join(this.getSchemasDir(), 'standalone');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getSchemaStandalonePath(filename: string): string {
|
|
104
|
+
return path.join(this.getSchemasStandaloneDir(), filename);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getTypesSrcDir(): string {
|
|
108
|
+
return path.isAbsolute(this.typesSrcDir)
|
|
109
|
+
? this.typesSrcDir
|
|
110
|
+
: path.join(this.root, this.typesSrcDir);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getTypesDistDir(): string {
|
|
114
|
+
return path.isAbsolute(this.typesDistDir)
|
|
115
|
+
? this.typesDistDir
|
|
116
|
+
: path.join(this.root, this.typesDistDir);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getTypesSrcPath(filename: string): string {
|
|
120
|
+
return path.join(this.getTypesSrcDir(), filename);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getTypesDistPath(filename: string): string {
|
|
124
|
+
return path.join(this.getTypesDistDir(), filename);
|
|
125
|
+
}
|
|
126
|
+
|
|
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);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getDependencyMapPath(): string {
|
|
150
|
+
return path.isAbsolute(this.dependencyMapPath)
|
|
151
|
+
? this.dependencyMapPath
|
|
152
|
+
: path.join(this.root, this.dependencyMapPath);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Schema URL methods
|
|
156
|
+
getBaseUrl(): string {
|
|
157
|
+
return this.baseUrl;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getVersion(): string {
|
|
161
|
+
return this.version;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getSchemaId(schemaName: string): string {
|
|
165
|
+
return `${this.baseUrl}/${this.version}/${schemaName}.json`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if a URL matches the configured schema base URL pattern
|
|
170
|
+
*/
|
|
171
|
+
isSchemaUrl(url: string): boolean {
|
|
172
|
+
const baseUrlPattern = this.baseUrl.replace('https://', 'https?://');
|
|
173
|
+
return new RegExp(`^${baseUrlPattern}/`, 'i').test(url);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Extract schema name from URL (removes base URL and version prefix)
|
|
178
|
+
*/
|
|
179
|
+
extractSchemaName(url: string): string {
|
|
180
|
+
// Remove base URL
|
|
181
|
+
let name = url.replace(new RegExp(`^${this.baseUrl}/`, 'i'), '');
|
|
182
|
+
|
|
183
|
+
// Remove version prefix (v0/, v1/, etc.)
|
|
184
|
+
name = name.replace(/^v\d+\//, '');
|
|
185
|
+
|
|
186
|
+
// Remove .json extension
|
|
187
|
+
name = name.replace(/\.json$/, '');
|
|
188
|
+
|
|
189
|
+
return name;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Singleton instance
|
|
194
|
+
let configInstance: SchemaConfig | null = null;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get the schema configuration singleton
|
|
198
|
+
* Throws error if required environment variables are not set
|
|
199
|
+
*/
|
|
200
|
+
export function getConfig(): SchemaConfig {
|
|
201
|
+
if (!configInstance) {
|
|
202
|
+
configInstance = new SchemaConfig();
|
|
203
|
+
}
|
|
204
|
+
return configInstance;
|
|
205
|
+
}
|
|
@@ -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();
|