@voidhash/mimic 0.0.1-alpha.9 → 0.0.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.
- package/.turbo/turbo-build.log +23 -23
- package/dist/Presence-DKKP4v5X.d.cts.map +1 -1
- package/dist/{Primitive-Cyvy7zvF.d.cts → Primitive-CvFVxR8_.d.cts} +2 -2
- package/dist/{Primitive-Cyvy7zvF.d.cts.map → Primitive-CvFVxR8_.d.cts.map} +1 -1
- package/dist/{Primitive-CasheIbX.d.mts → Primitive-lEhQyGVL.d.mts} +2 -2
- package/dist/{Primitive-CasheIbX.d.mts.map → Primitive-lEhQyGVL.d.mts.map} +1 -1
- package/dist/client/index.d.cts +1 -1
- package/dist/client/index.d.cts.map +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/index.cjs +177 -0
- package/dist/index.d.cts +79 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +79 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +172 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.cts +1 -1
- package/dist/server/index.d.cts.map +1 -1
- package/dist/server/index.d.mts +1 -1
- package/package.json +2 -2
- package/src/EffectSchema.ts +374 -0
- package/src/index.ts +1 -0
- package/tests/EffectSchema.test.ts +546 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
import * as EffectSchema from "../src/EffectSchema";
|
|
4
|
+
import * as Primitive from "../src/Primitive";
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Simple Primitive Tests
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
describe("EffectSchema - Simple Primitives", () => {
|
|
11
|
+
describe("toSetSchema", () => {
|
|
12
|
+
it("converts StringPrimitive to Schema.String", () => {
|
|
13
|
+
const primitive = Primitive.String();
|
|
14
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
15
|
+
|
|
16
|
+
// Verify it accepts valid strings
|
|
17
|
+
expect(Schema.decodeUnknownSync(schema)("hello")).toBe("hello");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("converts NumberPrimitive to Schema.Number", () => {
|
|
21
|
+
const primitive = Primitive.Number();
|
|
22
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
23
|
+
|
|
24
|
+
expect(Schema.decodeUnknownSync(schema)(42)).toBe(42);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("converts BooleanPrimitive to Schema.Boolean", () => {
|
|
28
|
+
const primitive = Primitive.Boolean();
|
|
29
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
30
|
+
|
|
31
|
+
expect(Schema.decodeUnknownSync(schema)(true)).toBe(true);
|
|
32
|
+
expect(Schema.decodeUnknownSync(schema)(false)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("converts LiteralPrimitive to Schema.Literal", () => {
|
|
36
|
+
const primitive = Primitive.Literal("active");
|
|
37
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
38
|
+
|
|
39
|
+
expect(Schema.decodeUnknownSync(schema)("active")).toBe("active");
|
|
40
|
+
|
|
41
|
+
// Should reject non-matching literals
|
|
42
|
+
expect(() => Schema.decodeUnknownSync(schema)("inactive")).toThrow();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("toUpdateSchema", () => {
|
|
47
|
+
it("update schema for simple primitives is same as set schema", () => {
|
|
48
|
+
const stringPrimitive = Primitive.String();
|
|
49
|
+
const setSchema = EffectSchema.toSetSchema(stringPrimitive);
|
|
50
|
+
const updateSchema = EffectSchema.toUpdateSchema(stringPrimitive);
|
|
51
|
+
|
|
52
|
+
// Both should accept strings
|
|
53
|
+
expect(Schema.decodeUnknownSync(setSchema)("test")).toBe("test");
|
|
54
|
+
expect(Schema.decodeUnknownSync(updateSchema)("test")).toBe("test");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Struct Primitive Tests
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
describe("EffectSchema - Struct Primitives", () => {
|
|
64
|
+
describe("toSetSchema", () => {
|
|
65
|
+
it("required fields are non-optional", () => {
|
|
66
|
+
const primitive = Primitive.Struct({
|
|
67
|
+
name: Primitive.String().required(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
71
|
+
|
|
72
|
+
// Should accept object with required field
|
|
73
|
+
expect(Schema.decodeUnknownSync(schema)({ name: "Alice" })).toEqual({ name: "Alice" });
|
|
74
|
+
|
|
75
|
+
// Should reject missing required field
|
|
76
|
+
expect(() => Schema.decodeUnknownSync(schema)({})).toThrow();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("fields with defaults are optional", () => {
|
|
80
|
+
const primitive = Primitive.Struct({
|
|
81
|
+
name: Primitive.String().default("default"),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
85
|
+
|
|
86
|
+
// Should accept object with field
|
|
87
|
+
expect(Schema.decodeUnknownSync(schema)({ name: "Alice" })).toEqual({ name: "Alice" });
|
|
88
|
+
|
|
89
|
+
// Should accept object without field (field is optional)
|
|
90
|
+
expect(Schema.decodeUnknownSync(schema)({})).toEqual({});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("non-required fields are optional", () => {
|
|
94
|
+
const primitive = Primitive.Struct({
|
|
95
|
+
email: Primitive.String(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
99
|
+
|
|
100
|
+
// Should accept object with field
|
|
101
|
+
expect(Schema.decodeUnknownSync(schema)({ email: "test@example.com" })).toEqual({ email: "test@example.com" });
|
|
102
|
+
|
|
103
|
+
// Should accept object without field
|
|
104
|
+
expect(Schema.decodeUnknownSync(schema)({})).toEqual({});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("handles mixed required/optional fields", () => {
|
|
108
|
+
const primitive = Primitive.Struct({
|
|
109
|
+
name: Primitive.String().required(),
|
|
110
|
+
age: Primitive.Number().default(0),
|
|
111
|
+
email: Primitive.String(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
115
|
+
|
|
116
|
+
// Should accept with only required field
|
|
117
|
+
expect(Schema.decodeUnknownSync(schema)({ name: "Alice" })).toEqual({ name: "Alice" });
|
|
118
|
+
|
|
119
|
+
// Should accept with all fields
|
|
120
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
121
|
+
name: "Alice",
|
|
122
|
+
age: 30,
|
|
123
|
+
email: "alice@example.com"
|
|
124
|
+
})).toEqual({
|
|
125
|
+
name: "Alice",
|
|
126
|
+
age: 30,
|
|
127
|
+
email: "alice@example.com"
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Should reject missing required field
|
|
131
|
+
expect(() => Schema.decodeUnknownSync(schema)({ age: 30 })).toThrow();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("toUpdateSchema", () => {
|
|
136
|
+
it("all fields are optional for updates", () => {
|
|
137
|
+
const primitive = Primitive.Struct({
|
|
138
|
+
name: Primitive.String().required(),
|
|
139
|
+
age: Primitive.Number().default(0),
|
|
140
|
+
email: Primitive.String(),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const schema = EffectSchema.toUpdateSchema(primitive);
|
|
144
|
+
|
|
145
|
+
// Should accept empty object
|
|
146
|
+
expect(Schema.decodeUnknownSync(schema)({})).toEqual({});
|
|
147
|
+
|
|
148
|
+
// Should accept partial updates
|
|
149
|
+
expect(Schema.decodeUnknownSync(schema)({ name: "Alice" })).toEqual({ name: "Alice" });
|
|
150
|
+
expect(Schema.decodeUnknownSync(schema)({ age: 30 })).toEqual({ age: 30 });
|
|
151
|
+
|
|
152
|
+
// Should accept full object
|
|
153
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
154
|
+
name: "Alice",
|
|
155
|
+
age: 30,
|
|
156
|
+
email: "alice@example.com"
|
|
157
|
+
})).toEqual({
|
|
158
|
+
name: "Alice",
|
|
159
|
+
age: 30,
|
|
160
|
+
email: "alice@example.com"
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// =============================================================================
|
|
167
|
+
// Nested Struct Tests
|
|
168
|
+
// =============================================================================
|
|
169
|
+
|
|
170
|
+
describe("EffectSchema - Nested Structs", () => {
|
|
171
|
+
describe("toSetSchema", () => {
|
|
172
|
+
it("handles nested struct with required fields", () => {
|
|
173
|
+
const primitive = Primitive.Struct({
|
|
174
|
+
user: Primitive.Struct({
|
|
175
|
+
name: Primitive.String().required(),
|
|
176
|
+
age: Primitive.Number(),
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
181
|
+
|
|
182
|
+
// Should accept valid nested structure
|
|
183
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
184
|
+
user: { name: "Alice" }
|
|
185
|
+
})).toEqual({
|
|
186
|
+
user: { name: "Alice" }
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Should reject missing required nested field
|
|
190
|
+
expect(() => Schema.decodeUnknownSync(schema)({ user: {} })).toThrow();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("toUpdateSchema", () => {
|
|
195
|
+
it("nested struct fields are also optional for updates", () => {
|
|
196
|
+
const primitive = Primitive.Struct({
|
|
197
|
+
user: Primitive.Struct({
|
|
198
|
+
name: Primitive.String().required(),
|
|
199
|
+
age: Primitive.Number(),
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const schema = EffectSchema.toUpdateSchema(primitive);
|
|
204
|
+
|
|
205
|
+
// Should accept empty object
|
|
206
|
+
expect(Schema.decodeUnknownSync(schema)({})).toEqual({});
|
|
207
|
+
|
|
208
|
+
// Should accept partial nested update
|
|
209
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
210
|
+
user: { name: "Alice" }
|
|
211
|
+
})).toEqual({
|
|
212
|
+
user: { name: "Alice" }
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Should accept nested struct with empty object (all fields optional)
|
|
216
|
+
expect(Schema.decodeUnknownSync(schema)({ user: {} })).toEqual({ user: {} });
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// =============================================================================
|
|
222
|
+
// Array Primitive Tests
|
|
223
|
+
// =============================================================================
|
|
224
|
+
|
|
225
|
+
describe("EffectSchema - Array Primitives", () => {
|
|
226
|
+
describe("toSetSchema", () => {
|
|
227
|
+
it("converts simple array to Schema.Array", () => {
|
|
228
|
+
const primitive = Primitive.Array(Primitive.String());
|
|
229
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
230
|
+
|
|
231
|
+
expect(Schema.decodeUnknownSync(schema)(["a", "b", "c"])).toEqual(["a", "b", "c"]);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("converts array of structs with proper field handling", () => {
|
|
235
|
+
const primitive = Primitive.Array(
|
|
236
|
+
Primitive.Struct({
|
|
237
|
+
name: Primitive.String().required(),
|
|
238
|
+
age: Primitive.Number(),
|
|
239
|
+
})
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
243
|
+
|
|
244
|
+
// Should accept array with valid elements
|
|
245
|
+
expect(Schema.decodeUnknownSync(schema)([
|
|
246
|
+
{ name: "Alice" },
|
|
247
|
+
{ name: "Bob", age: 30 },
|
|
248
|
+
])).toEqual([
|
|
249
|
+
{ name: "Alice" },
|
|
250
|
+
{ name: "Bob", age: 30 },
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
// Should reject element missing required field
|
|
254
|
+
expect(() => Schema.decodeUnknownSync(schema)([{ age: 30 }])).toThrow();
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// =============================================================================
|
|
260
|
+
// Union Primitive Tests
|
|
261
|
+
// =============================================================================
|
|
262
|
+
|
|
263
|
+
describe("EffectSchema - Union Primitives", () => {
|
|
264
|
+
describe("toSetSchema", () => {
|
|
265
|
+
it("creates union schema for variants", () => {
|
|
266
|
+
const primitive = Primitive.Union({
|
|
267
|
+
variants: {
|
|
268
|
+
text: Primitive.Struct({
|
|
269
|
+
type: Primitive.Literal("text"),
|
|
270
|
+
content: Primitive.String().required(),
|
|
271
|
+
}),
|
|
272
|
+
image: Primitive.Struct({
|
|
273
|
+
type: Primitive.Literal("image"),
|
|
274
|
+
url: Primitive.String().required(),
|
|
275
|
+
}),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
280
|
+
|
|
281
|
+
// Should accept text variant
|
|
282
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
283
|
+
type: "text",
|
|
284
|
+
content: "Hello"
|
|
285
|
+
})).toEqual({
|
|
286
|
+
type: "text",
|
|
287
|
+
content: "Hello"
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Should accept image variant
|
|
291
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
292
|
+
type: "image",
|
|
293
|
+
url: "https://example.com/image.png"
|
|
294
|
+
})).toEqual({
|
|
295
|
+
type: "image",
|
|
296
|
+
url: "https://example.com/image.png"
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe("toUpdateSchema", () => {
|
|
302
|
+
it("all variant fields are optional for updates", () => {
|
|
303
|
+
const primitive = Primitive.Union({
|
|
304
|
+
variants: {
|
|
305
|
+
text: Primitive.Struct({
|
|
306
|
+
type: Primitive.Literal("text"),
|
|
307
|
+
content: Primitive.String().required(),
|
|
308
|
+
}),
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const schema = EffectSchema.toUpdateSchema(primitive);
|
|
313
|
+
|
|
314
|
+
// Should accept partial variant
|
|
315
|
+
expect(Schema.decodeUnknownSync(schema)({ type: "text" })).toEqual({ type: "text" });
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// =============================================================================
|
|
321
|
+
// Either Primitive Tests
|
|
322
|
+
// =============================================================================
|
|
323
|
+
|
|
324
|
+
describe("EffectSchema - Either Primitives", () => {
|
|
325
|
+
describe("toSetSchema", () => {
|
|
326
|
+
it("creates union of scalar types", () => {
|
|
327
|
+
const primitive = Primitive.Either(
|
|
328
|
+
Primitive.String(),
|
|
329
|
+
Primitive.Number()
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const schema = EffectSchema.toSetSchema(primitive);
|
|
333
|
+
|
|
334
|
+
// Should accept string
|
|
335
|
+
expect(Schema.decodeUnknownSync(schema)("hello")).toBe("hello");
|
|
336
|
+
|
|
337
|
+
// Should accept number
|
|
338
|
+
expect(Schema.decodeUnknownSync(schema)(42)).toBe(42);
|
|
339
|
+
|
|
340
|
+
// Should reject non-matching types
|
|
341
|
+
expect(() => Schema.decodeUnknownSync(schema)(true)).toThrow();
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// =============================================================================
|
|
347
|
+
// TreeNode Primitive Tests
|
|
348
|
+
// =============================================================================
|
|
349
|
+
|
|
350
|
+
describe("EffectSchema - TreeNode Primitives", () => {
|
|
351
|
+
describe("toSetSchema", () => {
|
|
352
|
+
it("delegates to data struct for set schema", () => {
|
|
353
|
+
const CardNode = Primitive.TreeNode("card", {
|
|
354
|
+
data: Primitive.Struct({
|
|
355
|
+
title: Primitive.String().required(),
|
|
356
|
+
description: Primitive.String(),
|
|
357
|
+
}),
|
|
358
|
+
children: [Primitive.TreeNodeSelf],
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const schema = EffectSchema.toSetSchema(CardNode);
|
|
362
|
+
|
|
363
|
+
// Should accept valid data with required field
|
|
364
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
365
|
+
title: "My Card"
|
|
366
|
+
})).toEqual({
|
|
367
|
+
title: "My Card"
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Should accept valid data with all fields
|
|
371
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
372
|
+
title: "My Card",
|
|
373
|
+
description: "Card description"
|
|
374
|
+
})).toEqual({
|
|
375
|
+
title: "My Card",
|
|
376
|
+
description: "Card description"
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Should reject missing required field
|
|
380
|
+
expect(() => Schema.decodeUnknownSync(schema)({})).toThrow();
|
|
381
|
+
expect(() => Schema.decodeUnknownSync(schema)({ description: "no title" })).toThrow();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("toUpdateSchema", () => {
|
|
386
|
+
it("all data fields are optional for updates", () => {
|
|
387
|
+
const CardNode = Primitive.TreeNode("card", {
|
|
388
|
+
data: Primitive.Struct({
|
|
389
|
+
title: Primitive.String().required(),
|
|
390
|
+
description: Primitive.String(),
|
|
391
|
+
}),
|
|
392
|
+
children: [Primitive.TreeNodeSelf],
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const schema = EffectSchema.toUpdateSchema(CardNode);
|
|
396
|
+
|
|
397
|
+
// Should accept empty update
|
|
398
|
+
expect(Schema.decodeUnknownSync(schema)({})).toEqual({});
|
|
399
|
+
|
|
400
|
+
// Should accept partial update
|
|
401
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
402
|
+
title: "Updated Title"
|
|
403
|
+
})).toEqual({
|
|
404
|
+
title: "Updated Title"
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Should accept update with only optional field
|
|
408
|
+
expect(Schema.decodeUnknownSync(schema)({
|
|
409
|
+
description: "New description"
|
|
410
|
+
})).toEqual({
|
|
411
|
+
description: "New description"
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// =============================================================================
|
|
418
|
+
// Tree Primitive Tests
|
|
419
|
+
// =============================================================================
|
|
420
|
+
|
|
421
|
+
describe("EffectSchema - Tree Primitives", () => {
|
|
422
|
+
describe("toSetSchema", () => {
|
|
423
|
+
it("returns array of TreeNodeState schema", () => {
|
|
424
|
+
const FolderNode = Primitive.TreeNode("folder", {
|
|
425
|
+
data: Primitive.Struct({
|
|
426
|
+
name: Primitive.String().required(),
|
|
427
|
+
}),
|
|
428
|
+
children: [Primitive.TreeNodeSelf],
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const treePrimitive = Primitive.Tree({
|
|
432
|
+
root: FolderNode,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const schema = EffectSchema.toSetSchema(treePrimitive);
|
|
436
|
+
|
|
437
|
+
// Should accept valid tree state
|
|
438
|
+
expect(Schema.decodeUnknownSync(schema)([
|
|
439
|
+
{
|
|
440
|
+
id: "node-1",
|
|
441
|
+
type: "folder",
|
|
442
|
+
parentId: null,
|
|
443
|
+
pos: "a0",
|
|
444
|
+
data: { name: "Root" },
|
|
445
|
+
},
|
|
446
|
+
])).toEqual([
|
|
447
|
+
{
|
|
448
|
+
id: "node-1",
|
|
449
|
+
type: "folder",
|
|
450
|
+
parentId: null,
|
|
451
|
+
pos: "a0",
|
|
452
|
+
data: { name: "Root" },
|
|
453
|
+
},
|
|
454
|
+
]);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// =============================================================================
|
|
460
|
+
// TreeNodeStateSchema Export Tests
|
|
461
|
+
// =============================================================================
|
|
462
|
+
|
|
463
|
+
describe("EffectSchema - TreeNodeStateSchema", () => {
|
|
464
|
+
it("validates tree node state structure", () => {
|
|
465
|
+
const validNodeState = {
|
|
466
|
+
id: "node-123",
|
|
467
|
+
type: "card",
|
|
468
|
+
parentId: "parent-456",
|
|
469
|
+
pos: "a0",
|
|
470
|
+
data: { title: "Test" },
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
expect(Schema.decodeUnknownSync(EffectSchema.TreeNodeStateSchema)(validNodeState)).toEqual(validNodeState);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("accepts null parentId for root nodes", () => {
|
|
477
|
+
const rootNodeState = {
|
|
478
|
+
id: "root-1",
|
|
479
|
+
type: "folder",
|
|
480
|
+
parentId: null,
|
|
481
|
+
pos: "a0",
|
|
482
|
+
data: { name: "Root" },
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
expect(Schema.decodeUnknownSync(EffectSchema.TreeNodeStateSchema)(rootNodeState)).toEqual(rootNodeState);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("rejects invalid node state", () => {
|
|
489
|
+
const invalidNodeState = {
|
|
490
|
+
id: 123, // should be string
|
|
491
|
+
type: "card",
|
|
492
|
+
parentId: null,
|
|
493
|
+
pos: "a0",
|
|
494
|
+
data: {},
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
expect(() => Schema.decodeUnknownSync(EffectSchema.TreeNodeStateSchema)(invalidNodeState)).toThrow();
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// =============================================================================
|
|
502
|
+
// Complex Example Tests
|
|
503
|
+
// =============================================================================
|
|
504
|
+
|
|
505
|
+
describe("EffectSchema - Complex Examples", () => {
|
|
506
|
+
it("handles the example schema from the plan", () => {
|
|
507
|
+
const UserSchema = Primitive.Struct({
|
|
508
|
+
name: Primitive.String().required(),
|
|
509
|
+
age: Primitive.Number().default(0),
|
|
510
|
+
email: Primitive.String(),
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const setSchema = EffectSchema.toSetSchema(UserSchema);
|
|
514
|
+
const updateSchema = EffectSchema.toUpdateSchema(UserSchema);
|
|
515
|
+
|
|
516
|
+
// Set schema: name required, others optional
|
|
517
|
+
expect(Schema.decodeUnknownSync(setSchema)({ name: "Alice" })).toEqual({ name: "Alice" });
|
|
518
|
+
expect(() => Schema.decodeUnknownSync(setSchema)({})).toThrow();
|
|
519
|
+
|
|
520
|
+
// Update schema: all optional
|
|
521
|
+
expect(Schema.decodeUnknownSync(updateSchema)({})).toEqual({});
|
|
522
|
+
expect(Schema.decodeUnknownSync(updateSchema)({ age: 30 })).toEqual({ age: 30 });
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("handles the TreeNode example from the plan", () => {
|
|
526
|
+
const CardNode = Primitive.TreeNode("card", {
|
|
527
|
+
data: Primitive.Struct({
|
|
528
|
+
title: Primitive.String().required(),
|
|
529
|
+
description: Primitive.String(),
|
|
530
|
+
}),
|
|
531
|
+
children: [Primitive.TreeNodeSelf],
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const setSchema = EffectSchema.toSetSchema(CardNode);
|
|
535
|
+
const updateSchema = EffectSchema.toUpdateSchema(CardNode);
|
|
536
|
+
|
|
537
|
+
// Set schema: title required, description optional
|
|
538
|
+
expect(Schema.decodeUnknownSync(setSchema)({ title: "Test" })).toEqual({ title: "Test" });
|
|
539
|
+
expect(() => Schema.decodeUnknownSync(setSchema)({})).toThrow();
|
|
540
|
+
|
|
541
|
+
// Update schema: all optional
|
|
542
|
+
expect(Schema.decodeUnknownSync(updateSchema)({})).toEqual({});
|
|
543
|
+
expect(Schema.decodeUnknownSync(updateSchema)({ description: "Updated" })).toEqual({ description: "Updated" });
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|