exact-mirror 0.0.0 → 0.0.2

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
@@ -4,6 +4,18 @@ Enforce value to TypeBox/OpenAPI model
4
4
 
5
5
  By providing model ahead of time, the library will generate a function to mirror a value to an exact type
6
6
 
7
+ ```
8
+ $ bun benchmarks/small
9
+
10
+ clk: ~3.13 GHz
11
+ cpu: Apple M1 Max
12
+ runtime: bun 1.2.4 (arm64-darwin)
13
+
14
+ summary
15
+ Exact Mirror
16
+ 556.23x faster than TypeBox Value.Clean
17
+ ```
18
+
7
19
  ## Installation
8
20
 
9
21
  ```bash
@@ -1,4 +1,23 @@
1
+ import { TypeCompiler, type TypeCheck } from '@sinclair/typebox/compiler';
1
2
  import type { TAnySchema } from '@sinclair/typebox';
2
3
  export declare const mergeObjectIntersection: (schema: TAnySchema) => TAnySchema;
3
- export declare const createMirror: <T extends TAnySchema>(schema: T) => ((v: T["static"]) => T["static"]);
4
+ interface Instruction {
5
+ optionals: string[];
6
+ optionalsInArray: string[][];
7
+ parentIsOptional: boolean;
8
+ array: number;
9
+ unions: TypeCheck<any>[][];
10
+ unionKeys: Record<string, 1>;
11
+ /**
12
+ * TypeCompiler is required when using Union
13
+ *
14
+ * Left as opt-in to reduce bundle size
15
+ * many end-user doesn't use Union
16
+ *
17
+ * @default undefined
18
+ */
19
+ TypeCompiler?: typeof TypeCompiler;
20
+ typeCompilerWanred?: boolean;
21
+ }
22
+ export declare const createMirror: <T extends TAnySchema>(schema: T, { TypeCompiler }?: Pick<Instruction, "TypeCompiler">) => ((v: T["static"]) => T["static"]);
4
23
  export default createMirror;
package/dist/cjs/index.js CHANGED
@@ -25,12 +25,13 @@ __export(index_exports, {
25
25
  mergeObjectIntersection: () => mergeObjectIntersection
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
+ var import_compiler = require("@sinclair/typebox/compiler");
28
29
  var Kind = Symbol.for("TypeBox.Kind");
29
- var OptionalKind = Symbol.for("TypeBox.Optional");
30
30
  var isSpecialProperty = (name) => /(\ |-|\t|\n)/.test(name);
31
- var joinProperty = (v1, v2) => {
32
- if (isSpecialProperty(v2)) return `${v1}["${v2}"]`;
33
- return `${v1}.${v2}`;
31
+ var joinProperty = (v1, v2, isOptional = false) => {
32
+ if (typeof v2 === "number") return `${v1}[${v2}]`;
33
+ if (isSpecialProperty(v2)) return `${v1}${isOptional ? "?." : ""}["${v2}"]`;
34
+ return `${v1}${isOptional ? "?" : ""}.${v2}`;
34
35
  };
35
36
  var encodeProperty = (v) => isSpecialProperty(v) ? `"${v}"` : v;
36
37
  var mergeObjectIntersection = (schema) => {
@@ -52,27 +53,86 @@ var mergeObjectIntersection = (schema) => {
52
53
  }
53
54
  return newSchema;
54
55
  };
56
+ var handleRecord = (schema, property, instruction) => {
57
+ const child = schema.patternProperties["^(.*)$"] ?? schema.patternProperties[Object.keys(schema.patternProperties)[0]];
58
+ if (!child) return property;
59
+ const i = instruction.array;
60
+ instruction.array++;
61
+ return `(()=>{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)}}return ar${i}v})()`;
62
+ };
63
+ var handleTuple = (schema, property, instruction) => {
64
+ const i = instruction.array;
65
+ instruction.array++;
66
+ const isRoot = property === "v" && !instruction.unions.length;
67
+ let v = "";
68
+ if (!isRoot) v = `(()=>{`;
69
+ v += `const ar${i}v=[`;
70
+ for (let i2 = 0; i2 < schema.length; i2++) {
71
+ if (i2 !== 0) v += ",";
72
+ v += mirror(
73
+ schema[i2],
74
+ joinProperty(property, i2, instruction.parentIsOptional),
75
+ instruction
76
+ );
77
+ }
78
+ v += `];`;
79
+ if (!isRoot) v += `return ar${i}v})()`;
80
+ return v;
81
+ };
82
+ var handleUnion = (schemas, property, instruction) => {
83
+ if (instruction.TypeCompiler === void 0) {
84
+ if (!instruction.typeCompilerWanred) {
85
+ console.warn(
86
+ new Error("TypeBox's TypeCompiler is required to use Union")
87
+ );
88
+ instruction.typeCompilerWanred = true;
89
+ }
90
+ return property;
91
+ }
92
+ instruction.unionKeys[property] = 1;
93
+ const ui = instruction.unions.length;
94
+ const typeChecks = instruction.unions[ui] = [];
95
+ let v = `(()=>{
96
+ `;
97
+ for (let i = 0; i < schemas.length; i++) {
98
+ typeChecks.push(import_compiler.TypeCompiler.Compile(schemas[i]));
99
+ v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(schemas[i], property, instruction)}}
100
+ `;
101
+ }
102
+ v += `return undefined})()`;
103
+ return v;
104
+ };
55
105
  var mirror = (schema, property, instruction) => {
56
106
  if (!schema) return "";
57
- const isRoot = property === "v";
58
- if (isRoot && schema.type !== "object" && schema.type !== "array")
107
+ const isRoot = property === "v" && !instruction.unions.length;
108
+ if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
59
109
  return `return v`;
60
110
  let v = "";
61
111
  switch (schema.type) {
62
112
  case "object":
113
+ if (schema[Kind] === "Record") {
114
+ v = handleRecord(schema, property, instruction);
115
+ break;
116
+ }
63
117
  schema = mergeObjectIntersection(schema);
64
118
  v += "{";
65
119
  if (schema.additionalProperties) v += `...${property}`;
66
120
  const keys = Object.keys(schema.properties);
67
121
  for (let i2 = 0; i2 < keys.length; i2++) {
68
122
  const key = keys[i2];
69
- const name = joinProperty(property, key);
70
- if (!schema.required.includes(key)) {
123
+ let isOptional = schema.required && !schema.required.includes(key) || Array.isArray(schema.properties[key].anyOf);
124
+ const name = joinProperty(
125
+ property,
126
+ key,
127
+ instruction.parentIsOptional
128
+ );
129
+ if (isOptional) {
71
130
  const index = instruction.array;
72
131
  if (property.startsWith("ar")) {
132
+ const refName = name.slice(name.indexOf(".") + 1);
73
133
  const array = instruction.optionalsInArray;
74
- if (array[index]) array[index].push(name.slice(5));
75
- else array[index] = [name.slice(5)];
134
+ if (array[index]) array[index].push(refName);
135
+ else array[index] = [refName];
76
136
  } else {
77
137
  instruction.optionals.push(name);
78
138
  }
@@ -81,32 +141,45 @@ var mirror = (schema, property, instruction) => {
81
141
  if (schema.additionalProperties && child.type !== "object")
82
142
  continue;
83
143
  if (i2 !== 0) v += ",";
84
- v += `${encodeProperty(key)}:${mirror(child, name, instruction)}`;
144
+ v += `${encodeProperty(key)}:${isOptional ? `${name}===undefined?undefined:` : ""}${mirror(
145
+ child,
146
+ name,
147
+ {
148
+ ...instruction,
149
+ parentIsOptional: isOptional
150
+ }
151
+ )}`;
85
152
  }
86
153
  v += "}";
87
154
  break;
88
155
  case "array":
89
- const i = instruction.array;
90
- instruction.array++;
91
156
  if (schema.items.type !== "object" && schema.items.type !== "array") {
92
- v = property;
157
+ if (Array.isArray(schema.items))
158
+ v = handleTuple(schema.items, property, instruction);
159
+ else v = property;
93
160
  break;
94
161
  }
162
+ const i = instruction.array;
163
+ instruction.array++;
95
164
  if (!isRoot) v = `(()=>{`;
96
- v += `const ar${i}s=${property},ar${i}v=new Array(${property}.length);for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}s[i];ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)};`;
165
+ v += `const ar${i}s=${property},ar${i}v=new Array(${property}.length);for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}s[i];ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)}`;
97
166
  const optionals = instruction.optionalsInArray[i + 1];
98
167
  if (optionals) {
99
168
  for (let oi = 0; oi < optionals.length; oi++) {
100
- const key = `ar${i}v[i].${optionals[oi]}`;
101
- if (oi !== 0) v += ";";
102
- v += `if(${key}===undefined)delete ${key}`;
169
+ const target = `ar${i}v[i].${optionals[oi]}`;
170
+ v += `;if(${target}===undefined)delete ${target}`;
103
171
  }
104
172
  }
105
173
  v += `}`;
106
- if (!isRoot) v += `}return ar${i}v})()`;
174
+ if (!isRoot) v += `return ar${i}v})()`;
107
175
  break;
108
176
  default:
177
+ if (Array.isArray(schema.anyOf)) {
178
+ v = handleUnion(schema.anyOf, property, instruction);
179
+ break;
180
+ }
109
181
  v = property;
182
+ break;
110
183
  }
111
184
  if (!isRoot) return v;
112
185
  if (schema.type === "array") return `${v}return ar0v`;
@@ -114,18 +187,33 @@ var mirror = (schema, property, instruction) => {
114
187
  `;
115
188
  for (let i = 0; i < instruction.optionals.length; i++) {
116
189
  const key = instruction.optionals[i];
117
- v += `if(${key}===undefined)delete x${key.slice(1)}
190
+ const prop = key.slice(1);
191
+ v += `if(${key}===undefined`;
192
+ if (instruction.unionKeys[key]) v += `||x${prop}===undefined`;
193
+ v += `)delete x?${prop}
118
194
  `;
119
195
  }
120
196
  return `${v}return x`;
121
197
  };
122
- var createMirror = (schema) => {
198
+ var createMirror = (schema, { TypeCompiler: TypeCompiler2 } = {}) => {
199
+ const unions = [];
123
200
  const f = mirror(schema, "v", {
124
201
  optionals: [],
125
202
  optionalsInArray: [],
126
- array: 0
203
+ array: 0,
204
+ parentIsOptional: false,
205
+ unions,
206
+ unionKeys: {},
207
+ TypeCompiler: TypeCompiler2
208
+ });
209
+ if (!unions.length) return Function("v", f);
210
+ const fn = `return function mirror(v){${f}}`;
211
+ return Function(
212
+ "d",
213
+ fn
214
+ )({
215
+ unions
127
216
  });
128
- return Function("v", f);
129
217
  };
130
218
  var index_default = createMirror;
131
219
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.d.ts CHANGED
@@ -1,4 +1,23 @@
1
+ import { TypeCompiler, type TypeCheck } from '@sinclair/typebox/compiler';
1
2
  import type { TAnySchema } from '@sinclair/typebox';
2
3
  export declare const mergeObjectIntersection: (schema: TAnySchema) => TAnySchema;
3
- export declare const createMirror: <T extends TAnySchema>(schema: T) => ((v: T["static"]) => T["static"]);
4
+ interface Instruction {
5
+ optionals: string[];
6
+ optionalsInArray: string[][];
7
+ parentIsOptional: boolean;
8
+ array: number;
9
+ unions: TypeCheck<any>[][];
10
+ unionKeys: Record<string, 1>;
11
+ /**
12
+ * TypeCompiler is required when using Union
13
+ *
14
+ * Left as opt-in to reduce bundle size
15
+ * many end-user doesn't use Union
16
+ *
17
+ * @default undefined
18
+ */
19
+ TypeCompiler?: typeof TypeCompiler;
20
+ typeCompilerWanred?: boolean;
21
+ }
22
+ export declare const createMirror: <T extends TAnySchema>(schema: T, { TypeCompiler }?: Pick<Instruction, "TypeCompiler">) => ((v: T["static"]) => T["static"]);
4
23
  export default createMirror;
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  // src/index.ts
2
+ import { TypeCompiler } from "@sinclair/typebox/compiler";
2
3
  var Kind = Symbol.for("TypeBox.Kind");
3
- var OptionalKind = Symbol.for("TypeBox.Optional");
4
4
  var isSpecialProperty = (name) => /(\ |-|\t|\n)/.test(name);
5
- var joinProperty = (v1, v2) => {
6
- if (isSpecialProperty(v2)) return `${v1}["${v2}"]`;
7
- return `${v1}.${v2}`;
5
+ var joinProperty = (v1, v2, isOptional = false) => {
6
+ if (typeof v2 === "number") return `${v1}[${v2}]`;
7
+ if (isSpecialProperty(v2)) return `${v1}${isOptional ? "?." : ""}["${v2}"]`;
8
+ return `${v1}${isOptional ? "?" : ""}.${v2}`;
8
9
  };
9
10
  var encodeProperty = (v) => isSpecialProperty(v) ? `"${v}"` : v;
10
11
  var mergeObjectIntersection = (schema) => {
@@ -26,27 +27,86 @@ var mergeObjectIntersection = (schema) => {
26
27
  }
27
28
  return newSchema;
28
29
  };
30
+ var handleRecord = (schema, property, instruction) => {
31
+ const child = schema.patternProperties["^(.*)$"] ?? schema.patternProperties[Object.keys(schema.patternProperties)[0]];
32
+ if (!child) return property;
33
+ const i = instruction.array;
34
+ instruction.array++;
35
+ return `(()=>{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)}}return ar${i}v})()`;
36
+ };
37
+ var handleTuple = (schema, property, instruction) => {
38
+ const i = instruction.array;
39
+ instruction.array++;
40
+ const isRoot = property === "v" && !instruction.unions.length;
41
+ let v = "";
42
+ if (!isRoot) v = `(()=>{`;
43
+ v += `const ar${i}v=[`;
44
+ for (let i2 = 0; i2 < schema.length; i2++) {
45
+ if (i2 !== 0) v += ",";
46
+ v += mirror(
47
+ schema[i2],
48
+ joinProperty(property, i2, instruction.parentIsOptional),
49
+ instruction
50
+ );
51
+ }
52
+ v += `];`;
53
+ if (!isRoot) v += `return ar${i}v})()`;
54
+ return v;
55
+ };
56
+ var handleUnion = (schemas, property, instruction) => {
57
+ if (instruction.TypeCompiler === void 0) {
58
+ if (!instruction.typeCompilerWanred) {
59
+ console.warn(
60
+ new Error("TypeBox's TypeCompiler is required to use Union")
61
+ );
62
+ instruction.typeCompilerWanred = true;
63
+ }
64
+ return property;
65
+ }
66
+ instruction.unionKeys[property] = 1;
67
+ const ui = instruction.unions.length;
68
+ const typeChecks = instruction.unions[ui] = [];
69
+ let v = `(()=>{
70
+ `;
71
+ for (let i = 0; i < schemas.length; i++) {
72
+ typeChecks.push(TypeCompiler.Compile(schemas[i]));
73
+ v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror(schemas[i], property, instruction)}}
74
+ `;
75
+ }
76
+ v += `return undefined})()`;
77
+ return v;
78
+ };
29
79
  var mirror = (schema, property, instruction) => {
30
80
  if (!schema) return "";
31
- const isRoot = property === "v";
32
- if (isRoot && schema.type !== "object" && schema.type !== "array")
81
+ const isRoot = property === "v" && !instruction.unions.length;
82
+ if (isRoot && schema.type !== "object" && schema.type !== "array" && !schema.anyOf)
33
83
  return `return v`;
34
84
  let v = "";
35
85
  switch (schema.type) {
36
86
  case "object":
87
+ if (schema[Kind] === "Record") {
88
+ v = handleRecord(schema, property, instruction);
89
+ break;
90
+ }
37
91
  schema = mergeObjectIntersection(schema);
38
92
  v += "{";
39
93
  if (schema.additionalProperties) v += `...${property}`;
40
94
  const keys = Object.keys(schema.properties);
41
95
  for (let i2 = 0; i2 < keys.length; i2++) {
42
96
  const key = keys[i2];
43
- const name = joinProperty(property, key);
44
- if (!schema.required.includes(key)) {
97
+ let isOptional = schema.required && !schema.required.includes(key) || Array.isArray(schema.properties[key].anyOf);
98
+ const name = joinProperty(
99
+ property,
100
+ key,
101
+ instruction.parentIsOptional
102
+ );
103
+ if (isOptional) {
45
104
  const index = instruction.array;
46
105
  if (property.startsWith("ar")) {
106
+ const refName = name.slice(name.indexOf(".") + 1);
47
107
  const array = instruction.optionalsInArray;
48
- if (array[index]) array[index].push(name.slice(5));
49
- else array[index] = [name.slice(5)];
108
+ if (array[index]) array[index].push(refName);
109
+ else array[index] = [refName];
50
110
  } else {
51
111
  instruction.optionals.push(name);
52
112
  }
@@ -55,32 +115,45 @@ var mirror = (schema, property, instruction) => {
55
115
  if (schema.additionalProperties && child.type !== "object")
56
116
  continue;
57
117
  if (i2 !== 0) v += ",";
58
- v += `${encodeProperty(key)}:${mirror(child, name, instruction)}`;
118
+ v += `${encodeProperty(key)}:${isOptional ? `${name}===undefined?undefined:` : ""}${mirror(
119
+ child,
120
+ name,
121
+ {
122
+ ...instruction,
123
+ parentIsOptional: isOptional
124
+ }
125
+ )}`;
59
126
  }
60
127
  v += "}";
61
128
  break;
62
129
  case "array":
63
- const i = instruction.array;
64
- instruction.array++;
65
130
  if (schema.items.type !== "object" && schema.items.type !== "array") {
66
- v = property;
131
+ if (Array.isArray(schema.items))
132
+ v = handleTuple(schema.items, property, instruction);
133
+ else v = property;
67
134
  break;
68
135
  }
136
+ const i = instruction.array;
137
+ instruction.array++;
69
138
  if (!isRoot) v = `(()=>{`;
70
- v += `const ar${i}s=${property},ar${i}v=new Array(${property}.length);for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}s[i];ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)};`;
139
+ v += `const ar${i}s=${property},ar${i}v=new Array(${property}.length);for(let i=0;i<ar${i}s.length;i++){const ar${i}p=ar${i}s[i];ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)}`;
71
140
  const optionals = instruction.optionalsInArray[i + 1];
72
141
  if (optionals) {
73
142
  for (let oi = 0; oi < optionals.length; oi++) {
74
- const key = `ar${i}v[i].${optionals[oi]}`;
75
- if (oi !== 0) v += ";";
76
- v += `if(${key}===undefined)delete ${key}`;
143
+ const target = `ar${i}v[i].${optionals[oi]}`;
144
+ v += `;if(${target}===undefined)delete ${target}`;
77
145
  }
78
146
  }
79
147
  v += `}`;
80
- if (!isRoot) v += `}return ar${i}v})()`;
148
+ if (!isRoot) v += `return ar${i}v})()`;
81
149
  break;
82
150
  default:
151
+ if (Array.isArray(schema.anyOf)) {
152
+ v = handleUnion(schema.anyOf, property, instruction);
153
+ break;
154
+ }
83
155
  v = property;
156
+ break;
84
157
  }
85
158
  if (!isRoot) return v;
86
159
  if (schema.type === "array") return `${v}return ar0v`;
@@ -88,18 +161,33 @@ var mirror = (schema, property, instruction) => {
88
161
  `;
89
162
  for (let i = 0; i < instruction.optionals.length; i++) {
90
163
  const key = instruction.optionals[i];
91
- v += `if(${key}===undefined)delete x${key.slice(1)}
164
+ const prop = key.slice(1);
165
+ v += `if(${key}===undefined`;
166
+ if (instruction.unionKeys[key]) v += `||x${prop}===undefined`;
167
+ v += `)delete x?${prop}
92
168
  `;
93
169
  }
94
170
  return `${v}return x`;
95
171
  };
96
- var createMirror = (schema) => {
172
+ var createMirror = (schema, { TypeCompiler: TypeCompiler2 } = {}) => {
173
+ const unions = [];
97
174
  const f = mirror(schema, "v", {
98
175
  optionals: [],
99
176
  optionalsInArray: [],
100
- array: 0
177
+ array: 0,
178
+ parentIsOptional: false,
179
+ unions,
180
+ unionKeys: {},
181
+ TypeCompiler: TypeCompiler2
182
+ });
183
+ if (!unions.length) return Function("v", f);
184
+ const fn = `return function mirror(v){${f}}`;
185
+ return Function(
186
+ "d",
187
+ fn
188
+ )({
189
+ unions
101
190
  });
102
- return Function("v", f);
103
191
  };
104
192
  var index_default = createMirror;
105
193
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exact-mirror",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "Mirror exact value to TypeBox/OpenAPI model",
5
5
  "license": "MIT",
6
6
  "scripts": {