@voidhash/mimic 0.0.1-alpha.1 → 0.0.1-alpha.10

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.
Files changed (63) hide show
  1. package/.turbo/turbo-build.log +51 -0
  2. package/LICENSE.md +663 -0
  3. package/dist/Document-ChuFrTk1.cjs +571 -0
  4. package/dist/Document-CwiAFTIq.mjs +438 -0
  5. package/dist/Document-CwiAFTIq.mjs.map +1 -0
  6. package/dist/Presence-DKKP4v5X.d.cts +91 -0
  7. package/dist/Presence-DKKP4v5X.d.cts.map +1 -0
  8. package/dist/Presence-DdMVKcOv.mjs +110 -0
  9. package/dist/Presence-DdMVKcOv.mjs.map +1 -0
  10. package/dist/Presence-N8u7Eppr.d.mts +91 -0
  11. package/dist/Presence-N8u7Eppr.d.mts.map +1 -0
  12. package/dist/Presence-gWrmGBeu.cjs +126 -0
  13. package/dist/Primitive-CvFVxR8_.d.cts +1175 -0
  14. package/dist/Primitive-CvFVxR8_.d.cts.map +1 -0
  15. package/dist/Primitive-lEhQyGVL.d.mts +1175 -0
  16. package/dist/Primitive-lEhQyGVL.d.mts.map +1 -0
  17. package/dist/chunk-CLMFDpHK.mjs +18 -0
  18. package/dist/client/index.cjs +1456 -0
  19. package/dist/client/index.d.cts +692 -0
  20. package/dist/client/index.d.cts.map +1 -0
  21. package/dist/client/index.d.mts +692 -0
  22. package/dist/client/index.d.mts.map +1 -0
  23. package/dist/client/index.mjs +1413 -0
  24. package/dist/client/index.mjs.map +1 -0
  25. package/dist/index.cjs +2577 -0
  26. package/dist/index.d.cts +143 -0
  27. package/dist/index.d.cts.map +1 -0
  28. package/dist/index.d.mts +143 -0
  29. package/dist/index.d.mts.map +1 -0
  30. package/dist/index.mjs +2526 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/server/index.cjs +191 -0
  33. package/dist/server/index.d.cts +148 -0
  34. package/dist/server/index.d.cts.map +1 -0
  35. package/dist/server/index.d.mts +148 -0
  36. package/dist/server/index.d.mts.map +1 -0
  37. package/dist/server/index.mjs +182 -0
  38. package/dist/server/index.mjs.map +1 -0
  39. package/package.json +25 -13
  40. package/src/EffectSchema.ts +374 -0
  41. package/src/Primitive.ts +3 -0
  42. package/src/client/ClientDocument.ts +1 -1
  43. package/src/client/errors.ts +10 -10
  44. package/src/index.ts +1 -0
  45. package/src/primitives/Array.ts +57 -22
  46. package/src/primitives/Boolean.ts +33 -19
  47. package/src/primitives/Either.ts +379 -0
  48. package/src/primitives/Lazy.ts +16 -2
  49. package/src/primitives/Literal.ts +33 -20
  50. package/src/primitives/Number.ts +39 -26
  51. package/src/primitives/String.ts +40 -25
  52. package/src/primitives/Struct.ts +126 -29
  53. package/src/primitives/Tree.ts +119 -32
  54. package/src/primitives/TreeNode.ts +77 -30
  55. package/src/primitives/Union.ts +56 -29
  56. package/src/primitives/shared.ts +111 -9
  57. package/src/server/errors.ts +6 -6
  58. package/tests/EffectSchema.test.ts +546 -0
  59. package/tests/primitives/Array.test.ts +108 -0
  60. package/tests/primitives/Either.test.ts +707 -0
  61. package/tests/primitives/Struct.test.ts +250 -0
  62. package/tests/primitives/Tree.test.ts +250 -0
  63. package/tsdown.config.ts +1 -1
@@ -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
+
@@ -411,6 +411,114 @@ describe("ArrayPrimitive", () => {
411
411
  expect(positions[1]! < positions[2]!).toBe(true);
412
412
  });
413
413
  });
414
+ describe("struct elements with defaults", () => {
415
+ // Define a struct element with required and optional fields
416
+ const TaskStruct = Primitive.Struct({
417
+ title: Primitive.String().required(), // Must provide
418
+ priority: Primitive.Number().default(0), // Has default, optional
419
+ completed: Primitive.Boolean().default(false), // Has default, optional
420
+ });
421
+
422
+ const taskArray = Primitive.Array(TaskStruct);
423
+
424
+ // Helper to create environment for task array
425
+ const createTaskEnv = (
426
+ state: Primitive.ArrayEntry<{ title: string; priority: number; completed: boolean }>[] = []
427
+ ): { env: ReturnType<typeof ProxyEnvironment.make>; operations: Operation.Operation<any, any, any>[] } => {
428
+ const operations: Operation.Operation<any, any, any>[] = [];
429
+ let currentState = [...state];
430
+ let idCounter = 0;
431
+
432
+ const env = ProxyEnvironment.make({
433
+ onOperation: (op) => {
434
+ operations.push(op);
435
+ if (op.kind === "array.insert") {
436
+ currentState.push(op.payload);
437
+ } else if (op.kind === "array.set") {
438
+ currentState = op.payload;
439
+ }
440
+ },
441
+ getState: () => currentState,
442
+ generateId: () => `task-${++idCounter}`,
443
+ });
444
+
445
+ return { env, operations };
446
+ };
447
+
448
+ it("push() only requires fields without defaults", () => {
449
+ const { env, operations } = createTaskEnv();
450
+ const proxy = taskArray._internal.createProxy(env, OperationPath.make(""));
451
+
452
+ // Only provide required 'title', priority and completed should use defaults
453
+ proxy.push({ title: "New Task" });
454
+
455
+ expect(operations).toHaveLength(1);
456
+ expect(operations[0]!.kind).toBe("array.insert");
457
+
458
+ const payload = operations[0]!.payload as { value: { title: string; priority: number; completed: boolean } };
459
+ expect(payload.value.title).toBe("New Task");
460
+ expect(payload.value.priority).toBe(0); // Default value
461
+ expect(payload.value.completed).toBe(false); // Default value
462
+ });
463
+
464
+ it("push() allows overriding defaults", () => {
465
+ const { env, operations } = createTaskEnv();
466
+ const proxy = taskArray._internal.createProxy(env, OperationPath.make(""));
467
+
468
+ // Provide title and override priority, let completed use default
469
+ proxy.push({ title: "Important Task", priority: 10 });
470
+
471
+ const payload = operations[0]!.payload as { value: { title: string; priority: number; completed: boolean } };
472
+ expect(payload.value.title).toBe("Important Task");
473
+ expect(payload.value.priority).toBe(10); // Overridden
474
+ expect(payload.value.completed).toBe(false); // Default value
475
+ });
476
+
477
+ it("insertAt() applies defaults for omitted fields", () => {
478
+ const existingEntry = {
479
+ id: "existing",
480
+ pos: "a0",
481
+ value: { title: "Existing", priority: 5, completed: true },
482
+ };
483
+ const { env, operations } = createTaskEnv([existingEntry]);
484
+ const proxy = taskArray._internal.createProxy(env, OperationPath.make(""));
485
+
486
+ // Insert with only required field
487
+ proxy.insertAt(0, { title: "First Task" });
488
+
489
+ const payload = operations[0]!.payload as { value: { title: string; priority: number; completed: boolean } };
490
+ expect(payload.value.title).toBe("First Task");
491
+ expect(payload.value.priority).toBe(0); // Default
492
+ expect(payload.value.completed).toBe(false); // Default
493
+ });
494
+
495
+ it("set() applies defaults to each element", () => {
496
+ const { env, operations } = createTaskEnv();
497
+ const proxy = taskArray._internal.createProxy(env, OperationPath.make(""));
498
+
499
+ // Set array with items that only have required fields
500
+ proxy.set([
501
+ { title: "Task 1" },
502
+ { title: "Task 2", priority: 5 },
503
+ { title: "Task 3", completed: true },
504
+ ]);
505
+
506
+ expect(operations).toHaveLength(1);
507
+ expect(operations[0]!.kind).toBe("array.set");
508
+
509
+ const entries = operations[0]!.payload as { value: { title: string; priority: number; completed: boolean } }[];
510
+ expect(entries).toHaveLength(3);
511
+
512
+ // First item: only title, defaults for others
513
+ expect(entries[0]!.value).toEqual({ title: "Task 1", priority: 0, completed: false });
514
+
515
+ // Second item: title and priority, default for completed
516
+ expect(entries[1]!.value).toEqual({ title: "Task 2", priority: 5, completed: false });
517
+
518
+ // Third item: title and completed, default for priority
519
+ expect(entries[2]!.value).toEqual({ title: "Task 3", priority: 0, completed: true });
520
+ });
521
+ });
414
522
  });
415
523
 
416
524
  // =============================================================================