exact-mirror 1.0.2 → 1.1.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.
@@ -57,6 +57,16 @@ export interface Instruction<Emit extends boolean = false> {
57
57
  typeCompilerWanred?: boolean;
58
58
  modules?: TModule<{}>;
59
59
  definitions: Record<string, AnySchema>;
60
+ /**
61
+ * Shared cyclic codegen state: generated per-definition mirror
62
+ * functions, grouped by `$defs` object identity
63
+ */
64
+ cyclic: CyclicContext;
65
+ /**
66
+ * `$defs` group the current cyclic definition body is generated
67
+ * against, used to resolve `Ref` nodes to function calls
68
+ */
69
+ cyclicDefs?: CyclicGroup;
60
70
  recursion: number;
61
71
  /**
62
72
  * @default 8
@@ -73,6 +83,15 @@ export interface Instruction<Emit extends boolean = false> {
73
83
  removeUnknownUnionType: boolean;
74
84
  }
75
85
  export declare function deepClone<T>(source: T, weak?: WeakMap<object, any>): T;
86
+ interface CyclicGroup {
87
+ defs: Record<string, AnySchema>;
88
+ names: Record<string, string>;
89
+ }
90
+ interface CyclicContext {
91
+ groups: Map<object, CyclicGroup>;
92
+ fns: string[];
93
+ count: number;
94
+ }
76
95
  export interface Manifest {
77
96
  source: string;
78
97
  externals: {
package/dist/cjs/index.js CHANGED
@@ -80,7 +80,7 @@ var handleRecord = (schema, property, instruction) => {
80
80
  var handleTuple = (schema, property, instruction) => {
81
81
  const i = instruction.array;
82
82
  instruction.array++;
83
- const isRoot = property === "v" && !instruction.unions.length;
83
+ const isRoot = property === "v" && !instruction.fromUnion;
84
84
  let v = "";
85
85
  if (!isRoot) v = `(()=>{`;
86
86
  v += `const ar${i}v=[`;
@@ -122,6 +122,59 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
122
122
  }
123
123
  return source;
124
124
  }
125
+ var handleCyclic = (schema, property, instruction) => {
126
+ const defs = schema.$defs;
127
+ let group = instruction.cyclic.groups.get(defs);
128
+ if (!group) {
129
+ group = { defs, names: {} };
130
+ instruction.cyclic.groups.set(defs, group);
131
+ for (const name in defs)
132
+ group.names[name] = `cy${instruction.cyclic.count++}`;
133
+ for (const name in defs)
134
+ instruction.cyclic.fns.push(
135
+ `function ${group.names[name]}(v){${mirror(defs[name], "v", {
136
+ ...instruction,
137
+ cyclicDefs: group,
138
+ fromUnion: false,
139
+ parentIsOptional: false,
140
+ optionals: [],
141
+ optionalsInArray: [],
142
+ unionKeys: {},
143
+ array: 0,
144
+ recursion: 0
145
+ })}}`
146
+ );
147
+ }
148
+ const fn = group.names[schema.$ref];
149
+ if (!fn)
150
+ throw new Error(
151
+ `[exact-mirror] cyclic reference "${schema.$ref}" is not found in $defs`
152
+ );
153
+ return `(${property}==null?${property}:${fn}(${property}))`;
154
+ };
155
+ var withDefs = (type, group) => {
156
+ if (Kind in type) {
157
+ if (type[Kind] === "Cyclic") return type;
158
+ if (type[Kind] === "Ref" && type.$ref in group.defs)
159
+ return Object.defineProperty(
160
+ { $defs: group.defs, $ref: type.$ref },
161
+ Kind,
162
+ { value: "Cyclic" }
163
+ );
164
+ }
165
+ let entry = "~check";
166
+ while (entry in group.defs) entry += "~";
167
+ const def = Object.create(
168
+ Object.getPrototypeOf(type),
169
+ Object.getOwnPropertyDescriptors(type)
170
+ );
171
+ def.$id = entry;
172
+ return Object.defineProperty(
173
+ { $defs: { ...group.defs, [entry]: def }, $ref: entry },
174
+ Kind,
175
+ { value: "Cyclic" }
176
+ );
177
+ };
125
178
  var handleUnion = (schemas, property, instruction) => {
126
179
  if (instruction.Compile === void 0) {
127
180
  if (!instruction.typeCompilerWanred) {
@@ -143,15 +196,6 @@ var handleUnion = (schemas, property, instruction) => {
143
196
  if (!(Kind in type) || !type.$ref) return type;
144
197
  if (type[Kind] === "This")
145
198
  return deepClone(instruction.definitions[type.$ref]);
146
- else if (type[Kind] === "Cyclic") {
147
- if (!instruction.modules)
148
- console.warn(
149
- new Error(
150
- "[exact-mirror] modules is required when using nested cyclic reference"
151
- )
152
- );
153
- else return instruction.modules.$defs[type.$ref];
154
- }
155
199
  return type;
156
200
  };
157
201
  let cleanThenCheck = "";
@@ -166,7 +210,11 @@ var handleUnion = (schemas, property, instruction) => {
166
210
  type.items[i2] = unwrapRef(type.items[i2]);
167
211
  else type.items = unwrapRef(type.items);
168
212
  }
169
- typeChecks.push(instruction.Compile(type));
213
+ typeChecks.push(
214
+ instruction.Compile(
215
+ instruction.cyclicDefs ? withDefs(type, instruction.cyclicDefs) : type
216
+ )
217
+ );
170
218
  v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(
171
219
  type,
172
220
  property,
@@ -193,12 +241,31 @@ if(d.unions[${ui}][${i}].Check(tmp))return tmp
193
241
  };
194
242
  var mirror = (schema, property, instruction) => {
195
243
  if (!schema) return "";
196
- const isRoot = property === "v" && !instruction.unions.length;
197
- if (Kind in schema && schema[Kind] === "~Cyclic" && schema.$ref in schema.$defs)
198
- return mirror(schema.$defs[schema.$ref], property, {
199
- ...instruction,
200
- definitions: Object.assign(instruction.definitions, schema.$defs)
201
- });
244
+ const isRoot = property === "v" && !instruction.fromUnion;
245
+ const optionalsLength = instruction.optionals.length;
246
+ try {
247
+ if (Kind in schema && schema[Kind] === "Cyclic") {
248
+ const call = handleCyclic(schema, property, instruction);
249
+ return isRoot ? `return ${call}` : call;
250
+ }
251
+ return mirrorNode(schema, property, instruction);
252
+ } catch (error) {
253
+ instruction.optionals.length = optionalsLength;
254
+ console.warn(
255
+ new Error(
256
+ "[exact-mirror] failed to generate mirror for a schema node, the node is passed through as-is. Please report this issue to https://github.com/elysiajs/exact-mirror/issues"
257
+ ),
258
+ error
259
+ );
260
+ return isRoot ? "return v" : property;
261
+ }
262
+ };
263
+ var mirrorNode = (schema, property, instruction) => {
264
+ const isRoot = property === "v" && !instruction.fromUnion;
265
+ if (instruction.cyclicDefs && Kind in schema && schema[Kind] === "Ref" && schema.$ref && schema.$ref in instruction.cyclicDefs.names) {
266
+ const call = `(${property}==null?${property}:${instruction.cyclicDefs.names[schema.$ref]}(${property}))`;
267
+ return isRoot ? `return ${call}` : call;
268
+ }
202
269
  if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
203
270
  return `return ${sanitize("v", instruction.sanitize?.length, schema)}`;
204
271
  if (instruction.recursion >= instruction.recursionLimit) return property;
@@ -212,6 +279,10 @@ var mirror = (schema, property, instruction) => {
212
279
  break;
213
280
  }
214
281
  schema = mergeObjectIntersection(schema);
282
+ if (!schema.properties) {
283
+ v = property;
284
+ break;
285
+ }
215
286
  v += "{";
216
287
  if (schema.additionalProperties) v += `...${property},`;
217
288
  const keys = Object.keys(schema.properties);
@@ -270,29 +341,38 @@ var mirror = (schema, property, instruction) => {
270
341
  v += "}";
271
342
  break;
272
343
  case "array":
344
+ if (!schema.items) {
345
+ v = property;
346
+ break;
347
+ }
273
348
  if (
274
349
  // @ts-expect-error
275
350
  schema.items.type !== "object" && // @ts-expect-error
276
351
  schema.items.type !== "array"
277
352
  ) {
353
+ const cyclicItems = instruction.cyclicDefs !== void 0 && !Array.isArray(schema.items) && Kind in schema.items && schema.items[Kind] === "Ref" && schema.items.$ref !== void 0 && schema.items.$ref in instruction.cyclicDefs.names;
278
354
  if (Array.isArray(schema.items)) {
279
355
  v = handleTuple(schema.items, property, instruction);
280
356
  break;
281
- } else if (isRoot && !Array.isArray(schema.items.anyOf))
282
- return "return v";
283
- else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
284
- v = mirror(
285
- deepClone(instruction.definitions[schema.items.$ref]),
286
- property,
287
- {
288
- ...instruction,
289
- parentIsOptional: true,
290
- recursion: instruction.recursion + 1
291
- }
292
- );
293
- else if (!Array.isArray(schema.items.anyOf)) {
294
- v = property;
295
- break;
357
+ } else if (!cyclicItems) {
358
+ if (isRoot && !Array.isArray(schema.items.anyOf))
359
+ return "return v";
360
+ else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
361
+ v = mirror(
362
+ deepClone(
363
+ instruction.definitions[schema.items.$ref]
364
+ ),
365
+ property,
366
+ {
367
+ ...instruction,
368
+ parentIsOptional: true,
369
+ recursion: instruction.recursion + 1
370
+ }
371
+ );
372
+ else if (!Array.isArray(schema.items.anyOf)) {
373
+ v = property;
374
+ break;
375
+ }
296
376
  }
297
377
  }
298
378
  const i = instruction.array;
@@ -357,6 +437,7 @@ var createMirror = (schema, {
357
437
  emit
358
438
  } = {}) => {
359
439
  const unions = [];
440
+ const cyclic = { groups: /* @__PURE__ */ new Map(), fns: [], count: 0 };
360
441
  if (typeof sanitize2 === "function") sanitize2 = [sanitize2];
361
442
  const f = mirror(schema, "v", {
362
443
  optionals: [],
@@ -369,21 +450,23 @@ var createMirror = (schema, {
369
450
  modules,
370
451
  // @ts-ignore private property
371
452
  definitions: definitions ?? modules?.$defs ?? {},
453
+ cyclic,
372
454
  sanitize: sanitize2,
373
455
  recursion: 0,
374
456
  recursionLimit,
375
457
  removeUnknownUnionType
376
458
  });
459
+ const fns = cyclic.fns.length ? cyclic.fns.join("\n") + "\n" : "";
377
460
  if (!unions.length && !sanitize2?.length) {
378
- if (emit) return { source: f, externals: void 0 };
379
- return Function("v", f);
461
+ if (emit) return { source: fns + f, externals: void 0 };
462
+ return Function("v", fns + f);
380
463
  }
381
464
  let hof;
382
465
  if (sanitize2?.length) {
383
466
  hof = {};
384
467
  for (let i = 0; i < sanitize2.length; i++) hof[`h${i}`] = sanitize2[i];
385
468
  }
386
- const source = `return function mirror(v){${f}}`;
469
+ const source = `${fns}return function mirror(v){${f}}`;
387
470
  if (emit)
388
471
  return {
389
472
  source,
package/dist/index.d.ts CHANGED
@@ -57,6 +57,16 @@ export interface Instruction<Emit extends boolean = false> {
57
57
  typeCompilerWanred?: boolean;
58
58
  modules?: TModule<{}>;
59
59
  definitions: Record<string, AnySchema>;
60
+ /**
61
+ * Shared cyclic codegen state: generated per-definition mirror
62
+ * functions, grouped by `$defs` object identity
63
+ */
64
+ cyclic: CyclicContext;
65
+ /**
66
+ * `$defs` group the current cyclic definition body is generated
67
+ * against, used to resolve `Ref` nodes to function calls
68
+ */
69
+ cyclicDefs?: CyclicGroup;
60
70
  recursion: number;
61
71
  /**
62
72
  * @default 8
@@ -73,6 +83,15 @@ export interface Instruction<Emit extends boolean = false> {
73
83
  removeUnknownUnionType: boolean;
74
84
  }
75
85
  export declare function deepClone<T>(source: T, weak?: WeakMap<object, any>): T;
86
+ interface CyclicGroup {
87
+ defs: Record<string, AnySchema>;
88
+ names: Record<string, string>;
89
+ }
90
+ interface CyclicContext {
91
+ groups: Map<object, CyclicGroup>;
92
+ fns: string[];
93
+ count: number;
94
+ }
76
95
  export interface Manifest {
77
96
  source: string;
78
97
  externals: {
package/dist/index.mjs CHANGED
@@ -53,7 +53,7 @@ var handleRecord = (schema, property, instruction) => {
53
53
  var handleTuple = (schema, property, instruction) => {
54
54
  const i = instruction.array;
55
55
  instruction.array++;
56
- const isRoot = property === "v" && !instruction.unions.length;
56
+ const isRoot = property === "v" && !instruction.fromUnion;
57
57
  let v = "";
58
58
  if (!isRoot) v = `(()=>{`;
59
59
  v += `const ar${i}v=[`;
@@ -95,6 +95,59 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
95
95
  }
96
96
  return source;
97
97
  }
98
+ var handleCyclic = (schema, property, instruction) => {
99
+ const defs = schema.$defs;
100
+ let group = instruction.cyclic.groups.get(defs);
101
+ if (!group) {
102
+ group = { defs, names: {} };
103
+ instruction.cyclic.groups.set(defs, group);
104
+ for (const name in defs)
105
+ group.names[name] = `cy${instruction.cyclic.count++}`;
106
+ for (const name in defs)
107
+ instruction.cyclic.fns.push(
108
+ `function ${group.names[name]}(v){${mirror(defs[name], "v", {
109
+ ...instruction,
110
+ cyclicDefs: group,
111
+ fromUnion: false,
112
+ parentIsOptional: false,
113
+ optionals: [],
114
+ optionalsInArray: [],
115
+ unionKeys: {},
116
+ array: 0,
117
+ recursion: 0
118
+ })}}`
119
+ );
120
+ }
121
+ const fn = group.names[schema.$ref];
122
+ if (!fn)
123
+ throw new Error(
124
+ `[exact-mirror] cyclic reference "${schema.$ref}" is not found in $defs`
125
+ );
126
+ return `(${property}==null?${property}:${fn}(${property}))`;
127
+ };
128
+ var withDefs = (type, group) => {
129
+ if (Kind in type) {
130
+ if (type[Kind] === "Cyclic") return type;
131
+ if (type[Kind] === "Ref" && type.$ref in group.defs)
132
+ return Object.defineProperty(
133
+ { $defs: group.defs, $ref: type.$ref },
134
+ Kind,
135
+ { value: "Cyclic" }
136
+ );
137
+ }
138
+ let entry = "~check";
139
+ while (entry in group.defs) entry += "~";
140
+ const def = Object.create(
141
+ Object.getPrototypeOf(type),
142
+ Object.getOwnPropertyDescriptors(type)
143
+ );
144
+ def.$id = entry;
145
+ return Object.defineProperty(
146
+ { $defs: { ...group.defs, [entry]: def }, $ref: entry },
147
+ Kind,
148
+ { value: "Cyclic" }
149
+ );
150
+ };
98
151
  var handleUnion = (schemas, property, instruction) => {
99
152
  if (instruction.Compile === void 0) {
100
153
  if (!instruction.typeCompilerWanred) {
@@ -116,15 +169,6 @@ var handleUnion = (schemas, property, instruction) => {
116
169
  if (!(Kind in type) || !type.$ref) return type;
117
170
  if (type[Kind] === "This")
118
171
  return deepClone(instruction.definitions[type.$ref]);
119
- else if (type[Kind] === "Cyclic") {
120
- if (!instruction.modules)
121
- console.warn(
122
- new Error(
123
- "[exact-mirror] modules is required when using nested cyclic reference"
124
- )
125
- );
126
- else return instruction.modules.$defs[type.$ref];
127
- }
128
172
  return type;
129
173
  };
130
174
  let cleanThenCheck = "";
@@ -139,7 +183,11 @@ var handleUnion = (schemas, property, instruction) => {
139
183
  type.items[i2] = unwrapRef(type.items[i2]);
140
184
  else type.items = unwrapRef(type.items);
141
185
  }
142
- typeChecks.push(instruction.Compile(type));
186
+ typeChecks.push(
187
+ instruction.Compile(
188
+ instruction.cyclicDefs ? withDefs(type, instruction.cyclicDefs) : type
189
+ )
190
+ );
143
191
  v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(
144
192
  type,
145
193
  property,
@@ -166,12 +214,31 @@ if(d.unions[${ui}][${i}].Check(tmp))return tmp
166
214
  };
167
215
  var mirror = (schema, property, instruction) => {
168
216
  if (!schema) return "";
169
- const isRoot = property === "v" && !instruction.unions.length;
170
- if (Kind in schema && schema[Kind] === "~Cyclic" && schema.$ref in schema.$defs)
171
- return mirror(schema.$defs[schema.$ref], property, {
172
- ...instruction,
173
- definitions: Object.assign(instruction.definitions, schema.$defs)
174
- });
217
+ const isRoot = property === "v" && !instruction.fromUnion;
218
+ const optionalsLength = instruction.optionals.length;
219
+ try {
220
+ if (Kind in schema && schema[Kind] === "Cyclic") {
221
+ const call = handleCyclic(schema, property, instruction);
222
+ return isRoot ? `return ${call}` : call;
223
+ }
224
+ return mirrorNode(schema, property, instruction);
225
+ } catch (error) {
226
+ instruction.optionals.length = optionalsLength;
227
+ console.warn(
228
+ new Error(
229
+ "[exact-mirror] failed to generate mirror for a schema node, the node is passed through as-is. Please report this issue to https://github.com/elysiajs/exact-mirror/issues"
230
+ ),
231
+ error
232
+ );
233
+ return isRoot ? "return v" : property;
234
+ }
235
+ };
236
+ var mirrorNode = (schema, property, instruction) => {
237
+ const isRoot = property === "v" && !instruction.fromUnion;
238
+ if (instruction.cyclicDefs && Kind in schema && schema[Kind] === "Ref" && schema.$ref && schema.$ref in instruction.cyclicDefs.names) {
239
+ const call = `(${property}==null?${property}:${instruction.cyclicDefs.names[schema.$ref]}(${property}))`;
240
+ return isRoot ? `return ${call}` : call;
241
+ }
175
242
  if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
176
243
  return `return ${sanitize("v", instruction.sanitize?.length, schema)}`;
177
244
  if (instruction.recursion >= instruction.recursionLimit) return property;
@@ -185,6 +252,10 @@ var mirror = (schema, property, instruction) => {
185
252
  break;
186
253
  }
187
254
  schema = mergeObjectIntersection(schema);
255
+ if (!schema.properties) {
256
+ v = property;
257
+ break;
258
+ }
188
259
  v += "{";
189
260
  if (schema.additionalProperties) v += `...${property},`;
190
261
  const keys = Object.keys(schema.properties);
@@ -243,29 +314,38 @@ var mirror = (schema, property, instruction) => {
243
314
  v += "}";
244
315
  break;
245
316
  case "array":
317
+ if (!schema.items) {
318
+ v = property;
319
+ break;
320
+ }
246
321
  if (
247
322
  // @ts-expect-error
248
323
  schema.items.type !== "object" && // @ts-expect-error
249
324
  schema.items.type !== "array"
250
325
  ) {
326
+ const cyclicItems = instruction.cyclicDefs !== void 0 && !Array.isArray(schema.items) && Kind in schema.items && schema.items[Kind] === "Ref" && schema.items.$ref !== void 0 && schema.items.$ref in instruction.cyclicDefs.names;
251
327
  if (Array.isArray(schema.items)) {
252
328
  v = handleTuple(schema.items, property, instruction);
253
329
  break;
254
- } else if (isRoot && !Array.isArray(schema.items.anyOf))
255
- return "return v";
256
- else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
257
- v = mirror(
258
- deepClone(instruction.definitions[schema.items.$ref]),
259
- property,
260
- {
261
- ...instruction,
262
- parentIsOptional: true,
263
- recursion: instruction.recursion + 1
264
- }
265
- );
266
- else if (!Array.isArray(schema.items.anyOf)) {
267
- v = property;
268
- break;
330
+ } else if (!cyclicItems) {
331
+ if (isRoot && !Array.isArray(schema.items.anyOf))
332
+ return "return v";
333
+ else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
334
+ v = mirror(
335
+ deepClone(
336
+ instruction.definitions[schema.items.$ref]
337
+ ),
338
+ property,
339
+ {
340
+ ...instruction,
341
+ parentIsOptional: true,
342
+ recursion: instruction.recursion + 1
343
+ }
344
+ );
345
+ else if (!Array.isArray(schema.items.anyOf)) {
346
+ v = property;
347
+ break;
348
+ }
269
349
  }
270
350
  }
271
351
  const i = instruction.array;
@@ -330,6 +410,7 @@ var createMirror = (schema, {
330
410
  emit
331
411
  } = {}) => {
332
412
  const unions = [];
413
+ const cyclic = { groups: /* @__PURE__ */ new Map(), fns: [], count: 0 };
333
414
  if (typeof sanitize2 === "function") sanitize2 = [sanitize2];
334
415
  const f = mirror(schema, "v", {
335
416
  optionals: [],
@@ -342,21 +423,23 @@ var createMirror = (schema, {
342
423
  modules,
343
424
  // @ts-ignore private property
344
425
  definitions: definitions ?? modules?.$defs ?? {},
426
+ cyclic,
345
427
  sanitize: sanitize2,
346
428
  recursion: 0,
347
429
  recursionLimit,
348
430
  removeUnknownUnionType
349
431
  });
432
+ const fns = cyclic.fns.length ? cyclic.fns.join("\n") + "\n" : "";
350
433
  if (!unions.length && !sanitize2?.length) {
351
- if (emit) return { source: f, externals: void 0 };
352
- return Function("v", f);
434
+ if (emit) return { source: fns + f, externals: void 0 };
435
+ return Function("v", fns + f);
353
436
  }
354
437
  let hof;
355
438
  if (sanitize2?.length) {
356
439
  hof = {};
357
440
  for (let i = 0; i < sanitize2.length; i++) hof[`h${i}`] = sanitize2[i];
358
441
  }
359
- const source = `return function mirror(v){${f}}`;
442
+ const source = `${fns}return function mirror(v){${f}}`;
360
443
  if (emit)
361
444
  return {
362
445
  source,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exact-mirror",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Mirror exact value to TypeBox/OpenAPI model",
5
5
  "license": "MIT",
6
6
  "scripts": {