@voidhash/mimic 1.0.0-beta.16 → 1.0.0-beta.18

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 (95) hide show
  1. package/dist/EffectSchema.cjs +3 -3
  2. package/dist/EffectSchema.d.cts +5 -5
  3. package/dist/EffectSchema.d.cts.map +1 -1
  4. package/dist/EffectSchema.d.mts +5 -5
  5. package/dist/EffectSchema.d.mts.map +1 -1
  6. package/dist/EffectSchema.mjs +3 -3
  7. package/dist/EffectSchema.mjs.map +1 -1
  8. package/dist/FractionalIndex.mjs.map +1 -1
  9. package/dist/Operation.d.cts +4 -4
  10. package/dist/Operation.d.cts.map +1 -1
  11. package/dist/Operation.d.mts +4 -4
  12. package/dist/Operation.d.mts.map +1 -1
  13. package/dist/Operation.mjs.map +1 -1
  14. package/dist/OperationDefinition.d.cts +2 -2
  15. package/dist/OperationDefinition.d.cts.map +1 -1
  16. package/dist/OperationDefinition.d.mts +2 -2
  17. package/dist/OperationDefinition.d.mts.map +1 -1
  18. package/dist/OperationDefinition.mjs.map +1 -1
  19. package/dist/Presence.mjs.map +1 -1
  20. package/dist/SchemaJSON.cjs +305 -0
  21. package/dist/SchemaJSON.d.cts +11 -0
  22. package/dist/SchemaJSON.d.cts.map +1 -0
  23. package/dist/SchemaJSON.d.mts +11 -0
  24. package/dist/SchemaJSON.d.mts.map +1 -0
  25. package/dist/SchemaJSON.mjs +301 -0
  26. package/dist/SchemaJSON.mjs.map +1 -0
  27. package/dist/index.cjs +7 -0
  28. package/dist/index.d.cts +2 -1
  29. package/dist/index.d.mts +2 -1
  30. package/dist/index.mjs +2 -1
  31. package/dist/primitives/Array.cjs +12 -2
  32. package/dist/primitives/Array.d.cts.map +1 -1
  33. package/dist/primitives/Array.d.mts.map +1 -1
  34. package/dist/primitives/Array.mjs +12 -2
  35. package/dist/primitives/Array.mjs.map +1 -1
  36. package/dist/primitives/Boolean.mjs.map +1 -1
  37. package/dist/primitives/Either.mjs.map +1 -1
  38. package/dist/primitives/Literal.mjs.map +1 -1
  39. package/dist/primitives/Number.cjs +27 -5
  40. package/dist/primitives/Number.d.cts.map +1 -1
  41. package/dist/primitives/Number.d.mts.map +1 -1
  42. package/dist/primitives/Number.mjs +27 -5
  43. package/dist/primitives/Number.mjs.map +1 -1
  44. package/dist/primitives/String.cjs +44 -13
  45. package/dist/primitives/String.d.cts.map +1 -1
  46. package/dist/primitives/String.d.mts.map +1 -1
  47. package/dist/primitives/String.mjs +44 -13
  48. package/dist/primitives/String.mjs.map +1 -1
  49. package/dist/primitives/Union.mjs.map +1 -1
  50. package/dist/primitives/shared.d.cts +2 -0
  51. package/dist/primitives/shared.d.cts.map +1 -1
  52. package/dist/primitives/shared.d.mts +2 -0
  53. package/dist/primitives/shared.d.mts.map +1 -1
  54. package/dist/primitives/shared.mjs.map +1 -1
  55. package/package.json +15 -8
  56. package/src/EffectSchema.ts +3 -3
  57. package/src/FractionalIndex.ts +18 -18
  58. package/src/Operation.ts +5 -5
  59. package/src/OperationDefinition.ts +2 -2
  60. package/src/Presence.ts +3 -3
  61. package/src/SchemaJSON.ts +396 -0
  62. package/src/index.ts +1 -0
  63. package/src/primitives/Array.ts +18 -8
  64. package/src/primitives/Boolean.ts +2 -2
  65. package/src/primitives/Either.ts +2 -2
  66. package/src/primitives/Literal.ts +2 -2
  67. package/src/primitives/Number.ts +44 -22
  68. package/src/primitives/String.ts +61 -34
  69. package/src/primitives/Union.ts +1 -1
  70. package/src/primitives/shared.ts +2 -0
  71. package/.turbo/turbo-build.log +0 -270
  72. package/tests/Document.test.ts +0 -557
  73. package/tests/EffectSchema.test.ts +0 -546
  74. package/tests/FractionalIndex.test.ts +0 -377
  75. package/tests/OperationPath.test.ts +0 -151
  76. package/tests/Presence.test.ts +0 -321
  77. package/tests/Primitive.test.ts +0 -381
  78. package/tests/client/ClientDocument.test.ts +0 -1981
  79. package/tests/client/WebSocketTransport.test.ts +0 -1217
  80. package/tests/primitives/Array.test.ts +0 -526
  81. package/tests/primitives/Boolean.test.ts +0 -126
  82. package/tests/primitives/Either.test.ts +0 -707
  83. package/tests/primitives/Lazy.test.ts +0 -143
  84. package/tests/primitives/Literal.test.ts +0 -122
  85. package/tests/primitives/Number.test.ts +0 -133
  86. package/tests/primitives/String.test.ts +0 -128
  87. package/tests/primitives/Struct.test.ts +0 -1154
  88. package/tests/primitives/Tree.test.ts +0 -1139
  89. package/tests/primitives/TreeNode.test.ts +0 -50
  90. package/tests/primitives/Union.test.ts +0 -554
  91. package/tests/server/ServerDocument.test.ts +0 -903
  92. package/tsconfig.build.json +0 -24
  93. package/tsconfig.json +0 -8
  94. package/tsdown.config.ts +0 -18
  95. package/vitest.mts +0 -11
@@ -1,1154 +0,0 @@
1
- import { describe, expect, it } from "@effect/vitest";
2
- import * as Primitive from "../../src/Primitive";
3
- import * as ProxyEnvironment from "../../src/ProxyEnvironment";
4
- import * as OperationPath from "../../src/OperationPath";
5
- import * as Operation from "../../src/Operation";
6
-
7
- const hasOwn = (value: unknown, key: string): boolean =>
8
- Object.prototype.hasOwnProperty.call(value, key);
9
-
10
- describe("StructPrimitive", () => {
11
- describe("proxy", () => {
12
- it("nested field access returns field primitive proxy", () => {
13
- const operations: Operation.Operation<any, any, any>[] = [];
14
- const env = ProxyEnvironment.make((op) => {
15
- operations.push(op);
16
- });
17
-
18
- const structPrimitive = Primitive.Struct({
19
- name: Primitive.String().required(),
20
- title: Primitive.String(),
21
- });
22
-
23
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
24
-
25
- // Access nested field and call set
26
- proxy.name.set("John Doe");
27
-
28
- expect(operations).toHaveLength(1);
29
- expect(operations[0]!.kind).toBe("string.set");
30
- expect(operations[0]!.payload).toBe("John Doe");
31
- expect(operations[0]!.path.toTokens()).toEqual(["name"]);
32
- });
33
-
34
- it("nested field paths are constructed correctly", () => {
35
- const operations: Operation.Operation<any, any, any>[] = [];
36
- const env = ProxyEnvironment.make((op) => {
37
- operations.push(op);
38
- });
39
-
40
- const structPrimitive = Primitive.Struct({
41
- name: Primitive.String(),
42
- email: Primitive.String(),
43
- });
44
-
45
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make("users/0"));
46
-
47
- proxy.email.set("test@example.com");
48
-
49
- expect(operations[0]!.path.toTokens()).toEqual(["users", "0", "email"]);
50
- });
51
-
52
- it("set() on struct generates struct.set operation", () => {
53
- const operations: Operation.Operation<any, any, any>[] = [];
54
- const env = ProxyEnvironment.make((op) => {
55
- operations.push(op);
56
- });
57
-
58
- const structPrimitive = Primitive.Struct({
59
- name: Primitive.String(),
60
- age: Primitive.String(),
61
- });
62
-
63
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
64
-
65
- proxy.set({ name: "Alice", age: "30" });
66
-
67
- expect(operations).toHaveLength(1);
68
- expect(operations[0]!.kind).toBe("struct.set");
69
- expect(operations[0]!.payload).toEqual({ name: "Alice", age: "30" });
70
- });
71
-
72
- it("set() only requires fields that are required and without defaults", () => {
73
- const operations: Operation.Operation<any, any, any>[] = [];
74
- const env = ProxyEnvironment.make((op) => {
75
- operations.push(op);
76
- });
77
-
78
- const structPrimitive = Primitive.Struct({
79
- name: Primitive.String().required().default("John Doe"),
80
- age: Primitive.Number().required(),
81
- email: Primitive.String(),
82
- });
83
-
84
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
85
- proxy.set({ age: 30 });
86
-
87
- expect(operations).toHaveLength(1);
88
- expect(operations[0]!.kind).toBe("struct.set");
89
- expect(operations[0]!.payload).toEqual({ name: "John Doe", age: 30 });
90
- expect(hasOwn(operations[0]!.payload, "email")).toBe(false);
91
- });
92
-
93
- it("set() only requires fields that are required and without defaults", () => {
94
- const operations: Operation.Operation<any, any, any>[] = [];
95
- const env = ProxyEnvironment.make((op) => {
96
- operations.push(op);
97
- });
98
-
99
- const structPrimitive = Primitive.Struct({
100
- name: Primitive.String().required().default("John Doe"),
101
- age: Primitive.Number().required(),
102
- email: Primitive.String(),
103
- }).default({ age: 30 });
104
-
105
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
106
- proxy.set({ age: 30 });
107
-
108
- expect(operations).toHaveLength(1);
109
- expect(operations[0]!.kind).toBe("struct.set");
110
- expect(operations[0]!.payload).toEqual({ name: "John Doe", age: 30 });
111
- expect(hasOwn(operations[0]!.payload, "email")).toBe(false);
112
- });
113
-
114
- it("set() prunes optional keys explicitly set to undefined", () => {
115
- const operations: Operation.Operation<any, any, any>[] = [];
116
- const env = ProxyEnvironment.make((op) => {
117
- operations.push(op);
118
- });
119
-
120
- const structPrimitive = Primitive.Struct({
121
- name: Primitive.String().required(),
122
- email: Primitive.String(),
123
- });
124
-
125
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
126
- proxy.set({ name: "Alice", email: undefined });
127
-
128
- expect(operations).toHaveLength(1);
129
- expect(operations[0]!.kind).toBe("struct.set");
130
- expect(operations[0]!.payload).toEqual({ name: "Alice" });
131
- expect(hasOwn(operations[0]!.payload, "email")).toBe(false);
132
- });
133
-
134
- it("set() prunes optional keys explicitly set to null", () => {
135
- const operations: Operation.Operation<any, any, any>[] = [];
136
- const env = ProxyEnvironment.make((op) => {
137
- operations.push(op);
138
- });
139
-
140
- const structPrimitive = Primitive.Struct({
141
- name: Primitive.String().required(),
142
- email: Primitive.String(),
143
- });
144
-
145
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
146
- (proxy as any).set({ name: "Alice", email: null });
147
-
148
- expect(operations).toHaveLength(1);
149
- expect(operations[0]!.kind).toBe("struct.set");
150
- expect(operations[0]!.payload).toEqual({ name: "Alice" });
151
- expect(hasOwn(operations[0]!.payload, "email")).toBe(false);
152
- });
153
-
154
- it("multiple field sets generate separate operations", () => {
155
- const operations: Operation.Operation<any, any, any>[] = [];
156
- const env = ProxyEnvironment.make((op) => {
157
- operations.push(op);
158
- });
159
-
160
- const structPrimitive = Primitive.Struct({
161
- firstName: Primitive.String(),
162
- lastName: Primitive.String(),
163
- });
164
-
165
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
166
-
167
- proxy.firstName.set("John");
168
- proxy.lastName.set("Doe");
169
-
170
- expect(operations).toHaveLength(2);
171
- expect(operations[0]!.payload).toBe("John");
172
- expect(operations[1]!.payload).toBe("Doe");
173
- });
174
- });
175
-
176
- describe("applyOperation", () => {
177
- it("struct.set replaces entire struct state", () => {
178
- const structPrimitive = Primitive.Struct({
179
- name: Primitive.String(),
180
- email: Primitive.String(),
181
- });
182
-
183
- const operation: Operation.Operation<any, any, any> = {
184
- kind: "struct.set",
185
- path: OperationPath.make(""),
186
- payload: { name: "Bob", email: "bob@test.com" },
187
- };
188
-
189
- const result = structPrimitive._internal.applyOperation(undefined, operation);
190
-
191
- expect(result).toEqual({ name: "Bob", email: "bob@test.com" });
192
- });
193
-
194
- it("delegates field operations to nested primitives", () => {
195
- const structPrimitive = Primitive.Struct({
196
- name: Primitive.String(),
197
- title: Primitive.String(),
198
- });
199
-
200
- const operation: Operation.Operation<any, any, any> = {
201
- kind: "string.set",
202
- path: OperationPath.make("name"),
203
- payload: "Updated Name",
204
- };
205
-
206
- const currentState = { name: "Original", title: "Mr" };
207
- const result = structPrimitive._internal.applyOperation(currentState, operation);
208
-
209
- expect(result).toEqual({ name: "Updated Name", title: "Mr" });
210
- });
211
-
212
- it("creates state from undefined when applying field operation", () => {
213
- const structPrimitive = Primitive.Struct({
214
- name: Primitive.String(),
215
- });
216
-
217
- const operation: Operation.Operation<any, any, any> = {
218
- kind: "string.set",
219
- path: OperationPath.make("name"),
220
- payload: "New Name",
221
- };
222
-
223
- const result = structPrimitive._internal.applyOperation(undefined, operation);
224
-
225
- expect(result).toEqual({ name: "New Name" });
226
- });
227
-
228
- it("throws ValidationError for unknown field", () => {
229
- const structPrimitive = Primitive.Struct({
230
- name: Primitive.String(),
231
- });
232
-
233
- const operation: Operation.Operation<any, any, any> = {
234
- kind: "string.set",
235
- path: OperationPath.make("unknownField"),
236
- payload: "value",
237
- };
238
-
239
- expect(() => structPrimitive._internal.applyOperation(undefined, operation)).toThrow(
240
- Primitive.ValidationError
241
- );
242
- });
243
-
244
- it("throws ValidationError for non-object payload on struct.set", () => {
245
- const structPrimitive = Primitive.Struct({
246
- name: Primitive.String(),
247
- });
248
-
249
- const operation: Operation.Operation<any, any, any> = {
250
- kind: "struct.set",
251
- path: OperationPath.make(""),
252
- payload: "not an object",
253
- };
254
-
255
- expect(() => structPrimitive._internal.applyOperation(undefined, operation)).toThrow(
256
- Primitive.ValidationError
257
- );
258
- });
259
- });
260
-
261
- describe("getInitialState", () => {
262
- it("returns undefined when no field has defaults", () => {
263
- const structPrimitive = Primitive.Struct({
264
- name: Primitive.String(),
265
- email: Primitive.String(),
266
- });
267
-
268
- expect(structPrimitive._internal.getInitialState()).toBeUndefined();
269
- });
270
-
271
- it("returns partial state from field defaults", () => {
272
- const structPrimitive = Primitive.Struct({
273
- name: Primitive.String().default("Anonymous"),
274
- email: Primitive.String(),
275
- });
276
-
277
- const initialState = structPrimitive._internal.getInitialState();
278
-
279
- expect(initialState).toEqual({ name: "Anonymous" });
280
- });
281
-
282
- it("returns complete state when all fields have defaults", () => {
283
- const structPrimitive = Primitive.Struct({
284
- name: Primitive.String().default("Guest"),
285
- role: Primitive.String().default("user"),
286
- });
287
-
288
- const initialState = structPrimitive._internal.getInitialState();
289
-
290
- expect(initialState).toEqual({ name: "Guest", role: "user" });
291
- });
292
-
293
- it("uses struct default value over field defaults", () => {
294
- const structPrimitive = Primitive.Struct({
295
- name: Primitive.String().default("Field Default"),
296
- }).default({ name: "Struct Default" });
297
-
298
- const initialState = structPrimitive._internal.getInitialState();
299
-
300
- expect(initialState).toEqual({ name: "Struct Default" });
301
- });
302
- });
303
-
304
- describe("nested structs", () => {
305
- it("supports nested struct primitives", () => {
306
- const operations: Operation.Operation<any, any, any>[] = [];
307
- const env = ProxyEnvironment.make((op) => {
308
- operations.push(op);
309
- });
310
-
311
- const addressPrimitive = Primitive.Struct({
312
- street: Primitive.String(),
313
- city: Primitive.String(),
314
- });
315
-
316
- const personPrimitive = Primitive.Struct({
317
- name: Primitive.String(),
318
- address: addressPrimitive,
319
- });
320
-
321
- const proxy = personPrimitive._internal.createProxy(env, OperationPath.make(""));
322
-
323
- proxy.address.city.set("New York");
324
-
325
- expect(operations).toHaveLength(1);
326
- expect(operations[0]!.kind).toBe("string.set");
327
- expect(operations[0]!.payload).toBe("New York");
328
- expect(operations[0]!.path.toTokens()).toEqual(["address", "city"]);
329
- });
330
-
331
- it("applies operations to nested structs", () => {
332
- const addressPrimitive = Primitive.Struct({
333
- street: Primitive.String(),
334
- city: Primitive.String(),
335
- });
336
-
337
- const personPrimitive = Primitive.Struct({
338
- name: Primitive.String(),
339
- address: addressPrimitive,
340
- });
341
-
342
- const operation: Operation.Operation<any, any, any> = {
343
- kind: "string.set",
344
- path: OperationPath.make("address/city"),
345
- payload: "Los Angeles",
346
- };
347
-
348
- const currentState = {
349
- name: "John",
350
- address: { street: "123 Main St", city: "San Francisco" },
351
- };
352
-
353
- const result = personPrimitive._internal.applyOperation(currentState, operation);
354
-
355
- expect(result).toEqual({
356
- name: "John",
357
- address: { street: "123 Main St", city: "Los Angeles" },
358
- });
359
- });
360
- });
361
-
362
- describe("type inference", () => {
363
- it("infers correct state type from struct definition", () => {
364
- const structPrimitive = Primitive.Struct({
365
- name: Primitive.String(),
366
- email: Primitive.String(),
367
- });
368
-
369
- // This is a compile-time check - the type should be inferred correctly
370
- type ExpectedState = Primitive.InferState<typeof structPrimitive>;
371
- const state: ExpectedState = { name: "test", email: "test@test.com" };
372
-
373
- expect(state).toEqual({ name: "test", email: "test@test.com" });
374
- });
375
-
376
- it("infers correct proxy type with field access", () => {
377
- const structPrimitive = Primitive.Struct({
378
- name: Primitive.String(),
379
- });
380
-
381
- type ExpectedProxy = Primitive.InferProxy<typeof structPrimitive>;
382
-
383
- // This would fail at compile time if types are wrong
384
- const env = ProxyEnvironment.make(() => {});
385
- const proxy: ExpectedProxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
386
-
387
- // Verify the proxy has the expected shape
388
- expect(typeof proxy.name.set).toBe("function");
389
- expect(typeof proxy.set).toBe("function");
390
- });
391
- });
392
-
393
- describe("update", () => {
394
- it("update() generates individual field operations", () => {
395
- const operations: Operation.Operation<any, any, any>[] = [];
396
- const env = ProxyEnvironment.make((op) => {
397
- operations.push(op);
398
- });
399
-
400
- const structPrimitive = Primitive.Struct({
401
- name: Primitive.String(),
402
- email: Primitive.String(),
403
- });
404
-
405
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
406
-
407
- proxy.update({ name: "John" });
408
-
409
- expect(operations).toHaveLength(1);
410
- expect(operations[0]!.kind).toBe("string.set");
411
- expect(operations[0]!.payload).toBe("John");
412
- expect(operations[0]!.path.toTokens()).toEqual(["name"]);
413
- });
414
-
415
- it("update() with multiple fields generates multiple operations", () => {
416
- const operations: Operation.Operation<any, any, any>[] = [];
417
- const env = ProxyEnvironment.make((op) => {
418
- operations.push(op);
419
- });
420
-
421
- const structPrimitive = Primitive.Struct({
422
- firstName: Primitive.String(),
423
- lastName: Primitive.String(),
424
- email: Primitive.String(),
425
- });
426
-
427
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
428
-
429
- proxy.update({ firstName: "John", lastName: "Doe" });
430
-
431
- expect(operations).toHaveLength(2);
432
- expect(operations.map((op) => op.payload)).toContain("John");
433
- expect(operations.map((op) => op.payload)).toContain("Doe");
434
- });
435
-
436
- it("update() emits struct.unset for undefined optional values", () => {
437
- const operations: Operation.Operation<any, any, any>[] = [];
438
- const env = ProxyEnvironment.make((op) => {
439
- operations.push(op);
440
- });
441
-
442
- const structPrimitive = Primitive.Struct({
443
- name: Primitive.String(),
444
- email: Primitive.String(),
445
- });
446
-
447
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
448
-
449
- proxy.update({ name: "John", email: undefined });
450
-
451
- expect(operations).toHaveLength(2);
452
- const nameOp = operations.find((op) => op.path.toTokens().join("/") === "name");
453
- const unsetOp = operations.find((op) => op.path.toTokens().join("/") === "email");
454
- expect(nameOp!.kind).toBe("string.set");
455
- expect(nameOp!.payload).toBe("John");
456
- expect(unsetOp!.kind).toBe("struct.unset");
457
- });
458
-
459
- it("update() removes existing optional key for undefined value", () => {
460
- const operations: Operation.Operation<any, any, any>[] = [];
461
- const structPrimitive = Primitive.Struct({
462
- name: Primitive.String(),
463
- email: Primitive.String(),
464
- });
465
- let state: Primitive.InferState<typeof structPrimitive> | undefined = {
466
- name: "John",
467
- email: "john@example.com",
468
- };
469
-
470
- const env = ProxyEnvironment.make({
471
- onOperation: (op) => {
472
- operations.push(op);
473
- state = structPrimitive._internal.applyOperation(state, op);
474
- },
475
- getState: () => state,
476
- });
477
-
478
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
479
- proxy.update({ email: undefined });
480
-
481
- expect(operations).toHaveLength(1);
482
- expect(operations[0]!.kind).toBe("struct.unset");
483
- expect(state).toEqual({ name: "John" });
484
- expect(hasOwn(state, "email")).toBe(false);
485
- });
486
-
487
- it("update() removes existing optional key for null value", () => {
488
- const operations: Operation.Operation<any, any, any>[] = [];
489
- const structPrimitive = Primitive.Struct({
490
- name: Primitive.String(),
491
- email: Primitive.String(),
492
- });
493
- let state: Primitive.InferState<typeof structPrimitive> | undefined = {
494
- name: "John",
495
- email: "john@example.com",
496
- };
497
-
498
- const env = ProxyEnvironment.make({
499
- onOperation: (op) => {
500
- operations.push(op);
501
- state = structPrimitive._internal.applyOperation(state, op);
502
- },
503
- getState: () => state,
504
- });
505
-
506
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
507
- (proxy as any).update({ email: null });
508
-
509
- expect(operations).toHaveLength(1);
510
- expect(operations[0]!.kind).toBe("struct.unset");
511
- expect(state).toEqual({ name: "John" });
512
- expect(hasOwn(state, "email")).toBe(false);
513
- });
514
-
515
- it("update() throws for undefined on required fields without defaults", () => {
516
- const operations: Operation.Operation<any, any, any>[] = [];
517
- const env = ProxyEnvironment.make((op) => {
518
- operations.push(op);
519
- });
520
-
521
- const structPrimitive = Primitive.Struct({
522
- name: Primitive.String().required(),
523
- });
524
-
525
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
526
-
527
- expect(() => proxy.update({ name: undefined as never })).toThrow(Primitive.ValidationError);
528
- expect(operations).toHaveLength(0);
529
- });
530
-
531
- it("update() throws for null on required fields without defaults", () => {
532
- const operations: Operation.Operation<any, any, any>[] = [];
533
- const env = ProxyEnvironment.make((op) => {
534
- operations.push(op);
535
- });
536
-
537
- const structPrimitive = Primitive.Struct({
538
- name: Primitive.String().required(),
539
- });
540
-
541
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
542
-
543
- expect(() => (proxy as any).update({ name: null })).toThrow(Primitive.ValidationError);
544
- expect(operations).toHaveLength(0);
545
- });
546
-
547
- it("update() recursively updates nested structs", () => {
548
- const operations: Operation.Operation<any, any, any>[] = [];
549
- const env = ProxyEnvironment.make((op) => {
550
- operations.push(op);
551
- });
552
-
553
- const addressPrimitive = Primitive.Struct({
554
- street: Primitive.String(),
555
- city: Primitive.String(),
556
- zip: Primitive.String(),
557
- });
558
-
559
- const personPrimitive = Primitive.Struct({
560
- name: Primitive.String(),
561
- address: addressPrimitive,
562
- });
563
-
564
- const proxy = personPrimitive._internal.createProxy(env, OperationPath.make(""));
565
-
566
- // Partial update of nested struct - only city should be updated
567
- proxy.update({ address: { city: "New York" } });
568
-
569
- expect(operations).toHaveLength(1);
570
- expect(operations[0]!.kind).toBe("string.set");
571
- expect(operations[0]!.payload).toBe("New York");
572
- expect(operations[0]!.path.toTokens()).toEqual(["address", "city"]);
573
- });
574
-
575
- it("update() handles deeply nested structs", () => {
576
- const operations: Operation.Operation<any, any, any>[] = [];
577
- const env = ProxyEnvironment.make((op) => {
578
- operations.push(op);
579
- });
580
-
581
- const coordsPrimitive = Primitive.Struct({
582
- lat: Primitive.String(),
583
- lng: Primitive.String(),
584
- });
585
-
586
- const locationPrimitive = Primitive.Struct({
587
- name: Primitive.String(),
588
- coords: coordsPrimitive,
589
- });
590
-
591
- const personPrimitive = Primitive.Struct({
592
- name: Primitive.String(),
593
- location: locationPrimitive,
594
- });
595
-
596
- const proxy = personPrimitive._internal.createProxy(env, OperationPath.make(""));
597
-
598
- proxy.update({ location: { coords: { lat: "40.7128" } } });
599
-
600
- expect(operations).toHaveLength(1);
601
- expect(operations[0]!.kind).toBe("string.set");
602
- expect(operations[0]!.payload).toBe("40.7128");
603
- expect(operations[0]!.path.toTokens()).toEqual(["location", "coords", "lat"]);
604
- });
605
-
606
- it("update() can update both nested and top-level fields", () => {
607
- const operations: Operation.Operation<any, any, any>[] = [];
608
- const env = ProxyEnvironment.make((op) => {
609
- operations.push(op);
610
- });
611
-
612
- const addressPrimitive = Primitive.Struct({
613
- city: Primitive.String(),
614
- zip: Primitive.String(),
615
- });
616
-
617
- const personPrimitive = Primitive.Struct({
618
- name: Primitive.String(),
619
- address: addressPrimitive,
620
- });
621
-
622
- const proxy = personPrimitive._internal.createProxy(env, OperationPath.make(""));
623
-
624
- proxy.update({ name: "Jane", address: { city: "Boston" } });
625
-
626
- expect(operations).toHaveLength(2);
627
-
628
- const nameOp = operations.find((op) => op.path.toTokens().join("/") === "name");
629
- const cityOp = operations.find((op) => op.path.toTokens().join("/") === "address/city");
630
-
631
- expect(nameOp).toBeDefined();
632
- expect(nameOp!.payload).toBe("Jane");
633
-
634
- expect(cityOp).toBeDefined();
635
- expect(cityOp!.payload).toBe("Boston");
636
- });
637
-
638
- it("update() with empty object generates no operations", () => {
639
- const operations: Operation.Operation<any, any, any>[] = [];
640
- const env = ProxyEnvironment.make((op) => {
641
- operations.push(op);
642
- });
643
-
644
- const structPrimitive = Primitive.Struct({
645
- name: Primitive.String(),
646
- email: Primitive.String(),
647
- });
648
-
649
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
650
-
651
- proxy.update({});
652
-
653
- expect(operations).toHaveLength(0);
654
- });
655
-
656
- it("update() ignores unknown fields", () => {
657
- const operations: Operation.Operation<any, any, any>[] = [];
658
- const env = ProxyEnvironment.make((op) => {
659
- operations.push(op);
660
- });
661
-
662
- const structPrimitive = Primitive.Struct({
663
- name: Primitive.String(),
664
- });
665
-
666
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make(""));
667
-
668
- // Cast to any to bypass type checking for testing unknown fields
669
- (proxy as any).update({ name: "John", unknownField: "value" });
670
-
671
- expect(operations).toHaveLength(1);
672
- expect(operations[0]!.payload).toBe("John");
673
- });
674
-
675
- it("update() with nested path prefix", () => {
676
- const operations: Operation.Operation<any, any, any>[] = [];
677
- const env = ProxyEnvironment.make((op) => {
678
- operations.push(op);
679
- });
680
-
681
- const structPrimitive = Primitive.Struct({
682
- name: Primitive.String(),
683
- email: Primitive.String(),
684
- });
685
-
686
- const proxy = structPrimitive._internal.createProxy(env, OperationPath.make("users/0"));
687
-
688
- proxy.update({ name: "Updated" });
689
-
690
- expect(operations).toHaveLength(1);
691
- expect(operations[0]!.path.toTokens()).toEqual(["users", "0", "name"]);
692
- });
693
- });
694
- describe("extend", () => {
695
- it("extends struct with additional fields", () => {
696
- const basePrimitive = Primitive.Struct({
697
- name: Primitive.String().required(),
698
- });
699
-
700
- const extendedPrimitive = basePrimitive.extend({
701
- email: Primitive.String().required(),
702
- age: Primitive.Number(),
703
- });
704
-
705
- // Verify fields exist
706
- expect(extendedPrimitive.fields).toHaveProperty("name");
707
- expect(extendedPrimitive.fields).toHaveProperty("email");
708
- expect(extendedPrimitive.fields).toHaveProperty("age");
709
- });
710
-
711
- it("extended struct generates correct operations", () => {
712
- const operations: Operation.Operation<any, any, any>[] = [];
713
- const env = ProxyEnvironment.make((op) => {
714
- operations.push(op);
715
- });
716
-
717
- const basePrimitive = Primitive.Struct({
718
- name: Primitive.String(),
719
- });
720
-
721
- const extendedPrimitive = basePrimitive.extend({
722
- email: Primitive.String(),
723
- });
724
-
725
- const proxy = extendedPrimitive._internal.createProxy(env, OperationPath.make(""));
726
-
727
- proxy.name.set("John");
728
- proxy.email.set("john@example.com");
729
-
730
- expect(operations).toHaveLength(2);
731
- expect(operations[0]!.kind).toBe("string.set");
732
- expect(operations[0]!.payload).toBe("John");
733
- expect(operations[0]!.path.toTokens()).toEqual(["name"]);
734
- expect(operations[1]!.kind).toBe("string.set");
735
- expect(operations[1]!.payload).toBe("john@example.com");
736
- expect(operations[1]!.path.toTokens()).toEqual(["email"]);
737
- });
738
-
739
- it("extended struct set() works with all fields", () => {
740
- const operations: Operation.Operation<any, any, any>[] = [];
741
- const env = ProxyEnvironment.make((op) => {
742
- operations.push(op);
743
- });
744
-
745
- const basePrimitive = Primitive.Struct({
746
- name: Primitive.String(),
747
- });
748
-
749
- const extendedPrimitive = basePrimitive.extend({
750
- email: Primitive.String(),
751
- });
752
-
753
- const proxy = extendedPrimitive._internal.createProxy(env, OperationPath.make(""));
754
-
755
- proxy.set({ name: "John", email: "john@example.com" });
756
-
757
- expect(operations).toHaveLength(1);
758
- expect(operations[0]!.kind).toBe("struct.set");
759
- expect(operations[0]!.payload).toEqual({ name: "John", email: "john@example.com" });
760
- });
761
-
762
- it("extended struct preserves required status", () => {
763
- const basePrimitive = Primitive.Struct({
764
- name: Primitive.String(),
765
- }).required();
766
-
767
- const extendedPrimitive = basePrimitive.extend({
768
- email: Primitive.String(),
769
- });
770
-
771
- // The extended struct should still be required - verify via type system
772
- // Compile-time check: if the type doesn't match, this would be a type error
773
- type ExtendedTRequired = typeof extendedPrimitive._TRequired;
774
- const _typeCheck: ExtendedTRequired = true as const;
775
- expect(_typeCheck).toBe(true);
776
- });
777
-
778
- it("extended struct applyOperation works for both base and new fields", () => {
779
- const basePrimitive = Primitive.Struct({
780
- name: Primitive.String(),
781
- });
782
-
783
- const extendedPrimitive = basePrimitive.extend({
784
- email: Primitive.String(),
785
- });
786
-
787
- const nameOperation: Operation.Operation<any, any, any> = {
788
- kind: "string.set",
789
- path: OperationPath.make("name"),
790
- payload: "John",
791
- };
792
-
793
- const emailOperation: Operation.Operation<any, any, any> = {
794
- kind: "string.set",
795
- path: OperationPath.make("email"),
796
- payload: "john@example.com",
797
- };
798
-
799
- let state = extendedPrimitive._internal.applyOperation(undefined, nameOperation);
800
- state = extendedPrimitive._internal.applyOperation(state, emailOperation);
801
-
802
- expect(state).toEqual({ name: "John", email: "john@example.com" });
803
- });
804
-
805
- it("can chain multiple extend calls", () => {
806
- const basePrimitive = Primitive.Struct({
807
- id: Primitive.String(),
808
- });
809
-
810
- const extendedOnce = basePrimitive.extend({
811
- name: Primitive.String(),
812
- });
813
-
814
- const extendedTwice = extendedOnce.extend({
815
- email: Primitive.String(),
816
- });
817
-
818
- expect(extendedTwice.fields).toHaveProperty("id");
819
- expect(extendedTwice.fields).toHaveProperty("name");
820
- expect(extendedTwice.fields).toHaveProperty("email");
821
- });
822
-
823
- it("extend with nested struct works correctly", () => {
824
- const operations: Operation.Operation<any, any, any>[] = [];
825
- const env = ProxyEnvironment.make((op) => {
826
- operations.push(op);
827
- });
828
-
829
- const basePrimitive = Primitive.Struct({
830
- name: Primitive.String(),
831
- });
832
-
833
- const addressPrimitive = Primitive.Struct({
834
- city: Primitive.String(),
835
- zip: Primitive.String(),
836
- });
837
-
838
- const extendedPrimitive = basePrimitive.extend({
839
- address: addressPrimitive,
840
- });
841
-
842
- const proxy = extendedPrimitive._internal.createProxy(env, OperationPath.make(""));
843
-
844
- proxy.address.city.set("New York");
845
-
846
- expect(operations).toHaveLength(1);
847
- expect(operations[0]!.kind).toBe("string.set");
848
- expect(operations[0]!.payload).toBe("New York");
849
- expect(operations[0]!.path.toTokens()).toEqual(["address", "city"]);
850
- });
851
- });
852
-
853
- describe("partial", () => {
854
- it("makes all fields optional", () => {
855
- const structPrimitive = Primitive.Struct({
856
- name: Primitive.String().required(),
857
- email: Primitive.String().required(),
858
- age: Primitive.Number().required(),
859
- });
860
-
861
- const partialPrimitive = structPrimitive.partial();
862
-
863
- // All fields should now be optional (not required)
864
- expect(partialPrimitive.fields).toHaveProperty("name");
865
- expect(partialPrimitive.fields).toHaveProperty("email");
866
- expect(partialPrimitive.fields).toHaveProperty("age");
867
- });
868
-
869
- it("partial struct allows empty set()", () => {
870
- const operations: Operation.Operation<any, any, any>[] = [];
871
- const env = ProxyEnvironment.make((op) => {
872
- operations.push(op);
873
- });
874
-
875
- const structPrimitive = Primitive.Struct({
876
- name: Primitive.String().required(),
877
- email: Primitive.String().required(),
878
- });
879
-
880
- const partialPrimitive = structPrimitive.partial();
881
-
882
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
883
-
884
- // This should work without providing any fields since all are optional
885
- proxy.set({});
886
-
887
- expect(operations).toHaveLength(1);
888
- expect(operations[0]!.kind).toBe("struct.set");
889
- });
890
-
891
- it("partial struct allows partial set()", () => {
892
- const operations: Operation.Operation<any, any, any>[] = [];
893
- const env = ProxyEnvironment.make((op) => {
894
- operations.push(op);
895
- });
896
-
897
- const structPrimitive = Primitive.Struct({
898
- name: Primitive.String().required(),
899
- email: Primitive.String().required(),
900
- age: Primitive.Number().required(),
901
- });
902
-
903
- const partialPrimitive = structPrimitive.partial();
904
-
905
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
906
-
907
- // Only provide some fields
908
- proxy.set({ name: "John" });
909
-
910
- expect(operations).toHaveLength(1);
911
- expect(operations[0]!.kind).toBe("struct.set");
912
- expect(operations[0]!.payload).toHaveProperty("name", "John");
913
- });
914
-
915
- it("partial struct field access still works", () => {
916
- const operations: Operation.Operation<any, any, any>[] = [];
917
- const env = ProxyEnvironment.make((op) => {
918
- operations.push(op);
919
- });
920
-
921
- const structPrimitive = Primitive.Struct({
922
- name: Primitive.String().required(),
923
- email: Primitive.String().required(),
924
- });
925
-
926
- const partialPrimitive = structPrimitive.partial();
927
-
928
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
929
-
930
- proxy.name.set("John");
931
-
932
- expect(operations).toHaveLength(1);
933
- expect(operations[0]!.kind).toBe("string.set");
934
- expect(operations[0]!.payload).toBe("John");
935
- expect(operations[0]!.path.toTokens()).toEqual(["name"]);
936
- });
937
-
938
- it("partial struct preserves required/default status of struct itself", () => {
939
- const structPrimitive = Primitive.Struct({
940
- name: Primitive.String().required(),
941
- }).required();
942
-
943
- const partialPrimitive = structPrimitive.partial();
944
- // The struct itself should still be required - verify via type system
945
- // Compile-time check: if the type doesn't match, this would be a type error
946
- type PartialTRequired = typeof partialPrimitive._TRequired;
947
- const _typeCheck: PartialTRequired = true as const;
948
- expect(_typeCheck).toBe(true);
949
- });
950
-
951
- it("partial struct applyOperation works correctly", () => {
952
- const structPrimitive = Primitive.Struct({
953
- name: Primitive.String().required(),
954
- email: Primitive.String().required(),
955
- });
956
-
957
- const partialPrimitive = structPrimitive.partial();
958
-
959
- const operation: Operation.Operation<any, any, any> = {
960
- kind: "string.set",
961
- path: OperationPath.make("name"),
962
- payload: "John",
963
- };
964
-
965
- const result = partialPrimitive._internal.applyOperation(undefined, operation);
966
-
967
- expect(result).toEqual({ name: "John" });
968
- });
969
-
970
- it("partial can be combined with extend", () => {
971
- const basePrimitive = Primitive.Struct({
972
- id: Primitive.String().required(),
973
- name: Primitive.String().required(),
974
- });
975
-
976
- // First extend, then partial
977
- const extendedPartial = basePrimitive
978
- .extend({
979
- email: Primitive.String().required(),
980
- })
981
- .partial();
982
-
983
- expect(extendedPartial.fields).toHaveProperty("id");
984
- expect(extendedPartial.fields).toHaveProperty("name");
985
- expect(extendedPartial.fields).toHaveProperty("email");
986
- });
987
-
988
- it("partial works with nested structs", () => {
989
- const addressPrimitive = Primitive.Struct({
990
- city: Primitive.String().required(),
991
- zip: Primitive.String().required(),
992
- });
993
-
994
- const personPrimitive = Primitive.Struct({
995
- name: Primitive.String().required(),
996
- address: addressPrimitive.required(),
997
- });
998
-
999
- const partialPrimitive = personPrimitive.partial();
1000
-
1001
- const operations: Operation.Operation<any, any, any>[] = [];
1002
- const env = ProxyEnvironment.make((op) => {
1003
- operations.push(op);
1004
- });
1005
-
1006
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1007
-
1008
- // Nested struct access should still work
1009
- proxy.address.city.set("New York");
1010
-
1011
- expect(operations).toHaveLength(1);
1012
- expect(operations[0]!.kind).toBe("string.set");
1013
- expect(operations[0]!.payload).toBe("New York");
1014
- expect(operations[0]!.path.toTokens()).toEqual(["address", "city"]);
1015
- });
1016
-
1017
- it("partial struct update() works correctly", () => {
1018
- const operations: Operation.Operation<any, any, any>[] = [];
1019
- const env = ProxyEnvironment.make((op) => {
1020
- operations.push(op);
1021
- });
1022
-
1023
- const structPrimitive = Primitive.Struct({
1024
- name: Primitive.String().required(),
1025
- email: Primitive.String().required(),
1026
- age: Primitive.Number().required(),
1027
- });
1028
-
1029
- const partialPrimitive = structPrimitive.partial();
1030
-
1031
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1032
-
1033
- proxy.update({ name: "Jane" });
1034
-
1035
- expect(operations).toHaveLength(1);
1036
- expect(operations[0]!.kind).toBe("string.set");
1037
- expect(operations[0]!.payload).toBe("Jane");
1038
- });
1039
-
1040
- it("partial({ stripDefaults: true }) clears field defaults", () => {
1041
- const structPrimitive = Primitive.Struct({
1042
- paddingTop: Primitive.Number().default(0),
1043
- name: Primitive.String().default("Anonymous"),
1044
- age: Primitive.Number(),
1045
- });
1046
-
1047
- const partialPrimitive = structPrimitive.partial({ stripDefaults: true });
1048
-
1049
- // getInitialState should return undefined since all defaults are stripped
1050
- expect(partialPrimitive._internal.getInitialState()).toBeUndefined();
1051
- });
1052
-
1053
- it("partial({ stripDefaults: true }) toSnapshot returns undefined for unset fields", () => {
1054
- const env = ProxyEnvironment.make(() => {});
1055
-
1056
- const structPrimitive = Primitive.Struct({
1057
- paddingTop: Primitive.Number().default(0),
1058
- name: Primitive.String().default("test"),
1059
- });
1060
-
1061
- const partialPrimitive = structPrimitive.partial({ stripDefaults: true });
1062
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1063
-
1064
- // With no state set, toSnapshot should return undefined (no defaults to fall back on)
1065
- expect(proxy.toSnapshot()).toBeUndefined();
1066
- });
1067
-
1068
- it("partial({ stripDefaults: true }) strips nested struct defaults", () => {
1069
- const structPrimitive = Primitive.Struct({
1070
- profile: Primitive.Struct({
1071
- name: Primitive.String().default("Anonymous"),
1072
- age: Primitive.Number().default(0),
1073
- }),
1074
- });
1075
-
1076
- const partialPrimitive = structPrimitive.partial({ stripDefaults: true });
1077
- expect(partialPrimitive._internal.getInitialState()).toBeUndefined();
1078
-
1079
- const env = ProxyEnvironment.make(() => {});
1080
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1081
- expect(proxy.toSnapshot()).toBeUndefined();
1082
- });
1083
-
1084
- it("partial({ stripDefaults: true }) does not re-apply nested defaults during set()", () => {
1085
- const operations: Operation.Operation<any, any, any>[] = [];
1086
- const env = ProxyEnvironment.make((op) => {
1087
- operations.push(op);
1088
- });
1089
-
1090
- const structPrimitive = Primitive.Struct({
1091
- profile: Primitive.Struct({
1092
- name: Primitive.String().default("Anonymous"),
1093
- age: Primitive.Number().default(0),
1094
- }),
1095
- });
1096
-
1097
- const partialPrimitive = structPrimitive.partial({ stripDefaults: true });
1098
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1099
-
1100
- proxy.set({ profile: {} });
1101
-
1102
- expect(operations).toHaveLength(1);
1103
- expect(operations[0]!.kind).toBe("struct.set");
1104
- expect(operations[0]!.payload).toEqual({ profile: {} });
1105
- });
1106
-
1107
- it("partial({ stripDefaults: true }) keeps field proxies typed as possibly undefined", () => {
1108
- const structPrimitive = Primitive.Struct({
1109
- name: Primitive.String().default("Anonymous"),
1110
- });
1111
-
1112
- const partialPrimitive = structPrimitive.partial({ stripDefaults: true });
1113
- const env = ProxyEnvironment.make(() => {});
1114
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1115
-
1116
- // Compile-time assertion: with defaults stripped, field access is optional.
1117
- const value: string | undefined = proxy.name.get();
1118
- expect(value).toBeUndefined();
1119
- });
1120
-
1121
- it("partial() without stripDefaults preserves field defaults", () => {
1122
- const structPrimitive = Primitive.Struct({
1123
- paddingTop: Primitive.Number().default(0),
1124
- name: Primitive.String().default("Anonymous"),
1125
- });
1126
-
1127
- const partialPrimitive = structPrimitive.partial();
1128
-
1129
- // getInitialState should still return the defaults
1130
- expect(partialPrimitive._internal.getInitialState()).toEqual({
1131
- paddingTop: 0,
1132
- name: "Anonymous",
1133
- });
1134
- });
1135
-
1136
- it("partial() without stripDefaults keeps field proxies typed as defined when defaults exist", () => {
1137
- const structPrimitive = Primitive.Struct({
1138
- name: Primitive.String().default("Anonymous"),
1139
- });
1140
-
1141
- const partialPrimitive = structPrimitive.partial();
1142
- const env = ProxyEnvironment.make(() => {});
1143
- const proxy = partialPrimitive._internal.createProxy(env, OperationPath.make(""));
1144
-
1145
- // Compile-time assertion: without stripping defaults, field access remains defined.
1146
- const value: string = proxy.name.get();
1147
- expect(value).toBe("Anonymous");
1148
- });
1149
- });
1150
- });
1151
-
1152
- // =============================================================================
1153
- // Array Primitive Tests (Ordered with ID + Fractional Index)
1154
- // =============================================================================