@voidhash/mimic 0.0.1-alpha.6 → 0.0.1-alpha.8

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 (53) hide show
  1. package/.turbo/turbo-build.log +43 -15
  2. package/dist/Document-ChuFrTk1.cjs +571 -0
  3. package/dist/Document-CwiAFTIq.mjs +438 -0
  4. package/dist/Document-CwiAFTIq.mjs.map +1 -0
  5. package/dist/Presence-DKKP4v5X.d.cts +91 -0
  6. package/dist/Presence-DKKP4v5X.d.cts.map +1 -0
  7. package/dist/Presence-DdMVKcOv.mjs +110 -0
  8. package/dist/Presence-DdMVKcOv.mjs.map +1 -0
  9. package/dist/Presence-N8u7Eppr.d.mts +91 -0
  10. package/dist/Presence-N8u7Eppr.d.mts.map +1 -0
  11. package/dist/Presence-gWrmGBeu.cjs +126 -0
  12. package/dist/Primitive-BK7kfHJZ.d.cts +1165 -0
  13. package/dist/Primitive-BK7kfHJZ.d.cts.map +1 -0
  14. package/dist/Primitive-D1kdB6za.d.mts +1165 -0
  15. package/dist/Primitive-D1kdB6za.d.mts.map +1 -0
  16. package/dist/client/index.cjs +1456 -0
  17. package/dist/client/index.d.cts +692 -0
  18. package/dist/client/index.d.cts.map +1 -0
  19. package/dist/client/index.d.mts +692 -0
  20. package/dist/client/index.d.mts.map +1 -0
  21. package/dist/client/index.mjs +1413 -0
  22. package/dist/client/index.mjs.map +1 -0
  23. package/dist/index.cjs +309 -757
  24. package/dist/index.d.cts +5 -1054
  25. package/dist/index.d.cts.map +1 -1
  26. package/dist/index.d.mts +5 -1054
  27. package/dist/index.d.mts.map +1 -1
  28. package/dist/index.mjs +168 -575
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/server/index.cjs +191 -0
  31. package/dist/server/index.d.cts +148 -0
  32. package/dist/server/index.d.cts.map +1 -0
  33. package/dist/server/index.d.mts +148 -0
  34. package/dist/server/index.d.mts.map +1 -0
  35. package/dist/server/index.mjs +182 -0
  36. package/dist/server/index.mjs.map +1 -0
  37. package/package.json +17 -5
  38. package/src/primitives/Array.ts +57 -22
  39. package/src/primitives/Boolean.ts +32 -18
  40. package/src/primitives/Either.ts +39 -24
  41. package/src/primitives/Lazy.ts +16 -2
  42. package/src/primitives/Literal.ts +32 -19
  43. package/src/primitives/Number.ts +38 -25
  44. package/src/primitives/String.ts +39 -24
  45. package/src/primitives/Struct.ts +124 -27
  46. package/src/primitives/Tree.ts +117 -30
  47. package/src/primitives/Union.ts +56 -29
  48. package/src/primitives/shared.ts +103 -9
  49. package/tests/primitives/Array.test.ts +108 -0
  50. package/tests/primitives/Struct.test.ts +250 -0
  51. package/tests/primitives/Tree.test.ts +250 -0
  52. package/tsdown.config.ts +1 -1
  53. /package/dist/{chunk-C6wwvPpM.mjs → chunk-CLMFDpHK.mjs} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["Document.make","_transactionOrder: string[]","Transaction.isEmpty"],"sources":["../../src/server/ServerDocument.ts","../../src/server/errors.ts"],"sourcesContent":["import * as Document from \"../Document\";\nimport * as Transaction from \"../Transaction\";\nimport type * as Primitive from \"../Primitive\";\n\n// =============================================================================\n// Server Message Types (matching client's Transport expectations)\n// =============================================================================\n\n/**\n * Message sent when broadcasting a committed transaction.\n */\nexport interface TransactionMessage {\n readonly type: \"transaction\";\n readonly transaction: Transaction.Transaction;\n /** Server-assigned version number for ordering */\n readonly version: number;\n}\n\n/**\n * Message sent when a transaction is rejected.\n */\nexport interface ErrorMessage {\n readonly type: \"error\";\n readonly transactionId: string;\n readonly reason: string;\n}\n\n/**\n * Message sent as a full state snapshot.\n */\nexport interface SnapshotMessage {\n readonly type: \"snapshot\";\n readonly state: unknown;\n readonly version: number;\n}\n\n/**\n * Union of all server messages that can be broadcast.\n */\nexport type ServerMessage = TransactionMessage | ErrorMessage | SnapshotMessage;\n\n// =============================================================================\n// Submit Result Types\n// =============================================================================\n\n/**\n * Result of submitting a transaction to the server.\n */\nexport type SubmitResult =\n | { readonly success: true; readonly version: number }\n | { readonly success: false; readonly reason: string };\n\n// =============================================================================\n// Server Document Types\n// =============================================================================\n\n/**\n * Options for creating a ServerDocument.\n */\nexport interface ServerDocumentOptions<TSchema extends Primitive.AnyPrimitive> {\n /** The schema defining the document structure */\n readonly schema: TSchema;\n /** Initial state (optional, will use schema defaults if not provided) */\n readonly initialState?: Primitive.InferState<TSchema>;\n /** Initial version number (optional, defaults to 0) */\n readonly initialVersion?: number;\n /** Called when a transaction is successfully applied and should be broadcast */\n readonly onBroadcast: (message: TransactionMessage) => void;\n /** Called when a transaction is rejected (optional, for logging/metrics) */\n readonly onRejection?: (transactionId: string, reason: string) => void;\n /** Maximum number of processed transaction IDs to track for deduplication */\n readonly maxTransactionHistory?: number;\n}\n\n/**\n * A ServerDocument maintains the authoritative state and processes client transactions.\n */\nexport interface ServerDocument<TSchema extends Primitive.AnyPrimitive> {\n /** The schema defining this document's structure */\n readonly schema: TSchema;\n\n /** Returns the current authoritative state */\n get(): Primitive.InferState<TSchema> | undefined;\n\n /** Returns the current version number */\n getVersion(): number;\n\n /**\n * Submits a transaction for processing.\n * Validates and applies the transaction if valid, or rejects it with a reason.\n * @param transaction - The transaction to process\n * @returns SubmitResult indicating success with version or failure with reason\n */\n submit(transaction: Transaction.Transaction): SubmitResult;\n\n /**\n * Returns a snapshot of the current state and version.\n * Used to initialize new clients or resync after drift.\n */\n getSnapshot(): SnapshotMessage;\n\n /**\n * Checks if a transaction has already been processed.\n * @param transactionId - The transaction ID to check\n */\n hasProcessed(transactionId: string): boolean;\n}\n\n// =============================================================================\n// Server Document Implementation\n// =============================================================================\n\n/**\n * Creates a new ServerDocument for the given schema.\n */\nexport const make = <TSchema extends Primitive.AnyPrimitive>(\n options: ServerDocumentOptions<TSchema>\n): ServerDocument<TSchema> => {\n const {\n schema,\n initialState,\n initialVersion = 0,\n onBroadcast,\n onRejection,\n maxTransactionHistory = 1000,\n } = options;\n\n // ==========================================================================\n // Internal State\n // ==========================================================================\n\n // The authoritative document\n let _document = Document.make(schema, { initial: initialState });\n\n // Current version number (incremented on each successful transaction)\n let _version = initialVersion;\n\n // Track processed transaction IDs for deduplication\n const _processedTransactions = new Set<string>();\n const _transactionOrder: string[] = [];\n\n // ==========================================================================\n // Helper Functions\n // ==========================================================================\n\n /**\n * Records a transaction as processed, maintaining the history limit.\n */\n const recordTransaction = (transactionId: string): void => {\n _processedTransactions.add(transactionId);\n _transactionOrder.push(transactionId);\n\n // Evict oldest transactions if over limit\n while (_transactionOrder.length > maxTransactionHistory) {\n const oldest = _transactionOrder.shift();\n if (oldest) {\n _processedTransactions.delete(oldest);\n }\n }\n };\n\n /**\n * Validates that the transaction can be applied to the current state.\n * Creates a temporary document and attempts to apply the operations.\n */\n const validateTransaction = (\n transaction: Transaction.Transaction\n ): { valid: true } | { valid: false; reason: string } => {\n // Check for empty transaction\n if (Transaction.isEmpty(transaction)) {\n return { valid: false, reason: \"Transaction is empty\" };\n }\n\n // Check for duplicate transaction\n if (_processedTransactions.has(transaction.id)) {\n return { valid: false, reason: \"Transaction has already been processed\" };\n }\n\n // Create a temporary document with current state to test the operations\n const currentState = _document.get();\n const tempDoc = Document.make(schema, { initial: currentState });\n\n try {\n // Attempt to apply all operations\n tempDoc.apply(transaction.ops);\n return { valid: true };\n } catch (error) {\n // Operations failed to apply\n const message = error instanceof Error ? error.message : String(error);\n return { valid: false, reason: message };\n }\n };\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n const serverDocument: ServerDocument<TSchema> = {\n schema,\n\n get: (): Primitive.InferState<TSchema> | undefined => {\n return _document.get();\n },\n\n getVersion: (): number => {\n return _version;\n },\n\n submit: (transaction: Transaction.Transaction): SubmitResult => {\n // Validate the transaction\n const validation = validateTransaction(transaction);\n\n if (!validation.valid) {\n // Notify rejection callback if provided\n onRejection?.(transaction.id, validation.reason);\n\n return {\n success: false,\n reason: validation.reason,\n };\n }\n\n // Apply the transaction to the authoritative state\n try {\n _document.apply(transaction.ops);\n } catch (error) {\n // This shouldn't happen since we validated, but handle gracefully\n const reason = error instanceof Error ? error.message : String(error);\n onRejection?.(transaction.id, reason);\n return { success: false, reason };\n }\n\n // Increment version\n _version += 1;\n\n // Record as processed\n recordTransaction(transaction.id);\n\n // Broadcast the confirmed transaction\n const message: TransactionMessage = {\n type: \"transaction\",\n transaction,\n version: _version,\n };\n onBroadcast(message);\n\n return {\n success: true,\n version: _version,\n };\n },\n\n getSnapshot: (): SnapshotMessage => {\n return {\n type: \"snapshot\",\n state: _document.get(),\n version: _version,\n };\n },\n\n hasProcessed: (transactionId: string): boolean => {\n return _processedTransactions.has(transactionId);\n },\n };\n\n return serverDocument;\n};\n","import type * as Transaction from \"../Transaction\";\n\n// =============================================================================\n// Server Errors\n// =============================================================================\n\n/**\n * Base error class for all mimic-server errors.\n */\nexport class MimicServerError extends Error {\n readonly _tag: string = \"MimicServerError\";\n constructor(message: string) {\n super(message);\n this.name = \"MimicServerError\";\n }\n}\n\n/**\n * Error thrown when a transaction fails validation.\n */\nexport class ValidationError extends MimicServerError {\n override readonly _tag = \"ValidationError\";\n readonly transactionId: string;\n\n constructor(transactionId: string, message: string) {\n super(`Transaction ${transactionId} validation failed: ${message}`);\n this.name = \"ValidationError\";\n this.transactionId = transactionId;\n }\n}\n\n/**\n * Error thrown when an operation is invalid for the current schema.\n */\nexport class InvalidOperationError extends MimicServerError {\n override readonly _tag = \"InvalidOperationError\";\n readonly operationKind: string;\n readonly path: string;\n\n constructor(operationKind: string, path: string, message: string) {\n super(`Invalid operation ${operationKind} at path \"${path}\": ${message}`);\n this.name = \"InvalidOperationError\";\n this.operationKind = operationKind;\n this.path = path;\n }\n}\n\n/**\n * Error thrown when an operation cannot be applied to the current state.\n */\nexport class StateValidationError extends MimicServerError {\n override readonly _tag = \"StateValidationError\";\n readonly transactionId: string;\n override readonly cause?: Error;\n\n constructor(transactionId: string, message: string, cause?: Error) {\n super(`Transaction ${transactionId} cannot be applied to current state: ${message}`);\n this.name = \"StateValidationError\";\n this.transactionId = transactionId;\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when attempting to apply an empty transaction.\n */\nexport class EmptyTransactionError extends MimicServerError {\n override readonly _tag = \"EmptyTransactionError\";\n readonly transactionId: string;\n\n constructor(transactionId: string) {\n super(`Transaction ${transactionId} is empty and cannot be applied`);\n this.name = \"EmptyTransactionError\";\n this.transactionId = transactionId;\n }\n}\n\n/**\n * Error thrown when a duplicate transaction is submitted.\n */\nexport class DuplicateTransactionError extends MimicServerError {\n override readonly _tag = \"DuplicateTransactionError\";\n readonly transactionId: string;\n\n constructor(transactionId: string) {\n super(`Transaction ${transactionId} has already been processed`);\n this.name = \"DuplicateTransactionError\";\n this.transactionId = transactionId;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic",
3
- "version": "0.0.1-alpha.6",
3
+ "version": "0.0.1-alpha.8",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,9 +9,21 @@
9
9
  },
10
10
  "main": "./src/index.ts",
11
11
  "exports": {
12
- ".": "./src/index.ts",
13
- "./server": "./src/server/index.ts",
14
- "./client": "./src/client/index.ts"
12
+ ".": {
13
+ "import": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "require": "./dist/index.cjs"
16
+ },
17
+ "./server": {
18
+ "import": "./dist/server/index.mjs",
19
+ "types": "./dist/server/index.d.mts",
20
+ "require": "./dist/server/index.cjs"
21
+ },
22
+ "./client": {
23
+ "import": "./dist/client/index.mjs",
24
+ "types": "./dist/client/index.d.mts",
25
+ "require": "./dist/client/index.cjs"
26
+ }
15
27
  },
16
28
  "devDependencies": {
17
29
  "@effect/vitest": "^0.27.0",
@@ -19,7 +31,7 @@
19
31
  "typescript": "5.8.3",
20
32
  "vite-tsconfig-paths": "^5.1.4",
21
33
  "vitest": "^3.2.4",
22
- "@voidhash/tsconfig": "0.0.1-alpha.6"
34
+ "@voidhash/tsconfig": "0.0.1-alpha.7"
23
35
  },
24
36
  "peerDependencies": {
25
37
  "effect": "^3.19.12"
@@ -5,9 +5,10 @@ import * as OperationPath from "../OperationPath";
5
5
  import * as ProxyEnvironment from "../ProxyEnvironment";
6
6
  import * as Transform from "../Transform";
7
7
  import * as FractionalIndex from "../FractionalIndex";
8
- import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot } from "../Primitive";
8
+ import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot, InferSetInput } from "../Primitive";
9
9
  import { ValidationError } from "../Primitive";
10
- import { runValidators } from "./shared";
10
+ import { runValidators, applyDefaults } from "./shared";
11
+ import { StructPrimitive, StructSetInput } from "./Struct";
11
12
 
12
13
 
13
14
  /**
@@ -46,15 +47,28 @@ export interface ArrayEntrySnapshot<TElement extends AnyPrimitive> {
46
47
  */
47
48
  export type ArraySnapshot<TElement extends AnyPrimitive> = readonly ArrayEntrySnapshot<TElement>[];
48
49
 
50
+ /**
51
+ * Compute the input type for array element values.
52
+ * Uses StructSetInput directly for struct elements so that:
53
+ * - Fields that are required AND have no default must be provided
54
+ * - Fields that are optional OR have defaults can be omitted
55
+ *
56
+ * For non-struct elements, falls back to InferSetInput.
57
+ */
58
+ export type ArrayElementSetInput<TElement extends AnyPrimitive> =
59
+ TElement extends StructPrimitive<infer TFields, any, any>
60
+ ? StructSetInput<TFields>
61
+ : InferSetInput<TElement>;
62
+
49
63
  export interface ArrayProxy<TElement extends AnyPrimitive> {
50
64
  /** Gets the current array entries (sorted by position) */
51
65
  get(): ArrayState<TElement>;
52
- /** Replaces the entire array with new values (generates new IDs and positions) */
53
- set(values: readonly InferState<TElement>[]): void;
54
- /** Appends a value to the end of the array */
55
- push(value: InferState<TElement>): void;
56
- /** Inserts a value at the specified visual index */
57
- insertAt(index: number, value: InferState<TElement>): void;
66
+ /** Replaces the entire array with new values (generates new IDs and positions, applies defaults) */
67
+ set(values: readonly ArrayElementSetInput<TElement>[]): void;
68
+ /** Appends a value to the end of the array (applies defaults for struct elements) */
69
+ push(value: ArrayElementSetInput<TElement>): void;
70
+ /** Inserts a value at the specified visual index (applies defaults for struct elements) */
71
+ insertAt(index: number, value: ArrayElementSetInput<TElement>): void;
58
72
  /** Removes the element with the specified ID */
59
73
  remove(id: string): void;
60
74
  /** Moves an element to a new visual index */
@@ -77,12 +91,22 @@ interface ArrayPrimitiveSchema<TElement extends AnyPrimitive> {
77
91
  readonly validators: readonly Validator<ArrayState<TElement>>[];
78
92
  }
79
93
 
80
- export class ArrayPrimitive<TElement extends AnyPrimitive>
81
- implements Primitive<ArrayState<TElement>, ArrayProxy<TElement>>
94
+ /** Input type for array set() - an array of element set inputs */
95
+ export type ArraySetInput<TElement extends AnyPrimitive> = readonly ArrayElementSetInput<TElement>[];
96
+
97
+ /** Input type for array update() - same as set() for arrays */
98
+ export type ArrayUpdateInput<TElement extends AnyPrimitive> = readonly ArrayElementSetInput<TElement>[];
99
+
100
+ export class ArrayPrimitive<TElement extends AnyPrimitive, TRequired extends boolean = false, THasDefault extends boolean = false>
101
+ implements Primitive<ArrayState<TElement>, ArrayProxy<TElement>, TRequired, THasDefault, ArraySetInput<TElement>, ArrayUpdateInput<TElement>>
82
102
  {
83
103
  readonly _tag = "ArrayPrimitive" as const;
84
104
  readonly _State!: ArrayState<TElement>;
85
105
  readonly _Proxy!: ArrayProxy<TElement>;
106
+ readonly _TRequired!: TRequired;
107
+ readonly _THasDefault!: THasDefault;
108
+ readonly TSetInput!: ArraySetInput<TElement>;
109
+ readonly TUpdateInput!: ArrayUpdateInput<TElement>;
86
110
 
87
111
  private readonly _schema: ArrayPrimitiveSchema<TElement>;
88
112
 
@@ -118,7 +142,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
118
142
  }
119
143
 
120
144
  /** Mark this array as required */
121
- required(): ArrayPrimitive<TElement> {
145
+ required(): ArrayPrimitive<TElement, true, THasDefault> {
122
146
  return new ArrayPrimitive({
123
147
  ...this._schema,
124
148
  required: true,
@@ -126,7 +150,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
126
150
  }
127
151
 
128
152
  /** Set a default value for this array */
129
- default(defaultValue: ArrayState<TElement>): ArrayPrimitive<TElement> {
153
+ default(defaultValue: ArrayState<TElement>): ArrayPrimitive<TElement, TRequired, true> {
130
154
  return new ArrayPrimitive({
131
155
  ...this._schema,
132
156
  defaultValue,
@@ -139,7 +163,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
139
163
  }
140
164
 
141
165
  /** Add a custom validation rule */
142
- refine(fn: (value: ArrayState<TElement>) => boolean, message: string): ArrayPrimitive<TElement> {
166
+ refine(fn: (value: ArrayState<TElement>) => boolean, message: string): ArrayPrimitive<TElement, TRequired, THasDefault> {
143
167
  return new ArrayPrimitive({
144
168
  ...this._schema,
145
169
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -147,7 +171,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
147
171
  }
148
172
 
149
173
  /** Minimum array length */
150
- minLength(length: number): ArrayPrimitive<TElement> {
174
+ minLength(length: number): ArrayPrimitive<TElement, TRequired, THasDefault> {
151
175
  return this.refine(
152
176
  (v) => v.length >= length,
153
177
  `Array must have at least ${length} elements`
@@ -155,7 +179,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
155
179
  }
156
180
 
157
181
  /** Maximum array length */
158
- maxLength(length: number): ArrayPrimitive<TElement> {
182
+ maxLength(length: number): ArrayPrimitive<TElement, TRequired, THasDefault> {
159
183
  return this.refine(
160
184
  (v) => v.length <= length,
161
185
  `Array must have at most ${length} elements`
@@ -173,12 +197,17 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
173
197
  return sortByPos(state);
174
198
  };
175
199
 
200
+ // Helper to apply defaults for element values
201
+ const applyElementDefaults = (value: ArrayElementSetInput<TElement>): InferState<TElement> => {
202
+ return applyDefaults(elementPrimitive, value as Partial<InferState<TElement>>) as InferState<TElement>;
203
+ };
204
+
176
205
  return {
177
206
  get: (): ArrayState<TElement> => {
178
207
  return getCurrentState();
179
208
  },
180
209
 
181
- set: (values: readonly InferState<TElement>[]) => {
210
+ set: (values: readonly ArrayElementSetInput<TElement>[]) => {
182
211
  // Generate entries with new IDs and sequential positions
183
212
  const entries: ArrayEntry<InferState<TElement>>[] = [];
184
213
  let prevPos: string | null = null;
@@ -186,7 +215,9 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
186
215
  for (const value of values) {
187
216
  const id = env.generateId();
188
217
  const pos = generatePosBetween(prevPos, null);
189
- entries.push({ id, pos, value });
218
+ // Apply defaults to element value
219
+ const mergedValue = applyElementDefaults(value);
220
+ entries.push({ id, pos, value: mergedValue });
190
221
  prevPos = pos;
191
222
  }
192
223
 
@@ -195,27 +226,31 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
195
226
  );
196
227
  },
197
228
 
198
- push: (value: InferState<TElement>) => {
229
+ push: (value: ArrayElementSetInput<TElement>) => {
199
230
  const sorted = getCurrentState();
200
231
  const lastPos = sorted.length > 0 ? sorted[sorted.length - 1]!.pos : null;
201
232
  const id = env.generateId();
202
233
  const pos = generatePosBetween(lastPos, null);
234
+ // Apply defaults to element value
235
+ const mergedValue = applyElementDefaults(value);
203
236
 
204
237
  env.addOperation(
205
- Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value })
238
+ Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value: mergedValue })
206
239
  );
207
240
  },
208
241
 
209
- insertAt: (index: number, value: InferState<TElement>) => {
242
+ insertAt: (index: number, value: ArrayElementSetInput<TElement>) => {
210
243
  const sorted = getCurrentState();
211
244
  const leftPos = index > 0 && sorted[index - 1] ? sorted[index - 1]!.pos : null;
212
245
  const rightPos = index < sorted.length && sorted[index] ? sorted[index]!.pos : null;
213
246
 
214
247
  const id = env.generateId();
215
248
  const pos = generatePosBetween(leftPos, rightPos);
249
+ // Apply defaults to element value
250
+ const mergedValue = applyElementDefaults(value);
216
251
 
217
252
  env.addOperation(
218
- Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value })
253
+ Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value: mergedValue })
219
254
  );
220
255
  },
221
256
 
@@ -452,6 +487,6 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
452
487
  }
453
488
 
454
489
  /** Creates a new ArrayPrimitive with the given element type */
455
- export const Array = <TElement extends AnyPrimitive>(element: TElement): ArrayPrimitive<TElement> =>
490
+ export const Array = <TElement extends AnyPrimitive>(element: TElement): ArrayPrimitive<TElement, false, false> =>
456
491
  new ArrayPrimitive({ required: false, defaultValue: undefined, element, validators: [] });
457
492
 
@@ -4,17 +4,22 @@ import * as Operation from "../Operation";
4
4
  import * as OperationPath from "../OperationPath";
5
5
  import * as ProxyEnvironment from "../ProxyEnvironment";
6
6
  import * as Transform from "../Transform";
7
- import type { Primitive, PrimitiveInternal, MaybeUndefined, Validator } from "./shared";
7
+ import type { Primitive, PrimitiveInternal, MaybeUndefined, Validator, NeedsValue } from "./shared";
8
8
  import { runValidators, isCompatibleOperation, ValidationError } from "./shared";
9
9
 
10
10
 
11
- export interface BooleanProxy<TDefined extends boolean = false> {
11
+ type InferSetInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<boolean, TRequired, THasDefault>
12
+ type InferUpdateInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<boolean, TRequired, THasDefault>
13
+
14
+ export interface BooleanProxy<TRequired extends boolean = false, THasDefault extends boolean = false> {
12
15
  /** Gets the current boolean value */
13
- get(): MaybeUndefined<boolean, TDefined>;
16
+ get(): MaybeUndefined<boolean, TRequired, THasDefault>;
14
17
  /** Sets the boolean value, generating a boolean.set operation */
15
- set(value: boolean): void;
18
+ set(value: InferSetInput<TRequired, THasDefault>): void;
19
+ /** This is the same as set. Updates the boolean value, generating a boolean.set operation */
20
+ update(value: InferUpdateInput<TRequired, THasDefault>): void;
16
21
  /** Returns a readonly snapshot of the boolean value for rendering */
17
- toSnapshot(): MaybeUndefined<boolean, TDefined>;
22
+ toSnapshot(): MaybeUndefined<boolean, TRequired, THasDefault>;
18
23
  }
19
24
 
20
25
  interface BooleanPrimitiveSchema {
@@ -23,10 +28,14 @@ interface BooleanPrimitiveSchema {
23
28
  readonly validators: readonly Validator<boolean>[];
24
29
  }
25
30
 
26
- export class BooleanPrimitive<TDefined extends boolean = false> implements Primitive<boolean, BooleanProxy<TDefined>> {
31
+ export class BooleanPrimitive<TRequired extends boolean = false, THasDefault extends boolean = false> implements Primitive<boolean, BooleanProxy<TRequired, THasDefault>, TRequired, THasDefault, InferSetInput<TRequired, THasDefault>, InferUpdateInput<TRequired, THasDefault>> {
27
32
  readonly _tag = "BooleanPrimitive" as const;
28
33
  readonly _State!: boolean;
29
- readonly _Proxy!: BooleanProxy<TDefined>;
34
+ readonly _Proxy!: BooleanProxy<TRequired, THasDefault>;
35
+ readonly _TRequired!: TRequired;
36
+ readonly _THasDefault!: THasDefault;
37
+ readonly TUpdateInput!: InferUpdateInput<TRequired, THasDefault>;
38
+ readonly TSetInput!: InferSetInput<TRequired, THasDefault>;
30
39
 
31
40
  private readonly _schema: BooleanPrimitiveSchema;
32
41
 
@@ -44,7 +53,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
44
53
  }
45
54
 
46
55
  /** Mark this boolean as required */
47
- required(): BooleanPrimitive<true> {
56
+ required(): BooleanPrimitive<true, THasDefault> {
48
57
  return new BooleanPrimitive({
49
58
  ...this._schema,
50
59
  required: true,
@@ -52,7 +61,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
52
61
  }
53
62
 
54
63
  /** Set a default value for this boolean */
55
- default(defaultValue: boolean): BooleanPrimitive<true> {
64
+ default(defaultValue: boolean): BooleanPrimitive<TRequired, true> {
56
65
  return new BooleanPrimitive({
57
66
  ...this._schema,
58
67
  defaultValue,
@@ -60,29 +69,34 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
60
69
  }
61
70
 
62
71
  /** Add a custom validation rule */
63
- refine(fn: (value: boolean) => boolean, message: string): BooleanPrimitive<TDefined> {
72
+ refine(fn: (value: boolean) => boolean, message: string): BooleanPrimitive<TRequired, THasDefault> {
64
73
  return new BooleanPrimitive({
65
74
  ...this._schema,
66
75
  validators: [...this._schema.validators, { validate: fn, message }],
67
76
  });
68
77
  }
69
78
 
70
- readonly _internal: PrimitiveInternal<boolean, BooleanProxy<TDefined>> = {
71
- createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): BooleanProxy<TDefined> => {
79
+ readonly _internal: PrimitiveInternal<boolean, BooleanProxy<TRequired, THasDefault>> = {
80
+ createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): BooleanProxy<TRequired, THasDefault> => {
72
81
  const defaultValue = this._schema.defaultValue;
73
82
  return {
74
- get: (): MaybeUndefined<boolean, TDefined> => {
83
+ get: (): MaybeUndefined<boolean, TRequired, THasDefault> => {
75
84
  const state = env.getState(operationPath) as boolean | undefined;
76
- return (state ?? defaultValue) as MaybeUndefined<boolean, TDefined>;
85
+ return (state ?? defaultValue) as MaybeUndefined<boolean, TRequired, THasDefault>;
86
+ },
87
+ set: (value: InferSetInput<TRequired, THasDefault>) => {
88
+ env.addOperation(
89
+ Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
90
+ );
77
91
  },
78
- set: (value: boolean) => {
92
+ update: (value: InferUpdateInput<TRequired, THasDefault>) => {
79
93
  env.addOperation(
80
94
  Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
81
95
  );
82
96
  },
83
- toSnapshot: (): MaybeUndefined<boolean, TDefined> => {
97
+ toSnapshot: (): MaybeUndefined<boolean, TRequired, THasDefault> => {
84
98
  const state = env.getState(operationPath) as boolean | undefined;
85
- return (state ?? defaultValue) as MaybeUndefined<boolean, TDefined>;
99
+ return (state ?? defaultValue) as MaybeUndefined<boolean, TRequired, THasDefault>;
86
100
  },
87
101
  };
88
102
  },
@@ -123,6 +137,6 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
123
137
  }
124
138
 
125
139
  /** Creates a new BooleanPrimitive */
126
- export const Boolean = (): BooleanPrimitive<false> =>
140
+ export const Boolean = (): BooleanPrimitive<false, false> =>
127
141
  new BooleanPrimitive({ required: false, defaultValue: undefined, validators: [] });
128
142
 
@@ -4,7 +4,7 @@ import * as Operation from "../Operation";
4
4
  import * as OperationPath from "../OperationPath";
5
5
  import * as ProxyEnvironment from "../ProxyEnvironment";
6
6
  import * as Transform from "../Transform";
7
- import type { Primitive, PrimitiveInternal, MaybeUndefined, InferState } from "./shared";
7
+ import type { Primitive, PrimitiveInternal, MaybeUndefined, InferState, NeedsValue } from "./shared";
8
8
  import { ValidationError } from "./shared";
9
9
  import { StringPrimitive } from "./String";
10
10
  import { NumberPrimitive } from "./Number";
@@ -15,14 +15,17 @@ import { LiteralPrimitive, LiteralValue } from "./Literal";
15
15
  // Either Primitive - Simple Type Union
16
16
  // =============================================================================
17
17
 
18
+ type InferSetInput<TVariants extends readonly ScalarPrimitive[], TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<InferEitherState<TVariants>, TRequired, THasDefault>
19
+ type InferUpdateInput<TVariants extends readonly ScalarPrimitive[], TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<InferEitherState<TVariants>, TRequired, THasDefault>
20
+
18
21
  /**
19
22
  * Scalar primitives that can be used as variants in Either
20
23
  */
21
24
  export type ScalarPrimitive =
22
- | StringPrimitive<any>
23
- | NumberPrimitive<any>
24
- | BooleanPrimitive<any>
25
- | LiteralPrimitive<any, any>;
25
+ | StringPrimitive<any, any>
26
+ | NumberPrimitive<any, any>
27
+ | BooleanPrimitive<any, any>
28
+ | LiteralPrimitive<any, any, any>;
26
29
 
27
30
  /**
28
31
  * Infer the union state type from a tuple of scalar primitives
@@ -49,18 +52,21 @@ export interface EitherMatchHandlers<R> {
49
52
  /**
50
53
  * Proxy for accessing Either values
51
54
  */
52
- export interface EitherProxy<TVariants extends readonly ScalarPrimitive[], TDefined extends boolean = false> {
55
+ export interface EitherProxy<TVariants extends readonly ScalarPrimitive[], TRequired extends boolean = false, THasDefault extends boolean = false> {
53
56
  /** Gets the current value */
54
- get(): MaybeUndefined<InferEitherState<TVariants>, TDefined>;
57
+ get(): MaybeUndefined<InferEitherState<TVariants>, TRequired, THasDefault>;
55
58
 
56
59
  /** Sets the value to any of the allowed variant types */
57
- set(value: InferEitherState<TVariants>): void;
60
+ set(value: InferSetInput<TVariants, TRequired, THasDefault>): void;
61
+
62
+ /** This is the same as set. Updates the value, generating an either.set operation */
63
+ update(value: InferUpdateInput<TVariants, TRequired, THasDefault>): void;
58
64
 
59
65
  /** Pattern match on the value type */
60
66
  match<R>(handlers: EitherMatchHandlers<R>): R | undefined;
61
67
 
62
68
  /** Returns a readonly snapshot of the value for rendering */
63
- toSnapshot(): MaybeUndefined<InferEitherSnapshot<TVariants>, TDefined>;
69
+ toSnapshot(): MaybeUndefined<InferEitherSnapshot<TVariants>, TRequired, THasDefault>;
64
70
  }
65
71
 
66
72
  interface EitherPrimitiveSchema<TVariants extends readonly ScalarPrimitive[]> {
@@ -69,12 +75,16 @@ interface EitherPrimitiveSchema<TVariants extends readonly ScalarPrimitive[]> {
69
75
  readonly variants: TVariants;
70
76
  }
71
77
 
72
- export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefined extends boolean = false>
73
- implements Primitive<InferEitherState<TVariants>, EitherProxy<TVariants, TDefined>>
78
+ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TRequired extends boolean = false, THasDefault extends boolean = false>
79
+ implements Primitive<InferEitherState<TVariants>, EitherProxy<TVariants, TRequired, THasDefault>, TRequired, THasDefault, InferSetInput<TVariants, TRequired, THasDefault>, InferUpdateInput<TVariants, TRequired, THasDefault>>
74
80
  {
75
81
  readonly _tag = "EitherPrimitive" as const;
76
82
  readonly _State!: InferEitherState<TVariants>;
77
- readonly _Proxy!: EitherProxy<TVariants, TDefined>;
83
+ readonly _Proxy!: EitherProxy<TVariants, TRequired, THasDefault>;
84
+ readonly _TRequired!: TRequired;
85
+ readonly _THasDefault!: THasDefault;
86
+ readonly TUpdateInput!: InferUpdateInput<TVariants, TRequired, THasDefault>;
87
+ readonly TSetInput!: InferSetInput<TVariants, TRequired, THasDefault>;
78
88
 
79
89
  private readonly _schema: EitherPrimitiveSchema<TVariants>;
80
90
 
@@ -92,7 +102,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
92
102
  }
93
103
 
94
104
  /** Mark this either as required */
95
- required(): EitherPrimitive<TVariants, true> {
105
+ required(): EitherPrimitive<TVariants, true, THasDefault> {
96
106
  return new EitherPrimitive({
97
107
  ...this._schema,
98
108
  required: true,
@@ -100,7 +110,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
100
110
  }
101
111
 
102
112
  /** Set a default value for this either */
103
- default(defaultValue: InferEitherState<TVariants>): EitherPrimitive<TVariants, true> {
113
+ default(defaultValue: InferEitherState<TVariants>): EitherPrimitive<TVariants, TRequired, true> {
104
114
  return new EitherPrimitive({
105
115
  ...this._schema,
106
116
  defaultValue,
@@ -121,7 +131,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
121
131
  // Check for literal matches first (they take priority)
122
132
  for (const variant of this._schema.variants) {
123
133
  if (variant._tag === "LiteralPrimitive") {
124
- const literalVariant = variant as LiteralPrimitive<any, any>;
134
+ const literalVariant = variant as LiteralPrimitive<any, any, any>;
125
135
  if (value === literalVariant.literal) {
126
136
  return "literal";
127
137
  }
@@ -166,7 +176,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
166
176
  // Check for literal matches first (they take priority)
167
177
  for (const variant of this._schema.variants) {
168
178
  if (variant._tag === "LiteralPrimitive") {
169
- const literalVariant = variant as LiteralPrimitive<any, any>;
179
+ const literalVariant = variant as LiteralPrimitive<any, any, any>;
170
180
  if (value === literalVariant.literal) {
171
181
  return variant;
172
182
  }
@@ -246,19 +256,24 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
246
256
  matchingVariant._internal.applyOperation(undefined, syntheticOp);
247
257
  }
248
258
 
249
- readonly _internal: PrimitiveInternal<InferEitherState<TVariants>, EitherProxy<TVariants, TDefined>> = {
259
+ readonly _internal: PrimitiveInternal<InferEitherState<TVariants>, EitherProxy<TVariants, TRequired, THasDefault>> = {
250
260
  createProxy: (
251
261
  env: ProxyEnvironment.ProxyEnvironment,
252
262
  operationPath: OperationPath.OperationPath
253
- ): EitherProxy<TVariants, TDefined> => {
263
+ ): EitherProxy<TVariants, TRequired, THasDefault> => {
254
264
  const defaultValue = this._schema.defaultValue;
255
265
 
256
266
  return {
257
- get: (): MaybeUndefined<InferEitherState<TVariants>, TDefined> => {
267
+ get: (): MaybeUndefined<InferEitherState<TVariants>, TRequired, THasDefault> => {
258
268
  const state = env.getState(operationPath) as InferEitherState<TVariants> | undefined;
259
- return (state ?? defaultValue) as MaybeUndefined<InferEitherState<TVariants>, TDefined>;
269
+ return (state ?? defaultValue) as MaybeUndefined<InferEitherState<TVariants>, TRequired, THasDefault>;
270
+ },
271
+ set: (value: InferSetInput<TVariants, TRequired, THasDefault>) => {
272
+ env.addOperation(
273
+ Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
274
+ );
260
275
  },
261
- set: (value: InferEitherState<TVariants>) => {
276
+ update: (value: InferUpdateInput<TVariants, TRequired, THasDefault>) => {
262
277
  env.addOperation(
263
278
  Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
264
279
  );
@@ -284,9 +299,9 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
284
299
  return undefined;
285
300
  }
286
301
  },
287
- toSnapshot: (): MaybeUndefined<InferEitherSnapshot<TVariants>, TDefined> => {
302
+ toSnapshot: (): MaybeUndefined<InferEitherSnapshot<TVariants>, TRequired, THasDefault> => {
288
303
  const state = env.getState(operationPath) as InferEitherState<TVariants> | undefined;
289
- return (state ?? defaultValue) as MaybeUndefined<InferEitherSnapshot<TVariants>, TDefined>;
304
+ return (state ?? defaultValue) as MaybeUndefined<InferEitherSnapshot<TVariants>, TRequired, THasDefault>;
290
305
  },
291
306
  };
292
307
  },
@@ -350,7 +365,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
350
365
  */
351
366
  export function Either<TVariants extends readonly ScalarPrimitive[]>(
352
367
  ...variants: TVariants
353
- ): EitherPrimitive<TVariants, false> {
368
+ ): EitherPrimitive<TVariants, false, false> {
354
369
  if (variants.length === 0) {
355
370
  throw new ValidationError("Either requires at least one variant");
356
371
  }
@@ -4,10 +4,20 @@ import * as Operation from "../Operation";
4
4
  import * as OperationPath from "../OperationPath";
5
5
  import * as ProxyEnvironment from "../ProxyEnvironment";
6
6
  import * as Transform from "../Transform";
7
- import type { Primitive, PrimitiveInternal, AnyPrimitive, InferState, InferProxy, InferSnapshot } from "../Primitive";
7
+ import type { Primitive, PrimitiveInternal, AnyPrimitive, InferState, InferProxy, InferSnapshot, InferSetInput, InferUpdateInput } from "../Primitive";
8
8
  import { ValidationError } from "../Primitive";
9
9
  import { runValidators } from "./shared";
10
10
 
11
+ /**
12
+ * Type to infer SetInput from a lazy thunk
13
+ */
14
+ export type InferLazySetInput<T extends () => AnyPrimitive> = InferSetInput<ReturnType<T>>;
15
+
16
+ /**
17
+ * Type to infer UpdateInput from a lazy thunk
18
+ */
19
+ export type InferLazyUpdateInput<T extends () => AnyPrimitive> = InferUpdateInput<ReturnType<T>>;
20
+
11
21
 
12
22
  /**
13
23
  * Type to infer state from a lazy thunk
@@ -25,11 +35,15 @@ export type InferLazyProxy<T extends () => AnyPrimitive> = InferProxy<ReturnType
25
35
  export type InferLazySnapshot<T extends () => AnyPrimitive> = InferSnapshot<ReturnType<T>>;
26
36
 
27
37
  export class LazyPrimitive<TThunk extends () => AnyPrimitive>
28
- implements Primitive<InferLazyState<TThunk>, InferLazyProxy<TThunk>>
38
+ implements Primitive<InferLazyState<TThunk>, InferLazyProxy<TThunk>, false, false, InferLazySetInput<TThunk>, InferLazyUpdateInput<TThunk>>
29
39
  {
30
40
  readonly _tag = "LazyPrimitive" as const;
31
41
  readonly _State!: InferLazyState<TThunk>;
32
42
  readonly _Proxy!: InferLazyProxy<TThunk>;
43
+ readonly _TRequired!: false;
44
+ readonly _THasDefault!: false;
45
+ readonly TSetInput!: InferLazySetInput<TThunk>;
46
+ readonly TUpdateInput!: InferLazyUpdateInput<TThunk>;
33
47
 
34
48
  private readonly _thunk: TThunk;
35
49
  private _resolved: ReturnType<TThunk> | undefined;