@voidhash/mimic 1.0.0-beta.16 → 1.0.0-beta.17
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/EffectSchema.cjs +3 -3
- package/dist/EffectSchema.d.cts +5 -5
- package/dist/EffectSchema.d.cts.map +1 -1
- package/dist/EffectSchema.d.mts +5 -5
- package/dist/EffectSchema.d.mts.map +1 -1
- package/dist/EffectSchema.mjs +3 -3
- package/dist/EffectSchema.mjs.map +1 -1
- package/dist/FractionalIndex.mjs.map +1 -1
- package/dist/Operation.d.cts +4 -4
- package/dist/Operation.d.cts.map +1 -1
- package/dist/Operation.d.mts +4 -4
- package/dist/Operation.d.mts.map +1 -1
- package/dist/Operation.mjs.map +1 -1
- package/dist/OperationDefinition.d.cts +2 -2
- package/dist/OperationDefinition.d.cts.map +1 -1
- package/dist/OperationDefinition.d.mts +2 -2
- package/dist/OperationDefinition.d.mts.map +1 -1
- package/dist/OperationDefinition.mjs.map +1 -1
- package/dist/Presence.mjs.map +1 -1
- package/dist/SchemaJSON.cjs +305 -0
- package/dist/SchemaJSON.d.cts +11 -0
- package/dist/SchemaJSON.d.cts.map +1 -0
- package/dist/SchemaJSON.d.mts +11 -0
- package/dist/SchemaJSON.d.mts.map +1 -0
- package/dist/SchemaJSON.mjs +301 -0
- package/dist/SchemaJSON.mjs.map +1 -0
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/primitives/Array.cjs +12 -2
- package/dist/primitives/Array.d.cts.map +1 -1
- package/dist/primitives/Array.d.mts.map +1 -1
- package/dist/primitives/Array.mjs +12 -2
- package/dist/primitives/Array.mjs.map +1 -1
- package/dist/primitives/Boolean.mjs.map +1 -1
- package/dist/primitives/Either.mjs.map +1 -1
- package/dist/primitives/Literal.mjs.map +1 -1
- package/dist/primitives/Number.cjs +27 -5
- package/dist/primitives/Number.d.cts.map +1 -1
- package/dist/primitives/Number.d.mts.map +1 -1
- package/dist/primitives/Number.mjs +27 -5
- package/dist/primitives/Number.mjs.map +1 -1
- package/dist/primitives/String.cjs +44 -13
- package/dist/primitives/String.d.cts.map +1 -1
- package/dist/primitives/String.d.mts.map +1 -1
- package/dist/primitives/String.mjs +44 -13
- package/dist/primitives/String.mjs.map +1 -1
- package/dist/primitives/Union.mjs.map +1 -1
- package/dist/primitives/shared.d.cts +2 -0
- package/dist/primitives/shared.d.cts.map +1 -1
- package/dist/primitives/shared.d.mts +2 -0
- package/dist/primitives/shared.d.mts.map +1 -1
- package/dist/primitives/shared.mjs.map +1 -1
- package/package.json +15 -8
- package/src/EffectSchema.ts +3 -3
- package/src/FractionalIndex.ts +18 -18
- package/src/Operation.ts +5 -5
- package/src/OperationDefinition.ts +2 -2
- package/src/Presence.ts +3 -3
- package/src/SchemaJSON.ts +396 -0
- package/src/index.ts +1 -0
- package/src/primitives/Array.ts +18 -8
- package/src/primitives/Boolean.ts +2 -2
- package/src/primitives/Either.ts +2 -2
- package/src/primitives/Literal.ts +2 -2
- package/src/primitives/Number.ts +44 -22
- package/src/primitives/String.ts +61 -34
- package/src/primitives/Union.ts +1 -1
- package/src/primitives/shared.ts +2 -0
- package/.turbo/turbo-build.log +0 -270
- package/tests/Document.test.ts +0 -557
- package/tests/EffectSchema.test.ts +0 -546
- package/tests/FractionalIndex.test.ts +0 -377
- package/tests/OperationPath.test.ts +0 -151
- package/tests/Presence.test.ts +0 -321
- package/tests/Primitive.test.ts +0 -381
- package/tests/client/ClientDocument.test.ts +0 -1981
- package/tests/client/WebSocketTransport.test.ts +0 -1217
- package/tests/primitives/Array.test.ts +0 -526
- package/tests/primitives/Boolean.test.ts +0 -126
- package/tests/primitives/Either.test.ts +0 -707
- package/tests/primitives/Lazy.test.ts +0 -143
- package/tests/primitives/Literal.test.ts +0 -122
- package/tests/primitives/Number.test.ts +0 -133
- package/tests/primitives/String.test.ts +0 -128
- package/tests/primitives/Struct.test.ts +0 -1154
- package/tests/primitives/Tree.test.ts +0 -1139
- package/tests/primitives/TreeNode.test.ts +0 -50
- package/tests/primitives/Union.test.ts +0 -554
- package/tests/server/ServerDocument.test.ts +0 -903
- package/tsconfig.build.json +0 -24
- package/tsconfig.json +0 -8
- package/tsdown.config.ts +0 -18
- package/vitest.mts +0 -11
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import type { AnyPrimitive, Validator } from "./primitives/shared";
|
|
2
|
+
import { StringPrimitive } from "./primitives/String";
|
|
3
|
+
import { NumberPrimitive } from "./primitives/Number";
|
|
4
|
+
import { BooleanPrimitive } from "./primitives/Boolean";
|
|
5
|
+
import { LiteralPrimitive } from "./primitives/Literal";
|
|
6
|
+
import { StructPrimitive } from "./primitives/Struct";
|
|
7
|
+
import { ArrayPrimitive } from "./primitives/Array";
|
|
8
|
+
import { LazyPrimitive } from "./primitives/Lazy";
|
|
9
|
+
import { UnionPrimitive } from "./primitives/Union";
|
|
10
|
+
import { EitherPrimitive } from "./primitives/Either";
|
|
11
|
+
import { TreePrimitive } from "./primitives/Tree";
|
|
12
|
+
import { TreeNodePrimitive, type AnyTreeNodePrimitive } from "./primitives/TreeNode";
|
|
13
|
+
import * as Primitive from "./Primitive";
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// JSON Types
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
interface ValidatorJSON {
|
|
20
|
+
readonly kind: string;
|
|
21
|
+
readonly params?: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ScalarJSON {
|
|
25
|
+
readonly type: "string" | "number" | "boolean";
|
|
26
|
+
readonly required: boolean;
|
|
27
|
+
readonly default?: unknown;
|
|
28
|
+
readonly validators?: readonly ValidatorJSON[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface LiteralJSON {
|
|
32
|
+
readonly type: "literal";
|
|
33
|
+
readonly value: string | number | boolean | null;
|
|
34
|
+
readonly required: boolean;
|
|
35
|
+
readonly default?: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface StructJSON {
|
|
39
|
+
readonly type: "struct";
|
|
40
|
+
readonly required: boolean;
|
|
41
|
+
readonly default?: unknown;
|
|
42
|
+
readonly fields: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ArrayJSON {
|
|
46
|
+
readonly type: "array";
|
|
47
|
+
readonly required: boolean;
|
|
48
|
+
readonly default?: unknown;
|
|
49
|
+
readonly element: unknown;
|
|
50
|
+
readonly validators?: readonly ValidatorJSON[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface UnionJSON {
|
|
54
|
+
readonly type: "union";
|
|
55
|
+
readonly required: boolean;
|
|
56
|
+
readonly default?: unknown;
|
|
57
|
+
readonly discriminator: string;
|
|
58
|
+
readonly variants: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface EitherJSON {
|
|
62
|
+
readonly type: "either";
|
|
63
|
+
readonly required: boolean;
|
|
64
|
+
readonly default?: unknown;
|
|
65
|
+
readonly variants: readonly unknown[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface TreeNodeJSON {
|
|
69
|
+
readonly type: "treeNode";
|
|
70
|
+
readonly nodeType: string;
|
|
71
|
+
readonly data: unknown;
|
|
72
|
+
readonly children: readonly string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface TreeJSON {
|
|
76
|
+
readonly type: "tree";
|
|
77
|
+
readonly required: boolean;
|
|
78
|
+
readonly default?: unknown;
|
|
79
|
+
readonly root: string;
|
|
80
|
+
readonly nodes: Record<string, TreeNodeJSON>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// Helper: access private _schema
|
|
85
|
+
// =============================================================================
|
|
86
|
+
|
|
87
|
+
function getSchema(primitive: AnyPrimitive): any {
|
|
88
|
+
return (primitive as any)._schema;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// =============================================================================
|
|
92
|
+
// Serialize validators
|
|
93
|
+
// =============================================================================
|
|
94
|
+
|
|
95
|
+
function serializeValidators(validators: readonly Validator<any>[]): ValidatorJSON[] | undefined {
|
|
96
|
+
const serializable = validators.filter((v) => v.kind != null);
|
|
97
|
+
if (serializable.length === 0) return undefined;
|
|
98
|
+
return serializable.map((v) => {
|
|
99
|
+
const json: ValidatorJSON = { kind: v.kind! };
|
|
100
|
+
if (v.params !== undefined) {
|
|
101
|
+
return { ...json, params: v.params };
|
|
102
|
+
}
|
|
103
|
+
return json;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// =============================================================================
|
|
108
|
+
// toJSON
|
|
109
|
+
// =============================================================================
|
|
110
|
+
|
|
111
|
+
function collectTreeNodes(
|
|
112
|
+
node: AnyTreeNodePrimitive,
|
|
113
|
+
visited: Map<string, AnyTreeNodePrimitive>
|
|
114
|
+
): void {
|
|
115
|
+
if (visited.has(node.type)) return;
|
|
116
|
+
visited.set(node.type, node);
|
|
117
|
+
for (const child of node.children) {
|
|
118
|
+
collectTreeNodes(child, visited);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function toJSON(primitive: AnyPrimitive): unknown {
|
|
123
|
+
// Resolve Lazy first
|
|
124
|
+
if (primitive instanceof LazyPrimitive) {
|
|
125
|
+
const resolved = (primitive as any)._resolve();
|
|
126
|
+
return toJSON(resolved);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const schema = getSchema(primitive);
|
|
130
|
+
|
|
131
|
+
if (primitive instanceof StringPrimitive) {
|
|
132
|
+
const json: any = { type: "string", required: schema.required };
|
|
133
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
134
|
+
const validators = serializeValidators(schema.validators);
|
|
135
|
+
if (validators) json.validators = validators;
|
|
136
|
+
return json;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (primitive instanceof NumberPrimitive) {
|
|
140
|
+
const json: any = { type: "number", required: schema.required };
|
|
141
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
142
|
+
const validators = serializeValidators(schema.validators);
|
|
143
|
+
if (validators) json.validators = validators;
|
|
144
|
+
return json;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (primitive instanceof BooleanPrimitive) {
|
|
148
|
+
const json: any = { type: "boolean", required: schema.required };
|
|
149
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
150
|
+
return json;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (primitive instanceof LiteralPrimitive) {
|
|
154
|
+
const json: any = {
|
|
155
|
+
type: "literal",
|
|
156
|
+
value: (primitive as LiteralPrimitive<any>).literal,
|
|
157
|
+
required: schema.required,
|
|
158
|
+
};
|
|
159
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
160
|
+
return json;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (primitive instanceof StructPrimitive) {
|
|
164
|
+
const fields: Record<string, unknown> = {};
|
|
165
|
+
const structFields = (primitive as StructPrimitive<any>).fields;
|
|
166
|
+
for (const key in structFields) {
|
|
167
|
+
fields[key] = toJSON(structFields[key]!);
|
|
168
|
+
}
|
|
169
|
+
const json: any = { type: "struct", required: schema.required, fields };
|
|
170
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
171
|
+
return json;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (primitive instanceof ArrayPrimitive) {
|
|
175
|
+
const json: any = {
|
|
176
|
+
type: "array",
|
|
177
|
+
required: schema.required,
|
|
178
|
+
element: toJSON((primitive as ArrayPrimitive<any>).element),
|
|
179
|
+
};
|
|
180
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
181
|
+
const validators = serializeValidators(schema.validators);
|
|
182
|
+
if (validators) json.validators = validators;
|
|
183
|
+
return json;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (primitive instanceof UnionPrimitive) {
|
|
187
|
+
const variants: Record<string, unknown> = {};
|
|
188
|
+
const unionVariants = (primitive as UnionPrimitive<any, any>).variants;
|
|
189
|
+
for (const key in unionVariants) {
|
|
190
|
+
variants[key] = toJSON(unionVariants[key]!);
|
|
191
|
+
}
|
|
192
|
+
const json: any = {
|
|
193
|
+
type: "union",
|
|
194
|
+
required: schema.required,
|
|
195
|
+
discriminator: (primitive as UnionPrimitive<any, any>).discriminator,
|
|
196
|
+
variants,
|
|
197
|
+
};
|
|
198
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
199
|
+
return json;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (primitive instanceof EitherPrimitive) {
|
|
203
|
+
const variants = (primitive as EitherPrimitive<any>).variants;
|
|
204
|
+
const json: any = {
|
|
205
|
+
type: "either",
|
|
206
|
+
required: schema.required,
|
|
207
|
+
variants: variants.map((v: AnyPrimitive) => toJSON(v)),
|
|
208
|
+
};
|
|
209
|
+
if (schema.defaultValue !== undefined) json.default = schema.defaultValue;
|
|
210
|
+
return json;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (primitive instanceof TreePrimitive) {
|
|
214
|
+
const root = (primitive as TreePrimitive<any>).root;
|
|
215
|
+
const nodeMap = new Map<string, AnyTreeNodePrimitive>();
|
|
216
|
+
collectTreeNodes(root, nodeMap);
|
|
217
|
+
|
|
218
|
+
const nodes: Record<string, TreeNodeJSON> = {};
|
|
219
|
+
for (const [nodeType, node] of nodeMap) {
|
|
220
|
+
nodes[nodeType] = {
|
|
221
|
+
type: "treeNode",
|
|
222
|
+
nodeType,
|
|
223
|
+
data: toJSON(node.data),
|
|
224
|
+
children: node.children.map((c) => c.type),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const json: any = {
|
|
229
|
+
type: "tree",
|
|
230
|
+
required: schema.required,
|
|
231
|
+
root: root.type,
|
|
232
|
+
nodes,
|
|
233
|
+
};
|
|
234
|
+
if (schema.defaultInput !== undefined) json.default = schema.defaultInput;
|
|
235
|
+
return json;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
throw new Error(`Unknown primitive type: ${primitive._tag}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// =============================================================================
|
|
242
|
+
// fromJSON
|
|
243
|
+
// =============================================================================
|
|
244
|
+
|
|
245
|
+
function applyStringValidators(p: StringPrimitive<any, any>, validators: readonly ValidatorJSON[]): StringPrimitive<any, any> {
|
|
246
|
+
let result = p;
|
|
247
|
+
for (const v of validators) {
|
|
248
|
+
const params = v.params as any;
|
|
249
|
+
switch (v.kind) {
|
|
250
|
+
case "min": result = result.min(params.value); break;
|
|
251
|
+
case "max": result = result.max(params.value); break;
|
|
252
|
+
case "length": result = result.length(params.value); break;
|
|
253
|
+
case "regex": result = result.regex(new RegExp(params.pattern, params.flags)); break;
|
|
254
|
+
case "email": result = result.email(); break;
|
|
255
|
+
case "url": result = result.url(); break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function applyNumberValidators(p: NumberPrimitive<any, any>, validators: readonly ValidatorJSON[]): NumberPrimitive<any, any> {
|
|
262
|
+
let result = p;
|
|
263
|
+
for (const v of validators) {
|
|
264
|
+
const params = v.params as any;
|
|
265
|
+
switch (v.kind) {
|
|
266
|
+
case "min": result = result.min(params.value); break;
|
|
267
|
+
case "max": result = result.max(params.value); break;
|
|
268
|
+
case "positive": result = result.positive(); break;
|
|
269
|
+
case "negative": result = result.negative(); break;
|
|
270
|
+
case "int": result = result.int(); break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function applyArrayValidators(p: ArrayPrimitive<any, any, any>, validators: readonly ValidatorJSON[]): ArrayPrimitive<any, any, any> {
|
|
277
|
+
let result = p;
|
|
278
|
+
for (const v of validators) {
|
|
279
|
+
const params = v.params as any;
|
|
280
|
+
switch (v.kind) {
|
|
281
|
+
case "minLength": result = result.minLength(params.value); break;
|
|
282
|
+
case "maxLength": result = result.maxLength(params.value); break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function fromJSON(json: unknown): AnyPrimitive {
|
|
289
|
+
const obj = json as any;
|
|
290
|
+
|
|
291
|
+
switch (obj.type) {
|
|
292
|
+
case "string": {
|
|
293
|
+
let p: StringPrimitive<any, any> = Primitive.String();
|
|
294
|
+
if (obj.validators) p = applyStringValidators(p, obj.validators);
|
|
295
|
+
if (obj.required) p = p.required();
|
|
296
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
297
|
+
return p;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
case "number": {
|
|
301
|
+
let p: NumberPrimitive<any, any> = Primitive.Number();
|
|
302
|
+
if (obj.validators) p = applyNumberValidators(p, obj.validators);
|
|
303
|
+
if (obj.required) p = p.required();
|
|
304
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
305
|
+
return p;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
case "boolean": {
|
|
309
|
+
let p: BooleanPrimitive<any, any> = Primitive.Boolean();
|
|
310
|
+
if (obj.required) p = p.required();
|
|
311
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
312
|
+
return p;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case "literal": {
|
|
316
|
+
let p: LiteralPrimitive<any, any, any> = Primitive.Literal(obj.value);
|
|
317
|
+
if (obj.required) p = p.required();
|
|
318
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
319
|
+
return p;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
case "struct": {
|
|
323
|
+
const fields: Record<string, AnyPrimitive> = {};
|
|
324
|
+
for (const key in obj.fields) {
|
|
325
|
+
fields[key] = fromJSON(obj.fields[key]);
|
|
326
|
+
}
|
|
327
|
+
let p: StructPrimitive<any, any, any> = Primitive.Struct(fields);
|
|
328
|
+
if (obj.required) p = p.required();
|
|
329
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
330
|
+
return p;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
case "array": {
|
|
334
|
+
const element = fromJSON(obj.element);
|
|
335
|
+
let p: ArrayPrimitive<any, any, any> = Primitive.Array(element);
|
|
336
|
+
if (obj.validators) p = applyArrayValidators(p, obj.validators);
|
|
337
|
+
if (obj.required) p = p.required();
|
|
338
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
339
|
+
return p;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
case "union": {
|
|
343
|
+
const variants: Record<string, any> = {};
|
|
344
|
+
for (const key in obj.variants) {
|
|
345
|
+
variants[key] = fromJSON(obj.variants[key]);
|
|
346
|
+
}
|
|
347
|
+
let p: UnionPrimitive<any, any, any, any> = Primitive.Union({
|
|
348
|
+
discriminator: obj.discriminator,
|
|
349
|
+
variants,
|
|
350
|
+
});
|
|
351
|
+
if (obj.required) p = p.required();
|
|
352
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
353
|
+
return p;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
case "either": {
|
|
357
|
+
const variants = obj.variants.map((v: unknown) => fromJSON(v));
|
|
358
|
+
let p: EitherPrimitive<any, any, any> = Primitive.Either(...variants);
|
|
359
|
+
if (obj.required) p = p.required();
|
|
360
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
361
|
+
return p;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case "tree": {
|
|
365
|
+
const treeJSON = obj as TreeJSON;
|
|
366
|
+
// First pass: create all TreeNode primitives with placeholder children
|
|
367
|
+
const nodeMap = new Map<string, AnyTreeNodePrimitive>();
|
|
368
|
+
const nodeJSONMap = new Map<string, TreeNodeJSON>();
|
|
369
|
+
|
|
370
|
+
for (const nodeType in treeJSON.nodes) {
|
|
371
|
+
nodeJSONMap.set(nodeType, treeJSON.nodes[nodeType]!);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Create nodes with lazy children to handle circular references
|
|
375
|
+
for (const [nodeType, nodeJSON] of nodeJSONMap) {
|
|
376
|
+
const data = fromJSON(nodeJSON.data) as StructPrimitive<any>;
|
|
377
|
+
const childTypes = nodeJSON.children;
|
|
378
|
+
|
|
379
|
+
const node = Primitive.TreeNode(nodeType, {
|
|
380
|
+
data,
|
|
381
|
+
children: () => childTypes.map((ct) => nodeMap.get(ct)!),
|
|
382
|
+
});
|
|
383
|
+
nodeMap.set(nodeType, node);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const root = nodeMap.get(treeJSON.root)!;
|
|
387
|
+
let p: TreePrimitive<any, any, any> = Primitive.Tree({ root });
|
|
388
|
+
if (obj.required) p = p.required();
|
|
389
|
+
if (obj.default !== undefined) p = p.default(obj.default);
|
|
390
|
+
return p;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
default:
|
|
394
|
+
throw new Error(`Unknown JSON schema type: ${obj.type}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
package/src/index.ts
CHANGED
package/src/primitives/Array.ts
CHANGED
|
@@ -173,18 +173,28 @@ export class ArrayPrimitive<TElement extends AnyPrimitive, TRequired extends boo
|
|
|
173
173
|
|
|
174
174
|
/** Minimum array length */
|
|
175
175
|
minLength(length: number): ArrayPrimitive<TElement, TRequired, THasDefault> {
|
|
176
|
-
return
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
return new ArrayPrimitive({
|
|
177
|
+
...this._schema,
|
|
178
|
+
validators: [...this._schema.validators, {
|
|
179
|
+
validate: (v) => v.length >= length,
|
|
180
|
+
message: `Array must have at least ${length} elements`,
|
|
181
|
+
kind: "minLength",
|
|
182
|
+
params: { value: length },
|
|
183
|
+
}],
|
|
184
|
+
});
|
|
180
185
|
}
|
|
181
186
|
|
|
182
187
|
/** Maximum array length */
|
|
183
188
|
maxLength(length: number): ArrayPrimitive<TElement, TRequired, THasDefault> {
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
return new ArrayPrimitive({
|
|
190
|
+
...this._schema,
|
|
191
|
+
validators: [...this._schema.validators, {
|
|
192
|
+
validate: (v) => v.length <= length,
|
|
193
|
+
message: `Array must have at most ${length} elements`,
|
|
194
|
+
kind: "maxLength",
|
|
195
|
+
params: { value: length },
|
|
196
|
+
}],
|
|
197
|
+
});
|
|
188
198
|
}
|
|
189
199
|
|
|
190
200
|
readonly _internal: PrimitiveInternal<ArrayState<TElement>, ArrayProxy<TElement>> = {
|
|
@@ -87,12 +87,12 @@ export class BooleanPrimitive<TRequired extends boolean = false, THasDefault ext
|
|
|
87
87
|
},
|
|
88
88
|
set: (value: InferSetInput<TRequired, THasDefault>) => {
|
|
89
89
|
env.addOperation(
|
|
90
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
90
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
91
91
|
);
|
|
92
92
|
},
|
|
93
93
|
update: (value: InferUpdateInput<TRequired, THasDefault>) => {
|
|
94
94
|
env.addOperation(
|
|
95
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
95
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
96
96
|
);
|
|
97
97
|
},
|
|
98
98
|
toSnapshot: (): MaybeUndefined<boolean, TRequired, THasDefault> => {
|
package/src/primitives/Either.ts
CHANGED
|
@@ -271,12 +271,12 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TRequ
|
|
|
271
271
|
},
|
|
272
272
|
set: (value: InferSetInput<TVariants, TRequired, THasDefault>) => {
|
|
273
273
|
env.addOperation(
|
|
274
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
274
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
275
275
|
);
|
|
276
276
|
},
|
|
277
277
|
update: (value: InferUpdateInput<TVariants, TRequired, THasDefault>) => {
|
|
278
278
|
env.addOperation(
|
|
279
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
279
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
280
280
|
);
|
|
281
281
|
},
|
|
282
282
|
match: <R,>(handlers: EitherMatchHandlers<R>): R | undefined => {
|
|
@@ -87,12 +87,12 @@ export class LiteralPrimitive<T extends LiteralValue, TRequired extends boolean
|
|
|
87
87
|
},
|
|
88
88
|
set: (value: InferSetInput<T, TRequired, THasDefault>) => {
|
|
89
89
|
env.addOperation(
|
|
90
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
90
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
91
91
|
);
|
|
92
92
|
},
|
|
93
93
|
update: (value: InferUpdateInput<T, TRequired, THasDefault>) => {
|
|
94
94
|
env.addOperation(
|
|
95
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
95
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
96
96
|
);
|
|
97
97
|
},
|
|
98
98
|
toSnapshot: (): MaybeUndefined<T, TRequired, THasDefault> => {
|
package/src/primitives/Number.ts
CHANGED
|
@@ -79,42 +79,64 @@ export class NumberPrimitive<TRequired extends boolean = false, THasDefault exte
|
|
|
79
79
|
|
|
80
80
|
/** Minimum value (inclusive) */
|
|
81
81
|
min(value: number): NumberPrimitive<TRequired, THasDefault> {
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
return new NumberPrimitive({
|
|
83
|
+
...this._schema,
|
|
84
|
+
validators: [...this._schema.validators, {
|
|
85
|
+
validate: (v) => v >= value,
|
|
86
|
+
message: `Number must be at least ${value}`,
|
|
87
|
+
kind: "min",
|
|
88
|
+
params: { value },
|
|
89
|
+
}],
|
|
90
|
+
});
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/** Maximum value (inclusive) */
|
|
89
94
|
max(value: number): NumberPrimitive<TRequired, THasDefault> {
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
return new NumberPrimitive({
|
|
96
|
+
...this._schema,
|
|
97
|
+
validators: [...this._schema.validators, {
|
|
98
|
+
validate: (v) => v <= value,
|
|
99
|
+
message: `Number must be at most ${value}`,
|
|
100
|
+
kind: "max",
|
|
101
|
+
params: { value },
|
|
102
|
+
}],
|
|
103
|
+
});
|
|
94
104
|
}
|
|
95
105
|
|
|
96
106
|
/** Must be positive (> 0) */
|
|
97
107
|
positive(): NumberPrimitive<TRequired, THasDefault> {
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
return new NumberPrimitive({
|
|
109
|
+
...this._schema,
|
|
110
|
+
validators: [...this._schema.validators, {
|
|
111
|
+
validate: (v) => v > 0,
|
|
112
|
+
message: "Number must be positive",
|
|
113
|
+
kind: "positive",
|
|
114
|
+
}],
|
|
115
|
+
});
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
/** Must be negative (< 0) */
|
|
105
119
|
negative(): NumberPrimitive<TRequired, THasDefault> {
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
return new NumberPrimitive({
|
|
121
|
+
...this._schema,
|
|
122
|
+
validators: [...this._schema.validators, {
|
|
123
|
+
validate: (v) => v < 0,
|
|
124
|
+
message: "Number must be negative",
|
|
125
|
+
kind: "negative",
|
|
126
|
+
}],
|
|
127
|
+
});
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
/** Must be an integer */
|
|
113
131
|
int(): NumberPrimitive<TRequired, THasDefault> {
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
return new NumberPrimitive({
|
|
133
|
+
...this._schema,
|
|
134
|
+
validators: [...this._schema.validators, {
|
|
135
|
+
validate: (v) => globalThis.Number.isInteger(v),
|
|
136
|
+
message: "Number must be an integer",
|
|
137
|
+
kind: "int",
|
|
138
|
+
}],
|
|
139
|
+
});
|
|
118
140
|
}
|
|
119
141
|
|
|
120
142
|
readonly _internal: PrimitiveInternal<number, NumberProxy<TRequired, THasDefault>> = {
|
|
@@ -127,12 +149,12 @@ export class NumberPrimitive<TRequired extends boolean = false, THasDefault exte
|
|
|
127
149
|
},
|
|
128
150
|
set: (value: InferSetInput<TRequired, THasDefault>) => {
|
|
129
151
|
env.addOperation(
|
|
130
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
152
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
131
153
|
);
|
|
132
154
|
},
|
|
133
155
|
update: (value: InferUpdateInput<TRequired, THasDefault>) => {
|
|
134
156
|
env.addOperation(
|
|
135
|
-
Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
|
|
157
|
+
Operation.fromDefinition(operationPath, this._opDefinitions.set as any, value)
|
|
136
158
|
);
|
|
137
159
|
},
|
|
138
160
|
toSnapshot: (): MaybeUndefined<number, TRequired, THasDefault> => {
|