@toolproof-core/genesis 1.0.50 → 1.0.52

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.
@@ -1,42 +1,42 @@
1
- type JsonObject = Record<string, unknown>;
2
-
3
- type TimestampedResource = JsonObject & {
4
- id: string;
5
- resourceTypeHandle: string;
6
- provenance: {
7
- resourceProvenanceKind: 'genesis';
8
- };
9
- timestamp: string;
10
- projection: unknown;
11
- };
12
-
13
- type GenerateTimestampedResourcesOptions = {
14
- resourceTypeHandle: string;
15
- getResourceId: (projection: unknown) => string;
16
- };
17
-
18
- const GENESIS_TIMESTAMP = '2025-11-30T00:00:00.000Z';
19
-
20
- export function generateTimestampedResources(
21
- declarations: unknown[],
22
- options: GenerateTimestampedResourcesOptions,
23
- ): TimestampedResource[] {
24
- return declarations.map((projection) => {
25
- return createTimestampedResource(projection, options);
26
- });
27
- }
28
-
29
- function createTimestampedResource(
30
- projection: unknown,
31
- options: GenerateTimestampedResourcesOptions,
32
- ): TimestampedResource {
33
- return {
34
- id: options.getResourceId(projection),
35
- resourceTypeHandle: options.resourceTypeHandle,
36
- provenance: {
37
- resourceProvenanceKind: 'genesis',
38
- },
39
- timestamp: GENESIS_TIMESTAMP,
40
- projection,
41
- };
1
+ type JsonObject = Record<string, unknown>;
2
+
3
+ type TimestampedResource = JsonObject & {
4
+ id: string;
5
+ resourceTypeHandle: string;
6
+ provenance: {
7
+ resourceProvenanceKind: 'genesis';
8
+ };
9
+ timestamp: string;
10
+ projection: unknown;
11
+ };
12
+
13
+ type GenerateTimestampedResourcesOptions = {
14
+ resourceTypeHandle: string;
15
+ getResourceId: (projection: unknown) => string;
16
+ };
17
+
18
+ const GENESIS_TIMESTAMP = '2025-11-30T00:00:00.000Z';
19
+
20
+ export function generateTimestampedResources(
21
+ declarations: unknown[],
22
+ options: GenerateTimestampedResourcesOptions,
23
+ ): TimestampedResource[] {
24
+ return declarations.map((projection) => {
25
+ return createTimestampedResource(projection, options);
26
+ });
27
+ }
28
+
29
+ function createTimestampedResource(
30
+ projection: unknown,
31
+ options: GenerateTimestampedResourcesOptions,
32
+ ): TimestampedResource {
33
+ return {
34
+ id: options.getResourceId(projection),
35
+ resourceTypeHandle: options.resourceTypeHandle,
36
+ provenance: {
37
+ resourceProvenanceKind: 'genesis',
38
+ },
39
+ timestamp: GENESIS_TIMESTAMP,
40
+ projection,
41
+ };
42
42
  }
@@ -1,31 +1,31 @@
1
- import { compile } from 'json-schema-to-typescript';
2
- import { finalizeOutputDts, postProcessEmittedTypes } from './typeGenerationPostProcess.js';
3
- import { normalizeSchemaForGenerator, validateSchemaArrayKeywords } from './typeGenerationPreflight.js';
4
-
5
- export async function generateTypeDeclarations(parsedSchema: any, rootName: string): Promise<string> {
6
- const validationIssues = validateSchemaArrayKeywords(parsedSchema);
7
- if (validationIssues.length) {
8
- throw new Error(
9
- 'Schema is not in canonical form for type generation:\n' +
10
- validationIssues.map((issue) => `- ${issue}`).join('\n'),
11
- );
12
- }
13
-
14
- const normalizedSchema = normalizeSchemaForGenerator(parsedSchema);
15
-
16
- let ts = await compile(normalizedSchema, rootName, {
17
- bannerComment: '',
18
- declareExternallyReferenced: true,
19
- unreachableDefinitions: true,
20
- $refOptions: {
21
- resolve: {
22
- file: { order: 2 },
23
- http: false,
24
- https: false,
25
- },
26
- } as any,
27
- });
28
-
29
- ts = postProcessEmittedTypes(ts, parsedSchema);
30
- return finalizeOutputDts(ts);
1
+ import { compile } from 'json-schema-to-typescript';
2
+ import { finalizeOutputDts, postProcessEmittedTypes } from './typeGenerationPostProcess.js';
3
+ import { normalizeSchemaForGenerator, validateSchemaArrayKeywords } from './typeGenerationPreflight.js';
4
+
5
+ export async function generateTypeDeclarations(parsedSchema: any, rootName: string): Promise<string> {
6
+ const validationIssues = validateSchemaArrayKeywords(parsedSchema);
7
+ if (validationIssues.length) {
8
+ throw new Error(
9
+ 'Schema is not in canonical form for type generation:\n' +
10
+ validationIssues.map((issue) => `- ${issue}`).join('\n'),
11
+ );
12
+ }
13
+
14
+ const normalizedSchema = normalizeSchemaForGenerator(parsedSchema);
15
+
16
+ let ts = await compile(normalizedSchema, rootName, {
17
+ bannerComment: '',
18
+ declareExternallyReferenced: true,
19
+ unreachableDefinitions: true,
20
+ $refOptions: {
21
+ resolve: {
22
+ file: { order: 2 },
23
+ http: false,
24
+ https: false,
25
+ },
26
+ } as any,
27
+ });
28
+
29
+ ts = postProcessEmittedTypes(ts, parsedSchema);
30
+ return finalizeOutputDts(ts);
31
31
  }
@@ -1,246 +1,246 @@
1
- function deriveTemplateFromPattern(pattern: string): string | undefined {
2
- const match = /^\^([^$]+)\.\+\$/.exec(pattern);
3
- if (match) {
4
- const prefix = match[1];
5
- if (prefix === undefined) return undefined;
6
- if (!/[`]/.test(prefix)) {
7
- return '`' + prefix + '${string}`';
8
- }
9
- }
10
- return undefined;
11
- }
12
-
13
- function loadPatternTemplatesFromSchema(schema: any): Record<string, string> {
14
- const map: Record<string, string> = {};
15
- const defs = schema?.$defs && typeof schema.$defs === 'object' ? schema.$defs : {};
16
- for (const [defName, defVal] of Object.entries(defs)) {
17
- if (!/(Id|Handle)$/.test(defName)) continue;
18
-
19
- const value: any = defVal;
20
- if (value && value.type === 'string' && typeof value.pattern === 'string') {
21
- const tmpl = deriveTemplateFromPattern(value.pattern);
22
- if (tmpl) map[defName] = tmpl;
23
- }
24
- }
25
- return map;
26
- }
27
-
28
- function removeDuplicateUnionEntries(ts: string): string {
29
- return ts.replace(/export type ([A-Za-z0-9_]+) =([\s\S]*?);/g, (_match, typeName, body) => {
30
- const lines = body.split(/\r?\n/);
31
- const seen = new Set<string>();
32
- const kept: string[] = [];
33
- for (const line of lines) {
34
- const trimmed = line.trim();
35
- const match = /^\|\s*([A-Za-z0-9_]+)\b/.exec(trimmed);
36
- if (match) {
37
- const name = match[1];
38
- if (name === undefined) continue;
39
- if (!seen.has(name)) {
40
- seen.add(name);
41
- kept.push(' | ' + name);
42
- }
43
- continue;
44
- }
45
- if (trimmed.length) kept.push(line);
46
- }
47
- return `export type ${typeName} =\n` + kept.join('\n') + ';';
48
- });
49
- }
50
-
51
- function escapeRegExpLiteral(value: string): string {
52
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
53
- }
54
-
55
- function deriveStrategyStateInputEntryByRoleNameValueType(parsedSchema: any): string {
56
- const defs = parsedSchema?.$defs ?? parsedSchema?.projectionSchema?.$defs;
57
- const def = defs?.StrategyStateInputEntryByRoleName;
58
- const schemaNode = def?.projectionSchema ?? def;
59
- const additionalProperties = schemaNode?.additionalProperties;
60
-
61
- if (!additionalProperties || typeof additionalProperties !== 'object' || Array.isArray(additionalProperties)) {
62
- return 'StrategyStateInputEntry';
63
- }
64
-
65
- const union = (additionalProperties as any).oneOf ?? (additionalProperties as any).anyOf;
66
- if (!Array.isArray(union) || union.length === 0) {
67
- const ref = (additionalProperties as any).$ref;
68
- const match = typeof ref === 'string'
69
- ? /^#(?:\/(?:\$defs|definitions)\/)?([A-Za-z_][A-Za-z0-9_]*)$/.exec(ref)
70
- : undefined;
71
- const typeName = match?.[1];
72
- return typeName ?? 'StrategyStateInputEntry';
73
- }
74
-
75
- const names: string[] = [];
76
- for (const item of union) {
77
- const ref = item && typeof item === 'object' ? (item as any).$ref : undefined;
78
- if (typeof ref !== 'string') continue;
79
-
80
- const match = /^#(?:\/(?:\$defs|definitions)\/)?([A-Za-z_][A-Za-z0-9_]*)$/.exec(ref);
81
- const name = match?.[1];
82
- if (name !== undefined) names.push(name);
83
- }
84
-
85
- const unique = Array.from(new Set(names));
86
- if (!unique.length) return 'StrategyStateInputEntry';
87
-
88
- return unique.join(' | ');
89
- }
90
-
91
- function fixRecursiveArrayAliasSelfReference(ts: string, typeName: string): string {
92
- const lines = ts.split(/\r?\n/);
93
- const typeNamePattern = escapeRegExpLiteral(typeName);
94
- const startIndex = lines.findIndex((line) => new RegExp(`^export\\s+type\\s+${typeNamePattern}\\s*=\\s*$`).test(line.trimEnd()));
95
- if (startIndex < 0) return ts;
96
-
97
- let endIndex = -1;
98
- for (let i = startIndex + 1; i < lines.length; i++) {
99
- const line = lines[i];
100
- if (line !== undefined && /^\s*};\s*$/.test(line)) {
101
- endIndex = i;
102
- break;
103
- }
104
- }
105
- if (endIndex < 0) return ts;
106
-
107
- for (let i = startIndex + 1; i < endIndex; i++) {
108
- const line = lines[i];
109
- if (line !== undefined && new RegExp(`^\\s*\\|\\s*${typeNamePattern}\\s*$`).test(line)) {
110
- lines[i] = line.replace(new RegExp(`\\b${typeNamePattern}\\b`), `${typeName}[]`);
111
- }
112
- }
113
-
114
- return lines.join('\n');
115
- }
116
-
117
- export function postProcessEmittedTypes(ts: string, parsedSchema: any): string {
118
- ts = ts.replace(/^\s*\[k:\s*string\]:\s*unknown;\s*(?:\r?\n)?/gm, '');
119
-
120
- ts = ts.replace(
121
- /^(\s*)(\$defs\??:\s*)\{\s*\r?\n\s*\};/gm,
122
- (_m, indent, head) => `${indent}${head}Record<string, unknown>;`,
123
- );
124
-
125
- ts = ts.replace(
126
- /^(\s*)(properties\??:\s*)\{\s*\r?\n\s*\};/gm,
127
- (_m, indent, head) => `${indent}${head}Record<string, unknown>;`,
128
- );
129
-
130
- ts = ts.replace(
131
- /^(\s*)((?:allOf|anyOf|oneOf)\??:\s*)\{\s*\r?\n\s*\}\[\];/gm,
132
- (_m, indent, head) => `${indent}${head}Array<{[k: string]: unknown}>;`,
133
- );
134
-
135
- ts = removeDuplicateUnionEntries(ts);
136
-
137
- const schemaId = parsedSchema?.$id;
138
- if (typeof schemaId === 'string') {
139
- const match = /\/([^/]+)\.json$/i.exec(schemaId);
140
- const rootBaseName = match?.[1];
141
- if (rootBaseName) {
142
- const rootTypeNameEsc = escapeRegExpLiteral(rootBaseName);
143
- const generatedRootSchemaNamePattern = `Https[A-Za-z0-9_]*${rootTypeNameEsc}(?:Json)?`;
144
-
145
- ts = ts.replace(
146
- new RegExp(`^export interface ${generatedRootSchemaNamePattern}\\s*\\{\\s*\\}\\s*(?:\\r?\\n)?`, 'gm'),
147
- '',
148
- );
149
-
150
- ts = ts.replace(
151
- new RegExp(`\\b${generatedRootSchemaNamePattern}\\b`, 'g'),
152
- rootBaseName,
153
- );
154
- }
155
- }
156
-
157
- if (!ts || !ts.trim()) {
158
- throw new Error('Type generator emitted no output for schema.');
159
- }
160
-
161
- const patternTemplates = loadPatternTemplatesFromSchema(parsedSchema);
162
- ts = ts.replace(
163
- /^(export\s+type\s+)([A-Za-z_][A-Za-z0-9_]*(?:Id|Handle))(\s*=\s*)string\s*;$/gm,
164
- (_m, p1, typeName, p3) => {
165
- const tmpl = patternTemplates[typeName];
166
- return `${p1}${typeName}${p3}${tmpl ?? 'string'};`;
167
- },
168
- );
169
-
170
- const roleNameKeyType = 'RoleName';
171
- const unthreadedToolStepPathKeyType = 'UnthreadedToolStepPath';
172
- const threadedToolStepPathKeyType = 'ThreadedToolStepPath';
173
- const toolStepPathKeyType = 'ToolStepPath';
174
-
175
- ts = ts.replace(
176
- /export interface RoleValueByName\s*\{\s*\[k:\s*string\]:\s*RoleValue;\s*\}/g,
177
- `export type RoleValueByName = Record<${roleNameKeyType}, RoleValue>;`,
178
- );
179
-
180
- const strategyStateInputEntryByRoleNameValueType = deriveStrategyStateInputEntryByRoleNameValueType(parsedSchema);
181
- ts = ts.replace(
182
- /export interface StrategyStateInputEntryByRoleName\s*\{\s*\[k:\s*string\]:\s*[^;]+;\s*\}/g,
183
- `export type StrategyStateInputEntryByRoleName = Record<${roleNameKeyType}, ${strategyStateInputEntryByRoleNameValueType}>;`,
184
- );
185
-
186
- ts = ts.replace(
187
- /export\s+type\s+StrategyStateInputEntryByRoleName\s*=\s*Record<\s*RoleName\s*,\s*[^>]+>\s*;/g,
188
- `export type StrategyStateInputEntryByRoleName = Record<${roleNameKeyType}, ${strategyStateInputEntryByRoleNameValueType}>;`,
189
- );
190
-
191
- ts = ts.replace(
192
- /export interface StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath\s*\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\}/g,
193
- `export type StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath = Record<${unthreadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
194
- );
195
-
196
- ts = ts.replace(
197
- /export interface StrategyStateInputEntryByRoleNameByThreadedToolStepPath\s*\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\}/g,
198
- `export type StrategyStateInputEntryByRoleNameByThreadedToolStepPath = Record<${threadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
199
- );
200
-
201
- ts = ts.replace(
202
- /export\s+type\s+UnthreadedStrategyState\s*=\s*StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath\s*;/g,
203
- `export type UnthreadedStrategyState = Record<${unthreadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
204
- );
205
-
206
- ts = ts.replace(
207
- /export\s+type\s+ThreadedStrategyState\s*=\s*StrategyStateInputEntryByRoleNameByThreadedToolStepPath\s*;/g,
208
- `export type ThreadedStrategyState = Record<${threadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
209
- );
210
-
211
- ts = ts.replace(
212
- /(strategyState\??:\s*)\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\};/g,
213
- `$1Record<${toolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
214
- );
215
-
216
- ts = ts.replace(
217
- /(strategyStateUpdate\??:\s*)\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\};/g,
218
- `$1Record<${toolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
219
- );
220
-
221
- ts = ts.replace(
222
- /export interface StepsByThreadIndexFacet\s*\{\s*stepsByThreadIndex:\s*StepArrayArray;\s*\}/g,
223
- 'export interface StepsByThreadIndexFacet {\n stepsByThreadIndex: StepArrayArray;\n}',
224
- );
225
-
226
- ts = fixRecursiveArrayAliasSelfReference(ts, 'JsonData');
227
-
228
- return ts;
229
- }
230
-
231
- export function finalizeOutputDts(emittedType: string): string {
232
- const banner = '// Auto-generated from JSON schemas. Do not edit.\n';
233
-
234
- let output = banner + '\n' + emittedType + '\n';
235
- output = output.replace(/^\s*\[k:\s*string\]:\s*unknown;\s*$/gm, '');
236
- output = output
237
- .replace(/\r?\n\s*\r?\n(\s*};)/g, '\n$1')
238
- .replace(/\r?\n\s*\r?\n(\s*})/g, '\n$1')
239
- .replace(/(\r?\n){3,}/g, '\n\n');
240
-
241
- if (!/\bexport\b|\bdeclare\s+module\b|\bdeclare\s+namespace\b/.test(output)) {
242
- output += '\nexport {}\n';
243
- }
244
-
245
- return output;
1
+ function deriveTemplateFromPattern(pattern: string): string | undefined {
2
+ const match = /^\^([^$]+)\.\+\$/.exec(pattern);
3
+ if (match) {
4
+ const prefix = match[1];
5
+ if (prefix === undefined) return undefined;
6
+ if (!/[`]/.test(prefix)) {
7
+ return '`' + prefix + '${string}`';
8
+ }
9
+ }
10
+ return undefined;
11
+ }
12
+
13
+ function loadPatternTemplatesFromSchema(schema: any): Record<string, string> {
14
+ const map: Record<string, string> = {};
15
+ const defs = schema?.$defs && typeof schema.$defs === 'object' ? schema.$defs : {};
16
+ for (const [defName, defVal] of Object.entries(defs)) {
17
+ if (!/(Id|Handle)$/.test(defName)) continue;
18
+
19
+ const value: any = defVal;
20
+ if (value && value.type === 'string' && typeof value.pattern === 'string') {
21
+ const tmpl = deriveTemplateFromPattern(value.pattern);
22
+ if (tmpl) map[defName] = tmpl;
23
+ }
24
+ }
25
+ return map;
26
+ }
27
+
28
+ function removeDuplicateUnionEntries(ts: string): string {
29
+ return ts.replace(/export type ([A-Za-z0-9_]+) =([\s\S]*?);/g, (_match, typeName, body) => {
30
+ const lines = body.split(/\r?\n/);
31
+ const seen = new Set<string>();
32
+ const kept: string[] = [];
33
+ for (const line of lines) {
34
+ const trimmed = line.trim();
35
+ const match = /^\|\s*([A-Za-z0-9_]+)\b/.exec(trimmed);
36
+ if (match) {
37
+ const name = match[1];
38
+ if (name === undefined) continue;
39
+ if (!seen.has(name)) {
40
+ seen.add(name);
41
+ kept.push(' | ' + name);
42
+ }
43
+ continue;
44
+ }
45
+ if (trimmed.length) kept.push(line);
46
+ }
47
+ return `export type ${typeName} =\n` + kept.join('\n') + ';';
48
+ });
49
+ }
50
+
51
+ function escapeRegExpLiteral(value: string): string {
52
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
53
+ }
54
+
55
+ function deriveStrategyStateInputEntryByRoleNameValueType(parsedSchema: any): string {
56
+ const defs = parsedSchema?.$defs ?? parsedSchema?.projectionSchema?.$defs;
57
+ const def = defs?.StrategyStateInputEntryByRoleName;
58
+ const schemaNode = def?.projectionSchema ?? def;
59
+ const additionalProperties = schemaNode?.additionalProperties;
60
+
61
+ if (!additionalProperties || typeof additionalProperties !== 'object' || Array.isArray(additionalProperties)) {
62
+ return 'StrategyStateInputEntry';
63
+ }
64
+
65
+ const union = (additionalProperties as any).oneOf ?? (additionalProperties as any).anyOf;
66
+ if (!Array.isArray(union) || union.length === 0) {
67
+ const ref = (additionalProperties as any).$ref;
68
+ const match = typeof ref === 'string'
69
+ ? /^#(?:\/(?:\$defs|definitions)\/)?([A-Za-z_][A-Za-z0-9_]*)$/.exec(ref)
70
+ : undefined;
71
+ const typeName = match?.[1];
72
+ return typeName ?? 'StrategyStateInputEntry';
73
+ }
74
+
75
+ const names: string[] = [];
76
+ for (const item of union) {
77
+ const ref = item && typeof item === 'object' ? (item as any).$ref : undefined;
78
+ if (typeof ref !== 'string') continue;
79
+
80
+ const match = /^#(?:\/(?:\$defs|definitions)\/)?([A-Za-z_][A-Za-z0-9_]*)$/.exec(ref);
81
+ const name = match?.[1];
82
+ if (name !== undefined) names.push(name);
83
+ }
84
+
85
+ const unique = Array.from(new Set(names));
86
+ if (!unique.length) return 'StrategyStateInputEntry';
87
+
88
+ return unique.join(' | ');
89
+ }
90
+
91
+ function fixRecursiveArrayAliasSelfReference(ts: string, typeName: string): string {
92
+ const lines = ts.split(/\r?\n/);
93
+ const typeNamePattern = escapeRegExpLiteral(typeName);
94
+ const startIndex = lines.findIndex((line) => new RegExp(`^export\\s+type\\s+${typeNamePattern}\\s*=\\s*$`).test(line.trimEnd()));
95
+ if (startIndex < 0) return ts;
96
+
97
+ let endIndex = -1;
98
+ for (let i = startIndex + 1; i < lines.length; i++) {
99
+ const line = lines[i];
100
+ if (line !== undefined && /^\s*};\s*$/.test(line)) {
101
+ endIndex = i;
102
+ break;
103
+ }
104
+ }
105
+ if (endIndex < 0) return ts;
106
+
107
+ for (let i = startIndex + 1; i < endIndex; i++) {
108
+ const line = lines[i];
109
+ if (line !== undefined && new RegExp(`^\\s*\\|\\s*${typeNamePattern}\\s*$`).test(line)) {
110
+ lines[i] = line.replace(new RegExp(`\\b${typeNamePattern}\\b`), `${typeName}[]`);
111
+ }
112
+ }
113
+
114
+ return lines.join('\n');
115
+ }
116
+
117
+ export function postProcessEmittedTypes(ts: string, parsedSchema: any): string {
118
+ ts = ts.replace(/^\s*\[k:\s*string\]:\s*unknown;\s*(?:\r?\n)?/gm, '');
119
+
120
+ ts = ts.replace(
121
+ /^(\s*)(\$defs\??:\s*)\{\s*\r?\n\s*\};/gm,
122
+ (_m, indent, head) => `${indent}${head}Record<string, unknown>;`,
123
+ );
124
+
125
+ ts = ts.replace(
126
+ /^(\s*)(properties\??:\s*)\{\s*\r?\n\s*\};/gm,
127
+ (_m, indent, head) => `${indent}${head}Record<string, unknown>;`,
128
+ );
129
+
130
+ ts = ts.replace(
131
+ /^(\s*)((?:allOf|anyOf|oneOf)\??:\s*)\{\s*\r?\n\s*\}\[\];/gm,
132
+ (_m, indent, head) => `${indent}${head}Array<{[k: string]: unknown}>;`,
133
+ );
134
+
135
+ ts = removeDuplicateUnionEntries(ts);
136
+
137
+ const schemaId = parsedSchema?.$id;
138
+ if (typeof schemaId === 'string') {
139
+ const match = /\/([^/]+)\.json$/i.exec(schemaId);
140
+ const rootBaseName = match?.[1];
141
+ if (rootBaseName) {
142
+ const rootTypeNameEsc = escapeRegExpLiteral(rootBaseName);
143
+ const generatedRootSchemaNamePattern = `Https[A-Za-z0-9_]*${rootTypeNameEsc}(?:Json)?`;
144
+
145
+ ts = ts.replace(
146
+ new RegExp(`^export interface ${generatedRootSchemaNamePattern}\\s*\\{\\s*\\}\\s*(?:\\r?\\n)?`, 'gm'),
147
+ '',
148
+ );
149
+
150
+ ts = ts.replace(
151
+ new RegExp(`\\b${generatedRootSchemaNamePattern}\\b`, 'g'),
152
+ rootBaseName,
153
+ );
154
+ }
155
+ }
156
+
157
+ if (!ts || !ts.trim()) {
158
+ throw new Error('Type generator emitted no output for schema.');
159
+ }
160
+
161
+ const patternTemplates = loadPatternTemplatesFromSchema(parsedSchema);
162
+ ts = ts.replace(
163
+ /^(export\s+type\s+)([A-Za-z_][A-Za-z0-9_]*(?:Id|Handle))(\s*=\s*)string\s*;$/gm,
164
+ (_m, p1, typeName, p3) => {
165
+ const tmpl = patternTemplates[typeName];
166
+ return `${p1}${typeName}${p3}${tmpl ?? 'string'};`;
167
+ },
168
+ );
169
+
170
+ const roleNameKeyType = 'RoleName';
171
+ const unthreadedToolStepPathKeyType = 'UnthreadedToolStepPath';
172
+ const threadedToolStepPathKeyType = 'ThreadedToolStepPath';
173
+ const toolStepPathKeyType = 'ToolStepPath';
174
+
175
+ ts = ts.replace(
176
+ /export interface RoleValueByName\s*\{\s*\[k:\s*string\]:\s*RoleValue;\s*\}/g,
177
+ `export type RoleValueByName = Record<${roleNameKeyType}, RoleValue>;`,
178
+ );
179
+
180
+ const strategyStateInputEntryByRoleNameValueType = deriveStrategyStateInputEntryByRoleNameValueType(parsedSchema);
181
+ ts = ts.replace(
182
+ /export interface StrategyStateInputEntryByRoleName\s*\{\s*\[k:\s*string\]:\s*[^;]+;\s*\}/g,
183
+ `export type StrategyStateInputEntryByRoleName = Record<${roleNameKeyType}, ${strategyStateInputEntryByRoleNameValueType}>;`,
184
+ );
185
+
186
+ ts = ts.replace(
187
+ /export\s+type\s+StrategyStateInputEntryByRoleName\s*=\s*Record<\s*RoleName\s*,\s*[^>]+>\s*;/g,
188
+ `export type StrategyStateInputEntryByRoleName = Record<${roleNameKeyType}, ${strategyStateInputEntryByRoleNameValueType}>;`,
189
+ );
190
+
191
+ ts = ts.replace(
192
+ /export interface StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath\s*\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\}/g,
193
+ `export type StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath = Record<${unthreadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
194
+ );
195
+
196
+ ts = ts.replace(
197
+ /export interface StrategyStateInputEntryByRoleNameByThreadedToolStepPath\s*\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\}/g,
198
+ `export type StrategyStateInputEntryByRoleNameByThreadedToolStepPath = Record<${threadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
199
+ );
200
+
201
+ ts = ts.replace(
202
+ /export\s+type\s+UnthreadedStrategyState\s*=\s*StrategyStateInputEntryByRoleNameByUnthreadedToolStepPath\s*;/g,
203
+ `export type UnthreadedStrategyState = Record<${unthreadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
204
+ );
205
+
206
+ ts = ts.replace(
207
+ /export\s+type\s+ThreadedStrategyState\s*=\s*StrategyStateInputEntryByRoleNameByThreadedToolStepPath\s*;/g,
208
+ `export type ThreadedStrategyState = Record<${threadedToolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
209
+ );
210
+
211
+ ts = ts.replace(
212
+ /(strategyState\??:\s*)\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\};/g,
213
+ `$1Record<${toolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
214
+ );
215
+
216
+ ts = ts.replace(
217
+ /(strategyStateUpdate\??:\s*)\{\s*\[k:\s*string\]:\s*StrategyStateInputEntryByRoleName;\s*\};/g,
218
+ `$1Record<${toolStepPathKeyType}, StrategyStateInputEntryByRoleName>;`,
219
+ );
220
+
221
+ ts = ts.replace(
222
+ /export interface StepsByThreadIndexFacet\s*\{\s*stepsByThreadIndex:\s*StepArrayArray;\s*\}/g,
223
+ 'export interface StepsByThreadIndexFacet {\n stepsByThreadIndex: StepArrayArray;\n}',
224
+ );
225
+
226
+ ts = fixRecursiveArrayAliasSelfReference(ts, 'JsonData');
227
+
228
+ return ts;
229
+ }
230
+
231
+ export function finalizeOutputDts(emittedType: string): string {
232
+ const banner = '// Auto-generated from JSON schemas. Do not edit.\n';
233
+
234
+ let output = banner + '\n' + emittedType + '\n';
235
+ output = output.replace(/^\s*\[k:\s*string\]:\s*unknown;\s*$/gm, '');
236
+ output = output
237
+ .replace(/\r?\n\s*\r?\n(\s*};)/g, '\n$1')
238
+ .replace(/\r?\n\s*\r?\n(\s*})/g, '\n$1')
239
+ .replace(/(\r?\n){3,}/g, '\n\n');
240
+
241
+ if (!/\bexport\b|\bdeclare\s+module\b|\bdeclare\s+namespace\b/.test(output)) {
242
+ output += '\nexport {}\n';
243
+ }
244
+
245
+ return output;
246
246
  }