exact-mirror 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -28,13 +28,14 @@ __export(index_exports, {
28
28
  module.exports = __toCommonJS(index_exports);
29
29
  var Kind = "~kind";
30
30
  var Hint = "~hint";
31
- var isSpecialProperty = (name) => /(\ |-|\t|\n|\.|\[|\]|\{|\})/.test(name) || !isNaN(+name[0]);
31
+ var isSpecialProperty = (name) => !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
32
32
  var joinProperty = (v1, v2, isOptional = false) => {
33
33
  if (typeof v2 === "number") return `${v1}[${v2}]`;
34
- if (isSpecialProperty(v2)) return `${v1}${isOptional ? "?." : ""}["${v2}"]`;
34
+ if (isSpecialProperty(v2))
35
+ return `${v1}${isOptional ? "?." : ""}[${JSON.stringify(v2)}]`;
35
36
  return `${v1}${isOptional ? "?" : ""}.${v2}`;
36
37
  };
37
- var encodeProperty = (v) => isSpecialProperty(v) ? `"${v}"` : v;
38
+ var encodeProperty = (v) => isSpecialProperty(v) ? JSON.stringify(v) : v;
38
39
  var sanitize = (key, sanitize2 = 0, schema) => {
39
40
  if (schema.type !== "string" || schema.const || schema.trusted) return key;
40
41
  let hof = "";
@@ -80,7 +81,7 @@ var handleRecord = (schema, property, instruction) => {
80
81
  var handleTuple = (schema, property, instruction) => {
81
82
  const i = instruction.array;
82
83
  instruction.array++;
83
- const isRoot = property === "v" && !instruction.unions.length;
84
+ const isRoot = property === "v" && !instruction.fromUnion;
84
85
  let v = "";
85
86
  if (!isRoot) v = `(()=>{`;
86
87
  v += `const ar${i}v=[`;
@@ -122,6 +123,59 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
122
123
  }
123
124
  return source;
124
125
  }
126
+ var handleCyclic = (schema, property, instruction) => {
127
+ const defs = schema.$defs;
128
+ let group = instruction.cyclic.groups.get(defs);
129
+ if (!group) {
130
+ group = { defs, names: {} };
131
+ instruction.cyclic.groups.set(defs, group);
132
+ for (const name in defs)
133
+ group.names[name] = `cy${instruction.cyclic.count++}`;
134
+ for (const name in defs)
135
+ instruction.cyclic.fns.push(
136
+ `function ${group.names[name]}(v){${mirror(defs[name], "v", {
137
+ ...instruction,
138
+ cyclicDefs: group,
139
+ fromUnion: false,
140
+ parentIsOptional: false,
141
+ optionals: [],
142
+ optionalsInArray: [],
143
+ unionKeys: {},
144
+ array: 0,
145
+ recursion: 0
146
+ })}}`
147
+ );
148
+ }
149
+ const fn = group.names[schema.$ref];
150
+ if (!fn)
151
+ throw new Error(
152
+ `[exact-mirror] cyclic reference "${schema.$ref}" is not found in $defs`
153
+ );
154
+ return `(${property}==null?${property}:${fn}(${property}))`;
155
+ };
156
+ var withDefs = (type, group) => {
157
+ if (Kind in type) {
158
+ if (type[Kind] === "Cyclic") return type;
159
+ if (type[Kind] === "Ref" && type.$ref in group.defs)
160
+ return Object.defineProperty(
161
+ { $defs: group.defs, $ref: type.$ref },
162
+ Kind,
163
+ { value: "Cyclic" }
164
+ );
165
+ }
166
+ let entry = "~check";
167
+ while (entry in group.defs) entry += "~";
168
+ const def = Object.create(
169
+ Object.getPrototypeOf(type),
170
+ Object.getOwnPropertyDescriptors(type)
171
+ );
172
+ def.$id = entry;
173
+ return Object.defineProperty(
174
+ { $defs: { ...group.defs, [entry]: def }, $ref: entry },
175
+ Kind,
176
+ { value: "Cyclic" }
177
+ );
178
+ };
125
179
  var handleUnion = (schemas, property, instruction) => {
126
180
  if (instruction.Compile === void 0) {
127
181
  if (!instruction.typeCompilerWanred) {
@@ -143,15 +197,6 @@ var handleUnion = (schemas, property, instruction) => {
143
197
  if (!(Kind in type) || !type.$ref) return type;
144
198
  if (type[Kind] === "This")
145
199
  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
200
  return type;
156
201
  };
157
202
  let cleanThenCheck = "";
@@ -166,7 +211,11 @@ var handleUnion = (schemas, property, instruction) => {
166
211
  type.items[i2] = unwrapRef(type.items[i2]);
167
212
  else type.items = unwrapRef(type.items);
168
213
  }
169
- typeChecks.push(instruction.Compile(type));
214
+ typeChecks.push(
215
+ instruction.Compile(
216
+ instruction.cyclicDefs ? withDefs(type, instruction.cyclicDefs) : type
217
+ )
218
+ );
170
219
  v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(
171
220
  type,
172
221
  property,
@@ -193,12 +242,31 @@ if(d.unions[${ui}][${i}].Check(tmp))return tmp
193
242
  };
194
243
  var mirror = (schema, property, instruction) => {
195
244
  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
- });
245
+ const isRoot = property === "v" && !instruction.fromUnion;
246
+ const optionalsLength = instruction.optionals.length;
247
+ try {
248
+ if (Kind in schema && schema[Kind] === "Cyclic") {
249
+ const call = handleCyclic(schema, property, instruction);
250
+ return isRoot ? `return ${call}` : call;
251
+ }
252
+ return mirrorNode(schema, property, instruction);
253
+ } catch (error) {
254
+ instruction.optionals.length = optionalsLength;
255
+ console.warn(
256
+ new Error(
257
+ "[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"
258
+ ),
259
+ error
260
+ );
261
+ return isRoot ? "return v" : property;
262
+ }
263
+ };
264
+ var mirrorNode = (schema, property, instruction) => {
265
+ const isRoot = property === "v" && !instruction.fromUnion;
266
+ if (instruction.cyclicDefs && Kind in schema && schema[Kind] === "Ref" && schema.$ref && schema.$ref in instruction.cyclicDefs.names) {
267
+ const call = `(${property}==null?${property}:${instruction.cyclicDefs.names[schema.$ref]}(${property}))`;
268
+ return isRoot ? `return ${call}` : call;
269
+ }
202
270
  if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
203
271
  return `return ${sanitize("v", instruction.sanitize?.length, schema)}`;
204
272
  if (instruction.recursion >= instruction.recursionLimit) return property;
@@ -212,6 +280,10 @@ var mirror = (schema, property, instruction) => {
212
280
  break;
213
281
  }
214
282
  schema = mergeObjectIntersection(schema);
283
+ if (!schema.properties) {
284
+ v = property;
285
+ break;
286
+ }
215
287
  v += "{";
216
288
  if (schema.additionalProperties) v += `...${property},`;
217
289
  const keys = Object.keys(schema.properties);
@@ -270,29 +342,38 @@ var mirror = (schema, property, instruction) => {
270
342
  v += "}";
271
343
  break;
272
344
  case "array":
345
+ if (!schema.items) {
346
+ v = property;
347
+ break;
348
+ }
273
349
  if (
274
350
  // @ts-expect-error
275
351
  schema.items.type !== "object" && // @ts-expect-error
276
352
  schema.items.type !== "array"
277
353
  ) {
354
+ 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
355
  if (Array.isArray(schema.items)) {
279
356
  v = handleTuple(schema.items, property, instruction);
280
357
  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;
358
+ } else if (!cyclicItems) {
359
+ if (isRoot && !Array.isArray(schema.items.anyOf))
360
+ return "return v";
361
+ else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
362
+ v = mirror(
363
+ deepClone(
364
+ instruction.definitions[schema.items.$ref]
365
+ ),
366
+ property,
367
+ {
368
+ ...instruction,
369
+ parentIsOptional: true,
370
+ recursion: instruction.recursion + 1
371
+ }
372
+ );
373
+ else if (!Array.isArray(schema.items.anyOf)) {
374
+ v = property;
375
+ break;
376
+ }
296
377
  }
297
378
  }
298
379
  const i = instruction.array;
@@ -357,6 +438,7 @@ var createMirror = (schema, {
357
438
  emit
358
439
  } = {}) => {
359
440
  const unions = [];
441
+ const cyclic = { groups: /* @__PURE__ */ new Map(), fns: [], count: 0 };
360
442
  if (typeof sanitize2 === "function") sanitize2 = [sanitize2];
361
443
  const f = mirror(schema, "v", {
362
444
  optionals: [],
@@ -369,21 +451,23 @@ var createMirror = (schema, {
369
451
  modules,
370
452
  // @ts-ignore private property
371
453
  definitions: definitions ?? modules?.$defs ?? {},
454
+ cyclic,
372
455
  sanitize: sanitize2,
373
456
  recursion: 0,
374
457
  recursionLimit,
375
458
  removeUnknownUnionType
376
459
  });
460
+ const fns = cyclic.fns.length ? cyclic.fns.join("\n") + "\n" : "";
377
461
  if (!unions.length && !sanitize2?.length) {
378
- if (emit) return { source: f, externals: void 0 };
379
- return Function("v", f);
462
+ if (emit) return { source: fns + f, externals: void 0 };
463
+ return Function("v", fns + f);
380
464
  }
381
465
  let hof;
382
466
  if (sanitize2?.length) {
383
467
  hof = {};
384
468
  for (let i = 0; i < sanitize2.length; i++) hof[`h${i}`] = sanitize2[i];
385
469
  }
386
- const source = `return function mirror(v){${f}}`;
470
+ const source = `${fns}return function mirror(v){${f}}`;
387
471
  if (emit)
388
472
  return {
389
473
  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
@@ -1,13 +1,14 @@
1
1
  // src/index.ts
2
2
  var Kind = "~kind";
3
3
  var Hint = "~hint";
4
- var isSpecialProperty = (name) => /(\ |-|\t|\n|\.|\[|\]|\{|\})/.test(name) || !isNaN(+name[0]);
4
+ var isSpecialProperty = (name) => !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
5
5
  var joinProperty = (v1, v2, isOptional = false) => {
6
6
  if (typeof v2 === "number") return `${v1}[${v2}]`;
7
- if (isSpecialProperty(v2)) return `${v1}${isOptional ? "?." : ""}["${v2}"]`;
7
+ if (isSpecialProperty(v2))
8
+ return `${v1}${isOptional ? "?." : ""}[${JSON.stringify(v2)}]`;
8
9
  return `${v1}${isOptional ? "?" : ""}.${v2}`;
9
10
  };
10
- var encodeProperty = (v) => isSpecialProperty(v) ? `"${v}"` : v;
11
+ var encodeProperty = (v) => isSpecialProperty(v) ? JSON.stringify(v) : v;
11
12
  var sanitize = (key, sanitize2 = 0, schema) => {
12
13
  if (schema.type !== "string" || schema.const || schema.trusted) return key;
13
14
  let hof = "";
@@ -53,7 +54,7 @@ var handleRecord = (schema, property, instruction) => {
53
54
  var handleTuple = (schema, property, instruction) => {
54
55
  const i = instruction.array;
55
56
  instruction.array++;
56
- const isRoot = property === "v" && !instruction.unions.length;
57
+ const isRoot = property === "v" && !instruction.fromUnion;
57
58
  let v = "";
58
59
  if (!isRoot) v = `(()=>{`;
59
60
  v += `const ar${i}v=[`;
@@ -95,6 +96,59 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
95
96
  }
96
97
  return source;
97
98
  }
99
+ var handleCyclic = (schema, property, instruction) => {
100
+ const defs = schema.$defs;
101
+ let group = instruction.cyclic.groups.get(defs);
102
+ if (!group) {
103
+ group = { defs, names: {} };
104
+ instruction.cyclic.groups.set(defs, group);
105
+ for (const name in defs)
106
+ group.names[name] = `cy${instruction.cyclic.count++}`;
107
+ for (const name in defs)
108
+ instruction.cyclic.fns.push(
109
+ `function ${group.names[name]}(v){${mirror(defs[name], "v", {
110
+ ...instruction,
111
+ cyclicDefs: group,
112
+ fromUnion: false,
113
+ parentIsOptional: false,
114
+ optionals: [],
115
+ optionalsInArray: [],
116
+ unionKeys: {},
117
+ array: 0,
118
+ recursion: 0
119
+ })}}`
120
+ );
121
+ }
122
+ const fn = group.names[schema.$ref];
123
+ if (!fn)
124
+ throw new Error(
125
+ `[exact-mirror] cyclic reference "${schema.$ref}" is not found in $defs`
126
+ );
127
+ return `(${property}==null?${property}:${fn}(${property}))`;
128
+ };
129
+ var withDefs = (type, group) => {
130
+ if (Kind in type) {
131
+ if (type[Kind] === "Cyclic") return type;
132
+ if (type[Kind] === "Ref" && type.$ref in group.defs)
133
+ return Object.defineProperty(
134
+ { $defs: group.defs, $ref: type.$ref },
135
+ Kind,
136
+ { value: "Cyclic" }
137
+ );
138
+ }
139
+ let entry = "~check";
140
+ while (entry in group.defs) entry += "~";
141
+ const def = Object.create(
142
+ Object.getPrototypeOf(type),
143
+ Object.getOwnPropertyDescriptors(type)
144
+ );
145
+ def.$id = entry;
146
+ return Object.defineProperty(
147
+ { $defs: { ...group.defs, [entry]: def }, $ref: entry },
148
+ Kind,
149
+ { value: "Cyclic" }
150
+ );
151
+ };
98
152
  var handleUnion = (schemas, property, instruction) => {
99
153
  if (instruction.Compile === void 0) {
100
154
  if (!instruction.typeCompilerWanred) {
@@ -116,15 +170,6 @@ var handleUnion = (schemas, property, instruction) => {
116
170
  if (!(Kind in type) || !type.$ref) return type;
117
171
  if (type[Kind] === "This")
118
172
  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
173
  return type;
129
174
  };
130
175
  let cleanThenCheck = "";
@@ -139,7 +184,11 @@ var handleUnion = (schemas, property, instruction) => {
139
184
  type.items[i2] = unwrapRef(type.items[i2]);
140
185
  else type.items = unwrapRef(type.items);
141
186
  }
142
- typeChecks.push(instruction.Compile(type));
187
+ typeChecks.push(
188
+ instruction.Compile(
189
+ instruction.cyclicDefs ? withDefs(type, instruction.cyclicDefs) : type
190
+ )
191
+ );
143
192
  v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(
144
193
  type,
145
194
  property,
@@ -166,12 +215,31 @@ if(d.unions[${ui}][${i}].Check(tmp))return tmp
166
215
  };
167
216
  var mirror = (schema, property, instruction) => {
168
217
  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
- });
218
+ const isRoot = property === "v" && !instruction.fromUnion;
219
+ const optionalsLength = instruction.optionals.length;
220
+ try {
221
+ if (Kind in schema && schema[Kind] === "Cyclic") {
222
+ const call = handleCyclic(schema, property, instruction);
223
+ return isRoot ? `return ${call}` : call;
224
+ }
225
+ return mirrorNode(schema, property, instruction);
226
+ } catch (error) {
227
+ instruction.optionals.length = optionalsLength;
228
+ console.warn(
229
+ new Error(
230
+ "[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"
231
+ ),
232
+ error
233
+ );
234
+ return isRoot ? "return v" : property;
235
+ }
236
+ };
237
+ var mirrorNode = (schema, property, instruction) => {
238
+ const isRoot = property === "v" && !instruction.fromUnion;
239
+ if (instruction.cyclicDefs && Kind in schema && schema[Kind] === "Ref" && schema.$ref && schema.$ref in instruction.cyclicDefs.names) {
240
+ const call = `(${property}==null?${property}:${instruction.cyclicDefs.names[schema.$ref]}(${property}))`;
241
+ return isRoot ? `return ${call}` : call;
242
+ }
175
243
  if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
176
244
  return `return ${sanitize("v", instruction.sanitize?.length, schema)}`;
177
245
  if (instruction.recursion >= instruction.recursionLimit) return property;
@@ -185,6 +253,10 @@ var mirror = (schema, property, instruction) => {
185
253
  break;
186
254
  }
187
255
  schema = mergeObjectIntersection(schema);
256
+ if (!schema.properties) {
257
+ v = property;
258
+ break;
259
+ }
188
260
  v += "{";
189
261
  if (schema.additionalProperties) v += `...${property},`;
190
262
  const keys = Object.keys(schema.properties);
@@ -243,29 +315,38 @@ var mirror = (schema, property, instruction) => {
243
315
  v += "}";
244
316
  break;
245
317
  case "array":
318
+ if (!schema.items) {
319
+ v = property;
320
+ break;
321
+ }
246
322
  if (
247
323
  // @ts-expect-error
248
324
  schema.items.type !== "object" && // @ts-expect-error
249
325
  schema.items.type !== "array"
250
326
  ) {
327
+ 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
328
  if (Array.isArray(schema.items)) {
252
329
  v = handleTuple(schema.items, property, instruction);
253
330
  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;
331
+ } else if (!cyclicItems) {
332
+ if (isRoot && !Array.isArray(schema.items.anyOf))
333
+ return "return v";
334
+ else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This"))
335
+ v = mirror(
336
+ deepClone(
337
+ instruction.definitions[schema.items.$ref]
338
+ ),
339
+ property,
340
+ {
341
+ ...instruction,
342
+ parentIsOptional: true,
343
+ recursion: instruction.recursion + 1
344
+ }
345
+ );
346
+ else if (!Array.isArray(schema.items.anyOf)) {
347
+ v = property;
348
+ break;
349
+ }
269
350
  }
270
351
  }
271
352
  const i = instruction.array;
@@ -330,6 +411,7 @@ var createMirror = (schema, {
330
411
  emit
331
412
  } = {}) => {
332
413
  const unions = [];
414
+ const cyclic = { groups: /* @__PURE__ */ new Map(), fns: [], count: 0 };
333
415
  if (typeof sanitize2 === "function") sanitize2 = [sanitize2];
334
416
  const f = mirror(schema, "v", {
335
417
  optionals: [],
@@ -342,21 +424,23 @@ var createMirror = (schema, {
342
424
  modules,
343
425
  // @ts-ignore private property
344
426
  definitions: definitions ?? modules?.$defs ?? {},
427
+ cyclic,
345
428
  sanitize: sanitize2,
346
429
  recursion: 0,
347
430
  recursionLimit,
348
431
  removeUnknownUnionType
349
432
  });
433
+ const fns = cyclic.fns.length ? cyclic.fns.join("\n") + "\n" : "";
350
434
  if (!unions.length && !sanitize2?.length) {
351
- if (emit) return { source: f, externals: void 0 };
352
- return Function("v", f);
435
+ if (emit) return { source: fns + f, externals: void 0 };
436
+ return Function("v", fns + f);
353
437
  }
354
438
  let hof;
355
439
  if (sanitize2?.length) {
356
440
  hof = {};
357
441
  for (let i = 0; i < sanitize2.length; i++) hof[`h${i}`] = sanitize2[i];
358
442
  }
359
- const source = `return function mirror(v){${f}}`;
443
+ const source = `${fns}return function mirror(v){${f}}`;
360
444
  if (emit)
361
445
  return {
362
446
  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.1",
4
4
  "description": "Mirror exact value to TypeBox/OpenAPI model",
5
5
  "license": "MIT",
6
6
  "scripts": {