ajsc 2.0.0 → 3.0.0
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/JSONSchemaConverter.d.ts +4 -12
- package/dist/JSONSchemaConverter.js +135 -100
- package/dist/JSONSchemaConverter.js.map +1 -1
- package/dist/JSONSchemaConverter.test.js +195 -0
- package/dist/JSONSchemaConverter.test.js.map +1 -1
- package/dist/TypescriptBaseConverter.d.ts +29 -38
- package/dist/TypescriptBaseConverter.js +156 -85
- package/dist/TypescriptBaseConverter.js.map +1 -1
- package/dist/TypescriptConverter.d.ts +3 -6
- package/dist/TypescriptConverter.js +7 -107
- package/dist/TypescriptConverter.js.map +1 -1
- package/dist/TypescriptConverter.test.js +24 -0
- package/dist/TypescriptConverter.test.js.map +1 -1
- package/dist/TypescriptProcedureConverter.d.ts +6 -7
- package/dist/TypescriptProcedureConverter.js +19 -94
- package/dist/TypescriptProcedureConverter.js.map +1 -1
- package/dist/{TypescriptProceduresConverter.test.js → TypescriptProcedureConverter.test.js} +1 -2
- package/dist/TypescriptProcedureConverter.test.js.map +1 -0
- package/dist/types.d.ts +46 -6
- package/dist/utils/path-utils.test.js.map +1 -1
- package/package.json +6 -6
- package/dist/TypescriptProceduresConverter.test.js.map +0 -1
- package/src/JSONSchemaConverter.test.ts +0 -342
- package/src/JSONSchemaConverter.ts +0 -546
- package/src/Typebox.test.ts +0 -127
- package/src/TypescriptBaseConverter.ts +0 -190
- package/src/TypescriptConverter.test.ts +0 -399
- package/src/TypescriptConverter.ts +0 -189
- package/src/TypescriptProcedureConverter.ts +0 -180
- package/src/TypescriptProceduresConverter.test.ts +0 -1013
- package/src/index.ts +0 -4
- package/src/types.ts +0 -108
- package/src/utils/path-utils.test.ts +0 -102
- package/src/utils/path-utils.ts +0 -89
- package/src/utils/to-pascal-case.ts +0 -10
- /package/dist/{TypescriptProceduresConverter.test.d.ts → TypescriptProcedureConverter.test.d.ts} +0 -0
|
@@ -1,546 +0,0 @@
|
|
|
1
|
-
import { JSONSchema7, JSONSchema7Definition } from "json-schema";
|
|
2
|
-
import { ConverterOptions, IRNode, SignatureOccurrences } from "./types.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* JSONSchemaConverter converts a JSON Schema (Draft‑07) into an
|
|
6
|
-
* intermediate representation (IR) that can later be transformed
|
|
7
|
-
* into target language code via a language plugin.
|
|
8
|
-
*
|
|
9
|
-
* This implementation now supports internal definitions via both
|
|
10
|
-
* "$defs" and "definitions", and resolves local "$ref" pointers.
|
|
11
|
-
*/
|
|
12
|
-
export class JSONSchemaConverter {
|
|
13
|
-
private ir: IRNode;
|
|
14
|
-
// The root schema is stored to support reference resolution.
|
|
15
|
-
private rootSchema: JSONSchema7Definition | null = null;
|
|
16
|
-
// Keep track of the occurrences of each schema signature and the schema/property names
|
|
17
|
-
// used to reference the signature.
|
|
18
|
-
private signatureOccurrences: SignatureOccurrences = new Map();
|
|
19
|
-
|
|
20
|
-
get irNode(): IRNode {
|
|
21
|
-
return this.ir;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get signatureOccurrencesMap(): SignatureOccurrences {
|
|
25
|
-
return this.signatureOccurrences;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
29
|
-
schema: JSONSchema7Definition,
|
|
30
|
-
private opts?: ConverterOptions,
|
|
31
|
-
) {
|
|
32
|
-
if (typeof schema === "object") {
|
|
33
|
-
schema.title = schema.title || "Root";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Optionally validate the schema.
|
|
37
|
-
if (this.opts?.validateSchema) {
|
|
38
|
-
this.validateSchema(schema);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Resolve references (for now, this is a placeholder).
|
|
42
|
-
const resolvedSchema = this.resolveReferences(schema);
|
|
43
|
-
|
|
44
|
-
// Store the root schema (if it is an object) to support local $ref resolution.
|
|
45
|
-
if (typeof resolvedSchema === "object") {
|
|
46
|
-
this.rootSchema = resolvedSchema;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Convert the resolved schema to our intermediate representation.
|
|
50
|
-
let ir = this.convertToIR(resolvedSchema);
|
|
51
|
-
|
|
52
|
-
// add calculated signature occurrences by traversing the IR for all object types
|
|
53
|
-
|
|
54
|
-
// Apply any custom IR transformation.
|
|
55
|
-
if (this.opts?.transform) {
|
|
56
|
-
ir = this.opts.transform(ir);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
this.ir = ir;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private calcObjSignatureOccurrences(
|
|
63
|
-
schema: JSONSchema7Definition & {
|
|
64
|
-
type: "object";
|
|
65
|
-
properties: Record<string, JSONSchema7Definition>;
|
|
66
|
-
},
|
|
67
|
-
node: IRNode & { name: string },
|
|
68
|
-
): string {
|
|
69
|
-
// If no path then it's the root object
|
|
70
|
-
if (!node.path) {
|
|
71
|
-
return "";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const signature = this.getSchemaSignature(schema);
|
|
75
|
-
|
|
76
|
-
if (Object.keys(node.properties ?? {}).length === 0) {
|
|
77
|
-
// empty object - can be extended with options ie: emptyObjectAsUnknown
|
|
78
|
-
return signature;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (this.signatureOccurrences.has(signature)) {
|
|
82
|
-
const occurrences = this.signatureOccurrences.get(signature)!;
|
|
83
|
-
occurrences.total += 1;
|
|
84
|
-
|
|
85
|
-
const foundPath = occurrences.occurrences.find(
|
|
86
|
-
(x) => x.nodePath === node.path,
|
|
87
|
-
);
|
|
88
|
-
if (foundPath) {
|
|
89
|
-
foundPath.count += 1;
|
|
90
|
-
} else {
|
|
91
|
-
occurrences.occurrences.push({ node, nodePath: node.path, count: 1 });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.signatureOccurrences.set(signature, occurrences);
|
|
95
|
-
} else {
|
|
96
|
-
this.signatureOccurrences.set(signature, {
|
|
97
|
-
total: 1,
|
|
98
|
-
occurrences: [{ node, nodePath: node.path, count: 1 }],
|
|
99
|
-
signature,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return signature;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Recursively converts a JSON Schema definition into an IRNode.
|
|
108
|
-
*
|
|
109
|
-
* This method now handles $ref resolution (using the root schema),
|
|
110
|
-
* combinators (oneOf, anyOf, allOf), as well as enums, const, objects,
|
|
111
|
-
* arrays, and primitives.
|
|
112
|
-
*/
|
|
113
|
-
private convertSchema(
|
|
114
|
-
schema: JSONSchema7Definition,
|
|
115
|
-
ctx: {
|
|
116
|
-
path: string;
|
|
117
|
-
},
|
|
118
|
-
): IRNode {
|
|
119
|
-
if (!schema) {
|
|
120
|
-
// Return an unknown
|
|
121
|
-
return {
|
|
122
|
-
type: "null",
|
|
123
|
-
path: ctx.path,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
// Handle boolean schemas.
|
|
127
|
-
if (typeof schema === "boolean") {
|
|
128
|
-
if (schema === true) {
|
|
129
|
-
// A schema of 'true' accepts any value.
|
|
130
|
-
return {
|
|
131
|
-
type: "object",
|
|
132
|
-
properties: {},
|
|
133
|
-
path: ctx.path,
|
|
134
|
-
};
|
|
135
|
-
} else {
|
|
136
|
-
throw new Error(
|
|
137
|
-
"Encountered JSON Schema 'false', which is not supported.",
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// If the schema contains a $ref, resolve it.
|
|
143
|
-
if (schema.$ref) {
|
|
144
|
-
const resolved = this.resolveRef(schema);
|
|
145
|
-
return this.convertSchema(resolved, ctx);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Handle combinators.
|
|
149
|
-
if (schema.oneOf) {
|
|
150
|
-
const options = schema.oneOf.map((subSchema) =>
|
|
151
|
-
this.convertSchema(subSchema, ctx),
|
|
152
|
-
);
|
|
153
|
-
return {
|
|
154
|
-
type: "union",
|
|
155
|
-
options,
|
|
156
|
-
constraints: { combinator: "oneOf" },
|
|
157
|
-
path: ctx.path,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (schema.anyOf) {
|
|
161
|
-
const options = schema.anyOf.map((subSchema) =>
|
|
162
|
-
this.convertSchema(subSchema, ctx),
|
|
163
|
-
);
|
|
164
|
-
return {
|
|
165
|
-
type: "union",
|
|
166
|
-
options,
|
|
167
|
-
constraints: { combinator: "anyOf" },
|
|
168
|
-
path: ctx.path,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
if (schema.allOf) {
|
|
172
|
-
const options = schema.allOf.map((subSchema) =>
|
|
173
|
-
this.convertSchema(subSchema, ctx),
|
|
174
|
-
);
|
|
175
|
-
return {
|
|
176
|
-
type: "intersection",
|
|
177
|
-
options,
|
|
178
|
-
path: ctx.path,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Handle "enum".
|
|
183
|
-
if (schema.enum) {
|
|
184
|
-
return {
|
|
185
|
-
type: "enum",
|
|
186
|
-
values: schema.enum,
|
|
187
|
-
path: ctx.path,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Handle "const".
|
|
192
|
-
if (schema.const !== undefined) {
|
|
193
|
-
return {
|
|
194
|
-
type: "literal",
|
|
195
|
-
constraints: { value: schema.const },
|
|
196
|
-
path: ctx.path,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Process based on the "type" keyword.
|
|
201
|
-
if (schema.type) {
|
|
202
|
-
// If multiple types are provided, treat as a union.
|
|
203
|
-
if (Array.isArray(schema.type)) {
|
|
204
|
-
const options = schema.type.map((t) =>
|
|
205
|
-
this.convertSchema({ ...schema, type: t }, ctx),
|
|
206
|
-
);
|
|
207
|
-
return {
|
|
208
|
-
type: "union",
|
|
209
|
-
options,
|
|
210
|
-
path: ctx.path,
|
|
211
|
-
};
|
|
212
|
-
} else {
|
|
213
|
-
switch (schema.type) {
|
|
214
|
-
case "object": {
|
|
215
|
-
const name = this.getTypeName(schema, ctx);
|
|
216
|
-
|
|
217
|
-
const node: IRNode & {
|
|
218
|
-
name: string;
|
|
219
|
-
properties: {};
|
|
220
|
-
type: "object";
|
|
221
|
-
} = {
|
|
222
|
-
type: "object",
|
|
223
|
-
properties: {},
|
|
224
|
-
path: ctx.path,
|
|
225
|
-
name,
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
if (schema.properties) {
|
|
229
|
-
for (const key in schema.properties) {
|
|
230
|
-
const propSchema = schema.properties[key];
|
|
231
|
-
|
|
232
|
-
let child = this.convertSchema(propSchema, {
|
|
233
|
-
path: ctx.path ? `${ctx.path}.${key}` : key,
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Mark the property as required if listed in the "required" array.
|
|
237
|
-
child.required = schema.required
|
|
238
|
-
? schema.required.includes(key)
|
|
239
|
-
: false;
|
|
240
|
-
node.properties![key] = child;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (schema.additionalProperties) {
|
|
245
|
-
/**
|
|
246
|
-
* Json-schema additionalProperties have unknown keys.
|
|
247
|
-
* @example json-schema
|
|
248
|
-
* {
|
|
249
|
-
* "additionalProperties": {
|
|
250
|
-
* "type": "object",
|
|
251
|
-
* "properties": {
|
|
252
|
-
* "profile": {
|
|
253
|
-
* "type": "string"
|
|
254
|
-
* }
|
|
255
|
-
* },
|
|
256
|
-
* "required": [
|
|
257
|
-
* "profile"
|
|
258
|
-
* ]
|
|
259
|
-
* },
|
|
260
|
-
* "type": "object",
|
|
261
|
-
* "properties": {
|
|
262
|
-
* "id": {
|
|
263
|
-
* "type": "string"
|
|
264
|
-
* }
|
|
265
|
-
* },
|
|
266
|
-
* "required": [
|
|
267
|
-
* "id"
|
|
268
|
-
* ]
|
|
269
|
-
* }
|
|
270
|
-
*/
|
|
271
|
-
node.additionalProperties = this.convertSchema(
|
|
272
|
-
schema.additionalProperties,
|
|
273
|
-
{
|
|
274
|
-
// The "*" indicates an any/unknown key
|
|
275
|
-
path: ctx.path ? `${ctx.path}.*` : "*",
|
|
276
|
-
},
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (schema.patternProperties) {
|
|
281
|
-
/**
|
|
282
|
-
* Json-schema patternProperties are not directly supported in most languages.
|
|
283
|
-
* We mark each property key as
|
|
284
|
-
* @example json-schema
|
|
285
|
-
* {
|
|
286
|
-
* "type": "object",
|
|
287
|
-
* "patternProperties": {
|
|
288
|
-
* "^(.*)$": {
|
|
289
|
-
* "type": "object",
|
|
290
|
-
* "properties": {
|
|
291
|
-
* "name": {
|
|
292
|
-
* "type": "string"
|
|
293
|
-
* },
|
|
294
|
-
* "email": {
|
|
295
|
-
* "type": "string"
|
|
296
|
-
* }
|
|
297
|
-
* },
|
|
298
|
-
* "required": [
|
|
299
|
-
* "name",
|
|
300
|
-
* "email"
|
|
301
|
-
* ]
|
|
302
|
-
* }
|
|
303
|
-
* }
|
|
304
|
-
* }
|
|
305
|
-
*/
|
|
306
|
-
const propertySchemas = Object.values(schema.patternProperties);
|
|
307
|
-
|
|
308
|
-
if (propertySchemas.length > 1) {
|
|
309
|
-
// if multiple patternProperties, treat as a union of the property types.
|
|
310
|
-
node.additionalProperties = this.convertSchema(
|
|
311
|
-
{
|
|
312
|
-
anyOf: propertySchemas,
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
// The "*" indicates an any/unknown key
|
|
316
|
-
path: ctx.path ? `${ctx.path}.*` : "*",
|
|
317
|
-
},
|
|
318
|
-
);
|
|
319
|
-
} else {
|
|
320
|
-
// else just convert the single property schema
|
|
321
|
-
node.additionalProperties = this.convertSchema(
|
|
322
|
-
propertySchemas[0],
|
|
323
|
-
{
|
|
324
|
-
// The "*" indicates an any/unknown key
|
|
325
|
-
path: ctx.path ? `${ctx.path}.*` : "*",
|
|
326
|
-
},
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
node.signature = this.calcObjSignatureOccurrences(
|
|
332
|
-
schema as JSONSchema7 & {
|
|
333
|
-
type: "object";
|
|
334
|
-
properties: Record<string, JSONSchema7Definition>;
|
|
335
|
-
},
|
|
336
|
-
node,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
return node;
|
|
340
|
-
}
|
|
341
|
-
case "array": {
|
|
342
|
-
const name = this.getTypeName(schema, ctx);
|
|
343
|
-
|
|
344
|
-
const node: IRNode & { name: string } = {
|
|
345
|
-
type: "array",
|
|
346
|
-
path: ctx.path,
|
|
347
|
-
name,
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
if (schema.items) {
|
|
351
|
-
if (Array.isArray(schema.items)) {
|
|
352
|
-
// For tuple validation, treat as a union of the item types.
|
|
353
|
-
const options = schema.items.map((item, i) =>
|
|
354
|
-
this.convertSchema(item, ctx),
|
|
355
|
-
);
|
|
356
|
-
node.items = { type: "union", options, path: ctx.path };
|
|
357
|
-
} else {
|
|
358
|
-
node.items = this.convertSchema(schema.items, {
|
|
359
|
-
path: ctx.path ? `${ctx.path}.0` : "0",
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return node;
|
|
364
|
-
}
|
|
365
|
-
case "string":
|
|
366
|
-
case "number":
|
|
367
|
-
case "integer":
|
|
368
|
-
case "boolean":
|
|
369
|
-
case "null": {
|
|
370
|
-
const node: IRNode = { type: schema.type, path: ctx.path };
|
|
371
|
-
node.constraints = {};
|
|
372
|
-
if (schema.pattern) node.constraints.pattern = schema.pattern;
|
|
373
|
-
if (schema.minLength !== undefined)
|
|
374
|
-
node.constraints.minLength = schema.minLength;
|
|
375
|
-
if (schema.maxLength !== undefined)
|
|
376
|
-
node.constraints.maxLength = schema.maxLength;
|
|
377
|
-
if (schema.minimum !== undefined)
|
|
378
|
-
node.constraints.minimum = schema.minimum;
|
|
379
|
-
if (schema.maximum !== undefined)
|
|
380
|
-
node.constraints.maximum = schema.maximum;
|
|
381
|
-
return node;
|
|
382
|
-
}
|
|
383
|
-
default:
|
|
384
|
-
throw new Error(
|
|
385
|
-
`Unsupported schema type: ${schema.type} - ${JSON.stringify(schema)}`,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Fallback: if no type is provided, assume an object.
|
|
392
|
-
return { type: "object", properties: {}, path: ctx.path };
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Converts a JSON Schema definition to an IRNode.
|
|
397
|
-
*/
|
|
398
|
-
public convertToIRSchema(schema: JSONSchema7Definition): IRNode {
|
|
399
|
-
// reset the root schema
|
|
400
|
-
this.rootSchema = null;
|
|
401
|
-
|
|
402
|
-
// Optionally validate the schema.
|
|
403
|
-
if (this.opts?.validateSchema) {
|
|
404
|
-
this.validateSchema(schema);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Resolve references (for now, this is a placeholder).
|
|
408
|
-
const resolvedSchema = this.resolveReferences(schema);
|
|
409
|
-
|
|
410
|
-
// Store the root schema (if it is an object) to support local $ref resolution.
|
|
411
|
-
if (typeof resolvedSchema === "object") {
|
|
412
|
-
this.rootSchema = resolvedSchema;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Convert the resolved schema to our intermediate representation.
|
|
416
|
-
let ir = this.convertToIR(resolvedSchema);
|
|
417
|
-
|
|
418
|
-
// Apply any custom IR transformation.
|
|
419
|
-
if (this.opts?.transform) {
|
|
420
|
-
ir = this.opts.transform(ir);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return ir;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
private validateSchema(schema: JSONSchema7Definition): void {
|
|
427
|
-
// Implement or integrate with a JSON Schema validator if desired.
|
|
428
|
-
// For now, we assume the schema is valid.
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* A placeholder for reference resolution. In a more advanced implementation,
|
|
433
|
-
* this method might inline external references or perform more complex processing.
|
|
434
|
-
* ie: fetching a remote/url $def
|
|
435
|
-
*/
|
|
436
|
-
private resolveReferences(
|
|
437
|
-
schema: JSONSchema7Definition,
|
|
438
|
-
): JSONSchema7Definition {
|
|
439
|
-
// For this implementation, simply return the schema unchanged.
|
|
440
|
-
return schema;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
private convertToIR(schema: JSONSchema7Definition): IRNode {
|
|
444
|
-
return this.convertSchema(schema, { path: "" });
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
private getNameFromPath(path: string): string | undefined {
|
|
448
|
-
// ignore numbers
|
|
449
|
-
const split = path.split(".").filter((x) => !x.match(/^\d+$/));
|
|
450
|
-
|
|
451
|
-
return split[split.length - 1] || undefined;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
private getNextObjectSequence(): number {
|
|
455
|
-
return this.signatureOccurrences.size;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
private getParentNameFromPath(path: string): string | undefined {
|
|
459
|
-
const split = path.split(".").filter((x) => !x.match(/^\d+$/));
|
|
460
|
-
// ignore numbers
|
|
461
|
-
return split[split.length - 2] || undefined;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Generates a unique signature for a schema.
|
|
466
|
-
* Uses a stable JSON.stringify that sorts keys to ensure that
|
|
467
|
-
* semantically equivalent schemas produce the same string.
|
|
468
|
-
*/
|
|
469
|
-
private getSchemaSignature(schema: JSONSchema7Definition): string {
|
|
470
|
-
function sortKeys(obj: any): any {
|
|
471
|
-
if (typeof obj !== "object" || obj === null) return obj;
|
|
472
|
-
if (Array.isArray(obj)) return obj.map(sortKeys);
|
|
473
|
-
const sorted: any = {};
|
|
474
|
-
Object.keys(obj)
|
|
475
|
-
.sort()
|
|
476
|
-
.forEach((key) => {
|
|
477
|
-
sorted[key] = sortKeys(obj[key]);
|
|
478
|
-
});
|
|
479
|
-
return sorted;
|
|
480
|
-
}
|
|
481
|
-
return this.simpleHash(JSON.stringify(sortKeys(schema))).toString();
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
private getTypeName(schema: JSONSchema7, ctx: { path: string }): string {
|
|
485
|
-
return (
|
|
486
|
-
schema.title ||
|
|
487
|
-
this.getNameFromPath(ctx.path) ||
|
|
488
|
-
`Object${this.getNextObjectSequence()}`
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Resolves a local $ref using the stored root schema.
|
|
494
|
-
*
|
|
495
|
-
* This method expects local references (starting with "#/") and uses a simple
|
|
496
|
-
* JSON Pointer resolution algorithm.
|
|
497
|
-
*/
|
|
498
|
-
private resolveRef(schema: JSONSchema7): JSONSchema7Definition {
|
|
499
|
-
if (!schema.$ref) return schema as unknown as JSONSchema7Definition;
|
|
500
|
-
if (!this.rootSchema || typeof this.rootSchema !== "object") {
|
|
501
|
-
throw new Error("Root schema not available for reference resolution.");
|
|
502
|
-
}
|
|
503
|
-
const ref = schema.$ref;
|
|
504
|
-
if (!ref.startsWith("#/")) {
|
|
505
|
-
throw new Error(
|
|
506
|
-
`Only local references are supported. Encountered: ${ref}`,
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
return this.resolvePointer(ref, this.rootSchema);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Resolves a JSON Pointer (RFC 6901) within the given document.
|
|
514
|
-
*
|
|
515
|
-
* @param pointer A JSON Pointer string (e.g., "#/definitions/Foo" or "#/$defs/Bar")
|
|
516
|
-
* @param document The root document object.
|
|
517
|
-
*/
|
|
518
|
-
private resolvePointer(pointer: string, document: any): any {
|
|
519
|
-
// Remove the leading '#' character.
|
|
520
|
-
if (pointer[0] === "#") {
|
|
521
|
-
pointer = pointer.substring(1);
|
|
522
|
-
}
|
|
523
|
-
if (!pointer) return document;
|
|
524
|
-
// Split the pointer by "/" and filter out empty parts.
|
|
525
|
-
const parts = pointer.split("/").filter((part) => part);
|
|
526
|
-
let current = document;
|
|
527
|
-
for (const part of parts) {
|
|
528
|
-
// Unescape any "~1" to "/" and "~0" to "~".
|
|
529
|
-
const unescaped = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
530
|
-
current = current[unescaped];
|
|
531
|
-
if (current === undefined) {
|
|
532
|
-
throw new Error(`Reference "${pointer}" not found in document.`);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
return current;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
private simpleHash(str: string) {
|
|
539
|
-
let hash = 0;
|
|
540
|
-
for (let i = 0; i < str.length; i++) {
|
|
541
|
-
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
542
|
-
hash |= 0; // Convert to 32bit integer
|
|
543
|
-
}
|
|
544
|
-
return hash;
|
|
545
|
-
}
|
|
546
|
-
}
|
package/src/Typebox.test.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { JSONSchemaConverter } from "./JSONSchemaConverter.js";
|
|
3
|
-
import { Type } from "@sinclair/typebox";
|
|
4
|
-
|
|
5
|
-
describe("Typebox to IRNode Test", () => {
|
|
6
|
-
it("should convert strings", () => {
|
|
7
|
-
expect(new JSONSchemaConverter(Type.String()).irNode.type).toMatch(
|
|
8
|
-
"string",
|
|
9
|
-
);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("should convert numbers", () => {
|
|
13
|
-
expect(new JSONSchemaConverter(Type.Number()).irNode.type).toMatch(
|
|
14
|
-
"number",
|
|
15
|
-
);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("should convert booleans", () => {
|
|
19
|
-
expect(new JSONSchemaConverter(Type.Boolean()).irNode.type).toMatch(
|
|
20
|
-
"boolean",
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("should convert null", () => {
|
|
25
|
-
expect(new JSONSchemaConverter(Type.Null()).irNode.type).toMatch("null");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should convert literals", () => {
|
|
29
|
-
expect(
|
|
30
|
-
new JSONSchemaConverter(Type.Literal("fixedValue")).irNode,
|
|
31
|
-
).toMatchObject({ type: "literal", constraints: { value: "fixedValue" } });
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("should convert enums", () => {
|
|
35
|
-
expect(
|
|
36
|
-
new JSONSchemaConverter(
|
|
37
|
-
Type.Enum({ Value1: "value1", Value2: "value2", Value3: "value3" }),
|
|
38
|
-
).irNode,
|
|
39
|
-
).toMatchObject({
|
|
40
|
-
type: "union",
|
|
41
|
-
options: [
|
|
42
|
-
{ type: "literal", constraints: { value: "value1" } },
|
|
43
|
-
{ type: "literal", constraints: { value: "value2" } },
|
|
44
|
-
{ type: "literal", constraints: { value: "value3" } },
|
|
45
|
-
],
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should convert Record", () => {
|
|
50
|
-
expect(
|
|
51
|
-
new JSONSchemaConverter(
|
|
52
|
-
Type.Record(
|
|
53
|
-
Type.String(),
|
|
54
|
-
Type.Object({
|
|
55
|
-
name: Type.String(),
|
|
56
|
-
email: Type.String(),
|
|
57
|
-
}),
|
|
58
|
-
),
|
|
59
|
-
).irNode,
|
|
60
|
-
).toMatchObject({
|
|
61
|
-
type: "object",
|
|
62
|
-
additionalProperties: {
|
|
63
|
-
type: "object",
|
|
64
|
-
properties: {
|
|
65
|
-
name: {
|
|
66
|
-
type: "string",
|
|
67
|
-
path: "*.name",
|
|
68
|
-
constraints: {},
|
|
69
|
-
required: true,
|
|
70
|
-
},
|
|
71
|
-
email: {
|
|
72
|
-
type: "string",
|
|
73
|
-
path: "*.email",
|
|
74
|
-
constraints: {},
|
|
75
|
-
required: true,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("should convert unions", () => {
|
|
83
|
-
expect(
|
|
84
|
-
new JSONSchemaConverter(Type.Union([Type.String(), Type.Number()]))
|
|
85
|
-
.irNode,
|
|
86
|
-
).toMatchObject({
|
|
87
|
-
type: "union",
|
|
88
|
-
options: [{ type: "string" }, { type: "number" }],
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("should convert composite unions", () => {
|
|
93
|
-
expect(
|
|
94
|
-
new JSONSchemaConverter(
|
|
95
|
-
Type.Composite([
|
|
96
|
-
Type.Object({ a: Type.String() }),
|
|
97
|
-
Type.Object({ b: Type.Number() }),
|
|
98
|
-
]),
|
|
99
|
-
).irNode,
|
|
100
|
-
).toMatchObject({
|
|
101
|
-
type: "object",
|
|
102
|
-
properties: {
|
|
103
|
-
a: { type: "string", path: "a", required: true },
|
|
104
|
-
b: { type: "number", path: "b", required: true },
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("should convert intersections", () => {
|
|
110
|
-
expect(
|
|
111
|
-
new JSONSchemaConverter(Type.Intersect([Type.String(), Type.Number()]))
|
|
112
|
-
.irNode,
|
|
113
|
-
).toMatchObject({
|
|
114
|
-
type: "intersection",
|
|
115
|
-
options: [{ type: "string" }, { type: "number" }],
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should convert arrays", () => {
|
|
120
|
-
expect(
|
|
121
|
-
new JSONSchemaConverter(Type.Array(Type.String())).irNode,
|
|
122
|
-
).toMatchObject({
|
|
123
|
-
type: "array",
|
|
124
|
-
items: { type: "string" },
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|