exact-mirror 1.1.1 → 1.2.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/README.md CHANGED
@@ -50,3 +50,34 @@ const mirror = createMirror(shape)
50
50
 
51
51
  console.log(mirror(value)) // {"id":0,"name":"saltyaom"}
52
52
  ```
53
+
54
+ ## Decode / Encode
55
+
56
+ By default `createMirror` only **cleans** a value to the model's shape. Opt into `decode` or `encode` to also apply a TypeBox codec's transform at codec leaves during the same walk — a fast replacement for `Value.Decode` / `Value.Encode` (~350x faster on a nested + array schema).
57
+
58
+ ```typescript
59
+ import { Type as t } from 'typebox'
60
+ import { Compile } from 'typebox/compile'
61
+ import { createMirror } from 'exact-mirror'
62
+
63
+ const Numeric = t.Union([
64
+ t.Number(),
65
+ t
66
+ .Codec(t.String())
67
+ .Decode((v) => +v)
68
+ .Encode((v) => '' + v)
69
+ ])
70
+
71
+ const shape = t.Object({ id: Numeric })
72
+
73
+ const decode = createMirror(shape, { decode: true, Compile })
74
+
75
+ decode({ id: '2' }) // { id: 2 } — parsed + cleaned in one pass
76
+ decode({ id: 2 }) // { id: 2 } — already-numeric branch, untouched
77
+ ```
78
+
79
+ - `decode: true` applies each codec's `~codec.decode` (parse input).
80
+ - `encode: true` applies each codec's `~codec.encode` (the reverse).
81
+ - Both default **off** — output is byte-identical to the pure clean.
82
+
83
+ > Decode/encode is a pure transform: the value is assumed to have already passed `Check`. Validate the input first, then mirror.
package/dist/index.d.ts CHANGED
@@ -1,103 +1,145 @@
1
- import type { TSchema, TModule, Static } from 'typebox';
2
- import type { Compile, Validator } from 'typebox/compile';
1
+ import { Static, TModule, TSchema } from "typebox";
2
+ import { Compile, Validator } from "typebox/compile";
3
+
4
+ //#region src/index.d.ts
3
5
  interface BaseSchema {
4
- '~kind': string;
5
- id?: string;
6
- $id?: string;
7
- type?: string;
8
- $schema?: string;
9
- const?: unknown[];
10
- pattern?: string;
11
- additionalItems?: boolean | AnySchema;
12
- items?: AnySchema | AnySchema[];
13
- required?: string[];
14
- additionalProperties?: boolean | AnySchema;
15
- definitions?: {
16
- [name: string]: AnySchema;
17
- };
18
- properties?: {
19
- [name: string]: AnySchema;
20
- };
21
- patternProperties?: {
22
- [name: string]: AnySchema;
23
- };
24
- dependencies?: {
25
- [name: string]: AnySchema | string[];
26
- };
27
- enum?: any[];
28
- allOf?: AnySchema[];
29
- anyOf?: AnySchema[];
30
- oneOf?: AnySchema[];
31
- not?: AnySchema;
32
- $ref?: string;
33
- $defs?: Record<string, AnySchema>;
6
+ '~kind': string;
7
+ id?: string;
8
+ $id?: string;
9
+ type?: string;
10
+ $schema?: string;
11
+ const?: unknown[];
12
+ pattern?: string;
13
+ additionalItems?: boolean | AnySchema;
14
+ items?: AnySchema | AnySchema[];
15
+ required?: string[];
16
+ additionalProperties?: boolean | AnySchema;
17
+ definitions?: {
18
+ [name: string]: AnySchema;
19
+ };
20
+ properties?: {
21
+ [name: string]: AnySchema;
22
+ };
23
+ patternProperties?: {
24
+ [name: string]: AnySchema;
25
+ };
26
+ dependencies?: {
27
+ [name: string]: AnySchema | string[];
28
+ };
29
+ enum?: any[];
30
+ allOf?: AnySchema[];
31
+ anyOf?: AnySchema[];
32
+ oneOf?: AnySchema[];
33
+ not?: AnySchema;
34
+ $ref?: string;
35
+ $defs?: Record<string, AnySchema>;
34
36
  }
35
37
  type AnySchema = TSchema & BaseSchema;
36
- export declare const mergeObjectIntersection: (schema: AnySchema) => AnySchema;
38
+ declare const mergeObjectIntersection: (schema: AnySchema) => AnySchema;
37
39
  type MaybeArray<T> = T | T[];
38
- export interface Instruction<Emit extends boolean = false> {
39
- optionals: string[];
40
- optionalsInArray: string[][];
41
- parentIsOptional: boolean;
42
- array: number;
43
- unions: Validator<any>[][];
44
- unionKeys: Record<string, 1>;
45
- sanitize: MaybeArray<(v: string) => string> | undefined;
46
- fromUnion?: boolean;
47
- emit?: Emit;
48
- /**
49
- * TypeCompiler is required when using Union
50
- *
51
- * Left as opt-in to reduce bundle size
52
- * many end-user doesn't use Union
53
- *
54
- * @default undefined
55
- */
56
- Compile?: typeof Compile;
57
- typeCompilerWanred?: boolean;
58
- modules?: TModule<{}>;
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;
70
- recursion: number;
71
- /**
72
- * @default 8
73
- */
74
- recursionLimit: number;
75
- /**
76
- * If incorrect type is passed to Union value, should it be removed?
77
- *
78
- * If you check a value later, it's recommended to set this to `false`
79
- * otherwise, set this to true
80
- *
81
- * @default false
82
- */
83
- removeUnknownUnionType: boolean;
40
+ interface Instruction<Emit extends boolean = false> {
41
+ optionals: string[];
42
+ optionalsInArray: string[][];
43
+ parentIsOptional: boolean;
44
+ array: number;
45
+ unions: Validator<any>[][];
46
+ unionKeys: Record<string, 1>;
47
+ sanitize: MaybeArray<(v: string) => string> | undefined;
48
+ fromUnion?: boolean;
49
+ emit?: Emit;
50
+ /**
51
+ * Apply a TypeBox codec's `~codec` transform at codec leaves during the
52
+ * mirror walk, instead of only cleaning the value.
53
+ *
54
+ * @default undefined pure clean, no transform
55
+ */
56
+ transform?: 'decode' | 'encode';
57
+ /**
58
+ * Codec transform functions collected during codegen, referenced from the
59
+ * generated source by index as `d.codecs[i]`
60
+ */
61
+ codecs: Function[];
62
+ /**
63
+ * TypeCompiler is required when using Union
64
+ *
65
+ * Left as opt-in to reduce bundle size
66
+ * many end-user doesn't use Union
67
+ *
68
+ * @default undefined
69
+ */
70
+ Compile?: typeof Compile;
71
+ typeCompilerWanred?: boolean;
72
+ modules?: TModule<{}>;
73
+ definitions: Record<string, AnySchema>;
74
+ /**
75
+ * Shared cyclic codegen state: generated per-definition mirror
76
+ * functions, grouped by `$defs` object identity
77
+ */
78
+ cyclic: CyclicContext;
79
+ /**
80
+ * `$defs` group the current cyclic definition body is generated
81
+ * against, used to resolve `Ref` nodes to function calls
82
+ */
83
+ cyclicDefs?: CyclicGroup;
84
+ recursion: number;
85
+ /**
86
+ * @default 8
87
+ */
88
+ recursionLimit: number;
89
+ /**
90
+ * If incorrect type is passed to Union value, should it be removed?
91
+ *
92
+ * If you check a value later, it's recommended to set this to `false`
93
+ * otherwise, set this to true
94
+ *
95
+ * @default false
96
+ */
97
+ removeUnknownUnionType: boolean;
84
98
  }
85
- export declare function deepClone<T>(source: T, weak?: WeakMap<object, any>): T;
99
+ declare function deepClone<T>(source: T, weak?: WeakMap<object, any>): T;
86
100
  interface CyclicGroup {
87
- defs: Record<string, AnySchema>;
88
- names: Record<string, string>;
101
+ defs: Record<string, AnySchema>;
102
+ names: Record<string, string>;
89
103
  }
90
104
  interface CyclicContext {
91
- groups: Map<object, CyclicGroup>;
92
- fns: string[];
93
- count: number;
105
+ groups: Map<object, CyclicGroup>;
106
+ fns: string[];
107
+ count: number;
94
108
  }
95
- export interface Manifest {
96
- source: string;
97
- externals: {
98
- unions: Validator<any, TSchema, unknown, unknown>[][];
99
- hof?: Record<string, Function>;
100
- };
109
+ interface Manifest {
110
+ source: string;
111
+ externals: {
112
+ unions: Validator<any, TSchema, unknown, unknown>[][];
113
+ codecs?: Function[];
114
+ hof?: Record<string, Function>;
115
+ };
101
116
  }
102
- export declare const createMirror: <T extends TSchema, Emit extends boolean = false>(schema: T, { Compile, modules, definitions, sanitize, recursionLimit, removeUnknownUnionType, emit }?: Partial<Pick<Instruction<Emit>, "Compile" | "definitions" | "sanitize" | "modules" | "recursionLimit" | "removeUnknownUnionType" | "emit">>) => Emit extends true ? Manifest : (v: Static<T>) => Static<T>;
103
- export default createMirror;
117
+ declare const createMirror: <T extends TSchema, Emit extends boolean = false>(schema: T, {
118
+ Compile,
119
+ modules,
120
+ definitions,
121
+ sanitize,
122
+ recursionLimit,
123
+ removeUnknownUnionType,
124
+ emit,
125
+ decode,
126
+ encode
127
+ }?: Partial<Pick<Instruction<Emit>, "Compile" | "definitions" | "sanitize" | "modules" | "recursionLimit" | "removeUnknownUnionType" | "emit">> & {
128
+ /**
129
+ * Apply each codec's `~codec.decode` at codec leaves (parse input,
130
+ * e.g. numeric string → number) on top of the clean walk
131
+ *
132
+ * The value is assumed to have already passed `Check`
133
+ *
134
+ * @default false
135
+ */
136
+ decode?: boolean;
137
+ /**
138
+ * Apply each codec's `~codec.encode` at codec leaves
139
+ *
140
+ * @default false
141
+ */
142
+ encode?: boolean;
143
+ }) => Emit extends true ? Manifest : (v: Static<T>) => Static<T>;
144
+ //#endregion
145
+ export { Instruction, Manifest, createMirror, createMirror as default, deepClone, mergeObjectIntersection };
package/dist/index.js ADDED
@@ -0,0 +1,367 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+
3
+ //#region src/index.ts
4
+ const Kind = "~kind";
5
+ const Hint = "~hint";
6
+ const Codec = "~codec";
7
+ const isSpecialProperty = (name) => !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
8
+ const joinProperty = (v1, v2, isOptional = false) => {
9
+ if (typeof v2 === "number") return `${v1}[${v2}]`;
10
+ if (isSpecialProperty(v2)) return `${v1}${isOptional ? "?." : ""}[${JSON.stringify(v2)}]`;
11
+ return `${v1}${isOptional ? "?" : ""}.${v2}`;
12
+ };
13
+ const encodeProperty = (v) => isSpecialProperty(v) ? JSON.stringify(v) : v;
14
+ const sanitize = (key, sanitize = 0, schema) => {
15
+ if (schema.type !== "string" || schema.const || schema.trusted) return key;
16
+ let hof = "";
17
+ for (let i = sanitize - 1; i >= 0; i--) hof += `d.h${i}(`;
18
+ return hof + key + ")".repeat(sanitize);
19
+ };
20
+ const mergeObjectIntersection = (schema) => {
21
+ if (!schema.allOf || Kind in schema && (schema[Kind] !== "Intersect" || schema.type !== "object")) return schema;
22
+ const { allOf, ...newSchema } = schema;
23
+ newSchema.properties = {};
24
+ if (Kind in newSchema) newSchema[Kind] = "Object";
25
+ for (const type of allOf) {
26
+ if (type.type !== "object") continue;
27
+ const { properties, required, type: _, [Kind]: __, ...rest } = type;
28
+ if (required) newSchema.required = newSchema.required ? newSchema.required.concat(required) : required;
29
+ Object.assign(newSchema, rest);
30
+ for (const property in type.properties) newSchema.properties[property] = mergeObjectIntersection(type.properties[property]);
31
+ }
32
+ return newSchema;
33
+ };
34
+ const handleRecord = (schema, property, instruction) => {
35
+ const child = schema.patternProperties["^(.*)$"] ?? schema.patternProperties[Object.keys(schema.patternProperties)[0]];
36
+ if (!child) return property;
37
+ const i = instruction.array;
38
+ instruction.array++;
39
+ let v = `(()=>{const ar${i}s=Object.keys(${property}),ar${i}v={};for(let i=0;i<ar${i}s.length;i++){const ar${i}p=${property}[ar${i}s[i]];ar${i}v[ar${i}s[i]]=${mirror(child, `ar${i}p`, instruction)}`;
40
+ const optionals = instruction.optionalsInArray[i + 1];
41
+ if (optionals) {
42
+ for (let oi = 0; oi < optionals.length; oi++) {
43
+ const target = `ar${i}v[ar${i}s[i]]${optionals[oi]}`;
44
+ v += `;if(${target}===undefined)delete ${target}`;
45
+ }
46
+ instruction.optionalsInArray[i + 1] = [];
47
+ }
48
+ v += `}return ar${i}v})()`;
49
+ return v;
50
+ };
51
+ const handleTuple = (schema, property, instruction) => {
52
+ const i = instruction.array;
53
+ instruction.array++;
54
+ const isRoot = property === "v" && !instruction.fromUnion;
55
+ let v = "";
56
+ if (!isRoot) v = `(()=>{`;
57
+ v += `const ar${i}v=[`;
58
+ for (let i = 0; i < schema.length; i++) {
59
+ if (i !== 0) v += ",";
60
+ v += mirror(schema[i], joinProperty(property, i, instruction.parentIsOptional || instruction.fromUnion), instruction);
61
+ }
62
+ v += `];`;
63
+ if (!isRoot) v += `return ar${i}v})()`;
64
+ return v;
65
+ };
66
+ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
67
+ if (source === null || typeof source !== "object" || typeof source === "function") return source;
68
+ if (weak.has(source)) return weak.get(source);
69
+ if (Array.isArray(source)) {
70
+ const copy = new Array(source.length);
71
+ weak.set(source, copy);
72
+ for (let i = 0; i < source.length; i++) copy[i] = deepClone(source[i], weak);
73
+ return copy;
74
+ }
75
+ const keys = Object.keys(source).concat(Object.getOwnPropertySymbols(source));
76
+ const cloned = Object.create(null);
77
+ weak.set(source, cloned);
78
+ for (const key of keys) cloned[key] = deepClone(source[key], weak);
79
+ return cloned;
80
+ }
81
+ const handleCyclic = (schema, property, instruction) => {
82
+ const defs = schema.$defs;
83
+ let group = instruction.cyclic.groups.get(defs);
84
+ if (!group) {
85
+ group = {
86
+ defs,
87
+ names: {}
88
+ };
89
+ instruction.cyclic.groups.set(defs, group);
90
+ for (const name in defs) group.names[name] = `cy${instruction.cyclic.count++}`;
91
+ for (const name in defs) instruction.cyclic.fns.push(`function ${group.names[name]}(v){${mirror(defs[name], "v", {
92
+ ...instruction,
93
+ cyclicDefs: group,
94
+ fromUnion: false,
95
+ parentIsOptional: false,
96
+ optionals: [],
97
+ optionalsInArray: [],
98
+ unionKeys: {},
99
+ array: 0,
100
+ recursion: 0
101
+ })}}`);
102
+ }
103
+ const fn = group.names[schema.$ref];
104
+ if (!fn) throw new Error(`[exact-mirror] cyclic reference "${schema.$ref}" is not found in $defs`);
105
+ return `(${property}==null?${property}:${fn}(${property}))`;
106
+ };
107
+ const withDefs = (type, group) => {
108
+ if (Kind in type) {
109
+ if (type[Kind] === "Cyclic") return type;
110
+ if (type[Kind] === "Ref" && type.$ref in group.defs) return Object.defineProperty({
111
+ $defs: group.defs,
112
+ $ref: type.$ref
113
+ }, Kind, { value: "Cyclic" });
114
+ }
115
+ let entry = "~check";
116
+ while (entry in group.defs) entry += "~";
117
+ const def = Object.create(Object.getPrototypeOf(type), Object.getOwnPropertyDescriptors(type));
118
+ def.$id = entry;
119
+ return Object.defineProperty({
120
+ $defs: {
121
+ ...group.defs,
122
+ [entry]: def
123
+ },
124
+ $ref: entry
125
+ }, Kind, { value: "Cyclic" });
126
+ };
127
+ const handleUnion = (schemas, property, instruction) => {
128
+ if (instruction.Compile === void 0) {
129
+ if (!instruction.typeCompilerWanred) {
130
+ console.warn(/* @__PURE__ */ new Error("[exact-mirror] TypeBox's TypeCompiler is required to use Union"));
131
+ instruction.typeCompilerWanred = true;
132
+ }
133
+ return property;
134
+ }
135
+ instruction.unionKeys[property] = 1;
136
+ const ui = instruction.unions.length;
137
+ const typeChecks = instruction.unions[ui] = [];
138
+ let v = `(()=>{\n`;
139
+ const unwrapRef = (type) => {
140
+ if (!(Kind in type) || !type.$ref) return type;
141
+ if (type[Kind] === "This") return deepClone(instruction.definitions[type.$ref]);
142
+ return type;
143
+ };
144
+ let cleanThenCheck = "";
145
+ for (let i = 0; i < schemas.length; i++) {
146
+ let type = unwrapRef(schemas[i]);
147
+ if (Array.isArray(type.anyOf)) for (let i = 0; i < type.anyOf.length; i++) type.anyOf[i] = unwrapRef(type.anyOf[i]);
148
+ else if (type.items) if (Array.isArray(type.items)) for (let i = 0; i < type.items.length; i++) type.items[i] = unwrapRef(type.items[i]);
149
+ else type.items = unwrapRef(type.items);
150
+ typeChecks.push(instruction.Compile(instruction.cyclicDefs ? withDefs(type, instruction.cyclicDefs) : type));
151
+ v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(type, property, {
152
+ ...instruction,
153
+ recursion: instruction.recursion + 1,
154
+ parentIsOptional: true,
155
+ fromUnion: true
156
+ })}}\n`;
157
+ cleanThenCheck += (i ? "" : "let ") + "tmp=" + mirror(type, property, {
158
+ ...instruction,
159
+ recursion: instruction.recursion + 1,
160
+ parentIsOptional: true,
161
+ fromUnion: true
162
+ }) + `\nif(d.unions[${ui}][${i}].Check(tmp))return tmp\n`;
163
+ }
164
+ if (cleanThenCheck) v += cleanThenCheck;
165
+ v += `return ${instruction.removeUnknownUnionType ? "undefined" : property}`;
166
+ return v + `})()`;
167
+ };
168
+ const mirror = (schema, property, instruction) => {
169
+ if (!schema) return "";
170
+ const isRoot = property === "v" && !instruction.fromUnion;
171
+ const optionalsLength = instruction.optionals.length;
172
+ try {
173
+ if (instruction.transform && Codec in schema) {
174
+ const codec = schema[Codec][instruction.transform];
175
+ let ci = instruction.codecs.indexOf(codec);
176
+ if (ci === -1) ci = instruction.codecs.push(codec) - 1;
177
+ const body = mirrorNode(schema, `d.codecs[${ci}](${property})`, instruction);
178
+ return isRoot ? `return ${body}` : body;
179
+ }
180
+ if (Kind in schema && schema[Kind] === "Cyclic") {
181
+ const call = handleCyclic(schema, property, instruction);
182
+ return isRoot ? `return ${call}` : call;
183
+ }
184
+ return mirrorNode(schema, property, instruction);
185
+ } catch (error) {
186
+ instruction.optionals.length = optionalsLength;
187
+ console.warn(/* @__PURE__ */ new Error("[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"), error);
188
+ return isRoot ? "return v" : property;
189
+ }
190
+ };
191
+ const mirrorNode = (schema, property, instruction) => {
192
+ const isRoot = property === "v" && !instruction.fromUnion;
193
+ if (instruction.cyclicDefs && Kind in schema && schema[Kind] === "Ref" && schema.$ref && schema.$ref in instruction.cyclicDefs.names) {
194
+ const call = `(${property}==null?${property}:${instruction.cyclicDefs.names[schema.$ref]}(${property}))`;
195
+ return isRoot ? `return ${call}` : call;
196
+ }
197
+ if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf) return `return ${sanitize("v", instruction.sanitize?.length, schema)}`;
198
+ if (instruction.recursion >= instruction.recursionLimit) return property;
199
+ let v = "";
200
+ if (schema.$id && Hint in schema) instruction.definitions[schema.$id] = schema;
201
+ switch (schema.type) {
202
+ case "object":
203
+ if (schema[Kind] === "Record") {
204
+ v = handleRecord(schema, property, instruction);
205
+ break;
206
+ }
207
+ schema = mergeObjectIntersection(schema);
208
+ if (!schema.properties) {
209
+ v = property;
210
+ break;
211
+ }
212
+ v += "{";
213
+ if (schema.additionalProperties) v += `...${property},`;
214
+ const keys = Object.keys(schema.properties);
215
+ for (let i = 0; i < keys.length; i++) {
216
+ const key = keys[i];
217
+ let isOptional = !schema.required || schema.required && !schema.required.includes(key) || Array.isArray(schema.properties[key].anyOf);
218
+ const name = joinProperty(property, key, instruction.parentIsOptional || instruction.fromUnion);
219
+ if (isOptional) {
220
+ const index = instruction.array;
221
+ if (property.startsWith("ar")) {
222
+ const dotIndex = name.indexOf(".");
223
+ let refName;
224
+ if (dotIndex >= 0) refName = name.slice(dotIndex);
225
+ else refName = name.slice(property.length);
226
+ const array = instruction.optionalsInArray;
227
+ if (array[index]) array[index].push(refName);
228
+ else array[index] = [refName];
229
+ } else instruction.optionals.push(name);
230
+ }
231
+ const child = schema.properties[key];
232
+ if (i !== 0) v += ",";
233
+ v += `${encodeProperty(key)}:${isOptional ? `${name}===undefined?undefined:` : ""}${mirror(child, name, {
234
+ ...instruction,
235
+ recursion: instruction.recursion + 1,
236
+ parentIsOptional: isOptional
237
+ })}`;
238
+ }
239
+ v += "}";
240
+ break;
241
+ case "array":
242
+ if (!schema.items) {
243
+ v = property;
244
+ break;
245
+ }
246
+ if (schema.items.type !== "object" && schema.items.type !== "array") {
247
+ 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;
248
+ const codecItems = instruction.transform !== void 0 && !Array.isArray(schema.items) && Codec in schema.items;
249
+ if (Array.isArray(schema.items)) {
250
+ v = handleTuple(schema.items, property, instruction);
251
+ break;
252
+ } else if (!cyclicItems && !codecItems) {
253
+ if (isRoot && !Array.isArray(schema.items.anyOf)) return "return v";
254
+ else if (Kind in schema.items && schema.items.$ref && (schema.items[Kind] === "Ref" || schema.items[Kind] === "This")) v = mirror(deepClone(instruction.definitions[schema.items.$ref]), property, {
255
+ ...instruction,
256
+ parentIsOptional: true,
257
+ recursion: instruction.recursion + 1
258
+ });
259
+ else if (!Array.isArray(schema.items.anyOf)) {
260
+ v = property;
261
+ break;
262
+ }
263
+ }
264
+ }
265
+ const i = instruction.array;
266
+ instruction.array++;
267
+ let reference = property;
268
+ if (isRoot) v = `const ar${i}v=new Array(${property}.length);`;
269
+ else {
270
+ reference = `ar${i}s`;
271
+ v = `((${reference})=>{const ar${i}v=new Array(${reference}.length);`;
272
+ }
273
+ v += `for(let i=0;i<${reference}.length;i++){const ar${i}p=${reference}[i];ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)}`;
274
+ const optionals = instruction.optionalsInArray[i + 1];
275
+ if (optionals) {
276
+ for (let oi = 0; oi < optionals.length; oi++) {
277
+ const target = `ar${i}v[i]${optionals[oi]}`;
278
+ v += `;if(${target}===undefined)delete ${target}`;
279
+ }
280
+ instruction.optionalsInArray[i + 1] = [];
281
+ }
282
+ v += `}`;
283
+ if (!isRoot) v += `return ar${i}v})(${property})`;
284
+ break;
285
+ default:
286
+ if (schema.$ref && schema.$ref in instruction.definitions) return mirror(instruction.definitions[schema.$ref], property, instruction);
287
+ if (Array.isArray(schema.anyOf)) {
288
+ v = handleUnion(schema.anyOf, property, instruction);
289
+ break;
290
+ }
291
+ v = sanitize(property, instruction.sanitize?.length, schema);
292
+ break;
293
+ }
294
+ if (!isRoot) return v;
295
+ if (schema.type === "array") v = `${v}const x=ar0v;`;
296
+ else v = `const x=${v}\n`;
297
+ for (let i = 0; i < instruction.optionals.length; i++) {
298
+ const key = instruction.optionals[i];
299
+ const prop = key.slice(1);
300
+ v += `if(${key}===undefined`;
301
+ if (instruction.unionKeys[key]) v += `||x${prop}===undefined`;
302
+ const shouldQuestion = prop.charCodeAt(0) !== 63 && schema.type !== "array";
303
+ v += `)delete x${shouldQuestion ? prop.charCodeAt(0) === 91 ? "?." : "?" : ""}${prop}\n`;
304
+ }
305
+ return `${v}return x`;
306
+ };
307
+ const createMirror = (schema, { Compile, modules, definitions, sanitize, recursionLimit = 8, removeUnknownUnionType = false, emit, decode, encode } = Object.create(null)) => {
308
+ const unions = [];
309
+ const codecs = [];
310
+ const cyclic = {
311
+ groups: /* @__PURE__ */ new Map(),
312
+ fns: [],
313
+ count: 0
314
+ };
315
+ if (typeof sanitize === "function") sanitize = [sanitize];
316
+ const f = mirror(schema, "v", {
317
+ optionals: [],
318
+ optionalsInArray: [],
319
+ array: 0,
320
+ parentIsOptional: false,
321
+ unions,
322
+ unionKeys: Object.create(null),
323
+ Compile,
324
+ modules,
325
+ definitions: definitions ?? modules?.$defs ?? Object.create(null),
326
+ cyclic,
327
+ sanitize,
328
+ recursion: 0,
329
+ recursionLimit,
330
+ removeUnknownUnionType,
331
+ transform: decode ? "decode" : encode ? "encode" : void 0,
332
+ codecs
333
+ });
334
+ const fns = cyclic.fns.length ? cyclic.fns.join("\n") + "\n" : "";
335
+ if (!unions.length && !sanitize?.length && !codecs.length) {
336
+ if (emit) return {
337
+ source: fns + f,
338
+ externals: void 0
339
+ };
340
+ return Function("v", fns + f);
341
+ }
342
+ let hof;
343
+ if (sanitize?.length) {
344
+ hof = Object.create(null);
345
+ for (let i = 0; i < sanitize.length; i++) hof[`h${i}`] = sanitize[i];
346
+ }
347
+ const source = `${fns}return function mirror(v){${f}}`;
348
+ if (emit) return {
349
+ source,
350
+ externals: {
351
+ unions,
352
+ codecs: codecs.length ? codecs : void 0,
353
+ hof
354
+ }
355
+ };
356
+ const d = Object.create(null);
357
+ if (unions.length) d.unions = unions;
358
+ if (codecs.length) d.codecs = codecs;
359
+ if (hof) Object.assign(d, hof);
360
+ return Function("d", source)(d);
361
+ };
362
+
363
+ //#endregion
364
+ exports.createMirror = createMirror;
365
+ exports.default = createMirror;
366
+ exports.deepClone = deepClone;
367
+ exports.mergeObjectIntersection = mergeObjectIntersection;