@voidhash/mimic 0.0.1-alpha.7 → 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 +224 -765
  24. package/dist/index.d.cts +5 -1152
  25. package/dist/index.d.cts.map +1 -1
  26. package/dist/index.d.mts +5 -1152
  27. package/dist/index.d.mts.map +1 -1
  28. package/dist/index.mjs +69 -569
  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 +16 -4
  38. package/src/primitives/Array.ts +25 -14
  39. package/src/primitives/Boolean.ts +29 -17
  40. package/src/primitives/Either.ts +30 -17
  41. package/src/primitives/Lazy.ts +16 -2
  42. package/src/primitives/Literal.ts +29 -18
  43. package/src/primitives/Number.ts +35 -24
  44. package/src/primitives/String.ts +36 -23
  45. package/src/primitives/Struct.ts +74 -26
  46. package/src/primitives/Tree.ts +30 -14
  47. package/src/primitives/Union.ts +21 -21
  48. package/src/primitives/shared.ts +27 -34
  49. package/tests/primitives/Array.test.ts +108 -0
  50. package/tests/primitives/Struct.test.ts +2 -2
  51. package/tests/primitives/Tree.test.ts +128 -0
  52. package/tsdown.config.ts +1 -1
  53. /package/dist/{chunk-C6wwvPpM.mjs → chunk-CLMFDpHK.mjs} +0 -0
@@ -4,18 +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, AnyPrimitive, Validator } from "../Primitive";
8
- import { ValidationError } from "../Primitive";
9
- import { runValidators, isCompatibleOperation } from "./shared";
7
+ import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, NeedsValue } from "./shared";
8
+ import { ValidationError, runValidators, isCompatibleOperation } from "./shared";
10
9
 
11
10
 
12
- export interface NumberProxy<TDefined extends boolean = false> {
11
+ type InferSetInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<number, TRequired, THasDefault>
12
+ type InferUpdateInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<number, TRequired, THasDefault>
13
+
14
+ export interface NumberProxy<TRequired extends boolean = false, THasDefault extends boolean = false> {
13
15
  /** Gets the current number value */
14
- get(): MaybeUndefined<number, TDefined>;
16
+ get(): MaybeUndefined<number, TRequired, THasDefault>;
15
17
  /** Sets the number value, generating a number.set operation */
16
- set(value: number): void;
18
+ set(value: InferSetInput<TRequired, THasDefault>): void;
19
+ /** This is the same as set. Updates the number value, generating a number.set operation */
20
+ update(value: InferUpdateInput<TRequired, THasDefault>): void;
17
21
  /** Returns a readonly snapshot of the number value for rendering */
18
- toSnapshot(): MaybeUndefined<number, TDefined>;
22
+ toSnapshot(): MaybeUndefined<number, TRequired, THasDefault>;
19
23
  }
20
24
 
21
25
  interface NumberPrimitiveSchema {
@@ -24,12 +28,14 @@ interface NumberPrimitiveSchema {
24
28
  readonly validators: readonly Validator<number>[];
25
29
  }
26
30
 
27
- export class NumberPrimitive<TDefined extends boolean = false, THasDefault extends boolean = false> implements Primitive<number, NumberProxy<TDefined>, TDefined, THasDefault> {
31
+ export class NumberPrimitive<TRequired extends boolean = false, THasDefault extends boolean = false> implements Primitive<number, NumberProxy<TRequired, THasDefault>, TRequired, THasDefault, InferSetInput<TRequired, THasDefault>, InferUpdateInput<TRequired, THasDefault>> {
28
32
  readonly _tag = "NumberPrimitive" as const;
29
33
  readonly _State!: number;
30
- readonly _Proxy!: NumberProxy<TDefined>;
31
- readonly _TDefined!: TDefined;
34
+ readonly _Proxy!: NumberProxy<TRequired, THasDefault>;
35
+ readonly _TRequired!: TRequired;
32
36
  readonly _THasDefault!: THasDefault;
37
+ readonly TUpdateInput!: InferUpdateInput<TRequired, THasDefault>;
38
+ readonly TSetInput!: InferSetInput<TRequired, THasDefault>;
33
39
 
34
40
  private readonly _schema: NumberPrimitiveSchema;
35
41
 
@@ -55,7 +61,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
55
61
  }
56
62
 
57
63
  /** Set a default value for this number */
58
- default(defaultValue: number): NumberPrimitive<true, true> {
64
+ default(defaultValue: number): NumberPrimitive<TRequired, true> {
59
65
  return new NumberPrimitive({
60
66
  ...this._schema,
61
67
  defaultValue,
@@ -63,7 +69,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
63
69
  }
64
70
 
65
71
  /** Add a custom validation rule */
66
- refine(fn: (value: number) => boolean, message: string): NumberPrimitive<TDefined, THasDefault> {
72
+ refine(fn: (value: number) => boolean, message: string): NumberPrimitive<TRequired, THasDefault> {
67
73
  return new NumberPrimitive({
68
74
  ...this._schema,
69
75
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -71,7 +77,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
71
77
  }
72
78
 
73
79
  /** Minimum value (inclusive) */
74
- min(value: number): NumberPrimitive<TDefined, THasDefault> {
80
+ min(value: number): NumberPrimitive<TRequired, THasDefault> {
75
81
  return this.refine(
76
82
  (v) => v >= value,
77
83
  `Number must be at least ${value}`
@@ -79,7 +85,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
79
85
  }
80
86
 
81
87
  /** Maximum value (inclusive) */
82
- max(value: number): NumberPrimitive<TDefined, THasDefault> {
88
+ max(value: number): NumberPrimitive<TRequired, THasDefault> {
83
89
  return this.refine(
84
90
  (v) => v <= value,
85
91
  `Number must be at most ${value}`
@@ -87,7 +93,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
87
93
  }
88
94
 
89
95
  /** Must be positive (> 0) */
90
- positive(): NumberPrimitive<TDefined, THasDefault> {
96
+ positive(): NumberPrimitive<TRequired, THasDefault> {
91
97
  return this.refine(
92
98
  (v) => v > 0,
93
99
  "Number must be positive"
@@ -95,7 +101,7 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
95
101
  }
96
102
 
97
103
  /** Must be negative (< 0) */
98
- negative(): NumberPrimitive<TDefined, THasDefault> {
104
+ negative(): NumberPrimitive<TRequired, THasDefault> {
99
105
  return this.refine(
100
106
  (v) => v < 0,
101
107
  "Number must be negative"
@@ -103,29 +109,34 @@ export class NumberPrimitive<TDefined extends boolean = false, THasDefault exten
103
109
  }
104
110
 
105
111
  /** Must be an integer */
106
- int(): NumberPrimitive<TDefined, THasDefault> {
112
+ int(): NumberPrimitive<TRequired, THasDefault> {
107
113
  return this.refine(
108
114
  (v) => globalThis.Number.isInteger(v),
109
115
  "Number must be an integer"
110
116
  );
111
117
  }
112
118
 
113
- readonly _internal: PrimitiveInternal<number, NumberProxy<TDefined>> = {
114
- createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): NumberProxy<TDefined> => {
119
+ readonly _internal: PrimitiveInternal<number, NumberProxy<TRequired, THasDefault>> = {
120
+ createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): NumberProxy<TRequired, THasDefault> => {
115
121
  const defaultValue = this._schema.defaultValue;
116
122
  return {
117
- get: (): MaybeUndefined<number, TDefined> => {
123
+ get: (): MaybeUndefined<number, TRequired, THasDefault> => {
118
124
  const state = env.getState(operationPath) as number | undefined;
119
- return (state ?? defaultValue) as MaybeUndefined<number, TDefined>;
125
+ return (state ?? defaultValue) as MaybeUndefined<number, TRequired, THasDefault>;
126
+ },
127
+ set: (value: InferSetInput<TRequired, THasDefault>) => {
128
+ env.addOperation(
129
+ Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
130
+ );
120
131
  },
121
- set: (value: number) => {
132
+ update: (value: InferUpdateInput<TRequired, THasDefault>) => {
122
133
  env.addOperation(
123
134
  Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
124
135
  );
125
136
  },
126
- toSnapshot: (): MaybeUndefined<number, TDefined> => {
137
+ toSnapshot: (): MaybeUndefined<number, TRequired, THasDefault> => {
127
138
  const state = env.getState(operationPath) as number | undefined;
128
- return (state ?? defaultValue) as MaybeUndefined<number, TDefined>;
139
+ return (state ?? defaultValue) as MaybeUndefined<number, TRequired, THasDefault>;
129
140
  },
130
141
  };
131
142
  },
@@ -4,20 +4,26 @@ 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
+
11
+ type InferSetInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<string, TRequired, THasDefault>
12
+ type InferUpdateInput<TRequired extends boolean = false, THasDefault extends boolean = false> = NeedsValue<string, TRequired, THasDefault>
13
+
10
14
  // =============================================================================
11
15
  // String Primitive
12
16
  // =============================================================================
13
17
 
14
- export interface StringProxy<TDefined extends boolean = false> {
18
+ export interface StringProxy<TRequired extends boolean = false, THasDefault extends boolean = false> {
15
19
  /** Gets the current string value */
16
- get(): MaybeUndefined<string, TDefined>;
20
+ get(): MaybeUndefined<string, TRequired, THasDefault>;
17
21
  /** Sets the string value, generating a string.set operation */
18
- set(value: string): void;
22
+ set(value: InferSetInput<TRequired, THasDefault>): void;
23
+ /** This is the same as set. Updates the string value, generating a string.set operation */
24
+ update(value: InferUpdateInput<TRequired, THasDefault>): void;
19
25
  /** Returns a readonly snapshot of the string value for rendering */
20
- toSnapshot(): MaybeUndefined<string, TDefined>;
26
+ toSnapshot(): MaybeUndefined<string, TRequired, THasDefault>;
21
27
  }
22
28
 
23
29
  interface StringPrimitiveSchema {
@@ -26,12 +32,14 @@ interface StringPrimitiveSchema {
26
32
  readonly validators: readonly Validator<string>[];
27
33
  }
28
34
 
29
- export class StringPrimitive<TDefined extends boolean = false, THasDefault extends boolean = false> implements Primitive<string, StringProxy<TDefined>, TDefined, THasDefault> {
35
+ export class StringPrimitive<TRequired extends boolean = false, THasDefault extends boolean = false> implements Primitive<string, StringProxy<TRequired, THasDefault>, TRequired, THasDefault, InferSetInput<TRequired, THasDefault>, InferUpdateInput<TRequired, THasDefault>> {
30
36
  readonly _tag = "StringPrimitive" as const;
31
37
  readonly _State!: string;
32
- readonly _Proxy!: StringProxy<TDefined>;
33
- readonly _TDefined!: TDefined;
38
+ readonly _Proxy!: StringProxy<TRequired, THasDefault>;
39
+ readonly _TRequired!: TRequired;
34
40
  readonly _THasDefault!: THasDefault;
41
+ readonly TUpdateInput!: InferUpdateInput<TRequired, THasDefault>;
42
+ readonly TSetInput!: InferSetInput<TRequired, THasDefault>;
35
43
 
36
44
  private readonly _schema: StringPrimitiveSchema;
37
45
 
@@ -57,7 +65,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
57
65
  }
58
66
 
59
67
  /** Set a default value for this string */
60
- default(defaultValue: string): StringPrimitive<true, true> {
68
+ default(defaultValue: string): StringPrimitive<TRequired, true> {
61
69
  return new StringPrimitive({
62
70
  ...this._schema,
63
71
  defaultValue,
@@ -65,7 +73,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
65
73
  }
66
74
 
67
75
  /** Add a custom validation rule */
68
- refine(fn: (value: string) => boolean, message: string): StringPrimitive<TDefined, THasDefault> {
76
+ refine(fn: (value: string) => boolean, message: string): StringPrimitive<TRequired, THasDefault> {
69
77
  return new StringPrimitive({
70
78
  ...this._schema,
71
79
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -73,7 +81,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
73
81
  }
74
82
 
75
83
  /** Minimum string length */
76
- min(length: number): StringPrimitive<TDefined, THasDefault> {
84
+ min(length: number): StringPrimitive<TRequired, THasDefault> {
77
85
  return this.refine(
78
86
  (v) => v.length >= length,
79
87
  `String must be at least ${length} characters`
@@ -81,7 +89,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
81
89
  }
82
90
 
83
91
  /** Maximum string length */
84
- max(length: number): StringPrimitive<TDefined, THasDefault> {
92
+ max(length: number): StringPrimitive<TRequired, THasDefault> {
85
93
  return this.refine(
86
94
  (v) => v.length <= length,
87
95
  `String must be at most ${length} characters`
@@ -89,7 +97,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
89
97
  }
90
98
 
91
99
  /** Exact string length */
92
- length(exact: number): StringPrimitive<TDefined, THasDefault> {
100
+ length(exact: number): StringPrimitive<TRequired, THasDefault> {
93
101
  return this.refine(
94
102
  (v) => v.length === exact,
95
103
  `String must be exactly ${exact} characters`
@@ -97,7 +105,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
97
105
  }
98
106
 
99
107
  /** Match a regex pattern */
100
- regex(pattern: RegExp, message?: string): StringPrimitive<TDefined, THasDefault> {
108
+ regex(pattern: RegExp, message?: string): StringPrimitive<TRequired, THasDefault> {
101
109
  return this.refine(
102
110
  (v) => pattern.test(v),
103
111
  message ?? `String must match pattern ${pattern}`
@@ -105,7 +113,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
105
113
  }
106
114
 
107
115
  /** Validate as email format */
108
- email(): StringPrimitive<TDefined, THasDefault> {
116
+ email(): StringPrimitive<TRequired, THasDefault> {
109
117
  // Simple email regex - covers most common cases
110
118
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
111
119
  return this.refine(
@@ -115,7 +123,7 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
115
123
  }
116
124
 
117
125
  /** Validate as URL format */
118
- url(): StringPrimitive<TDefined, THasDefault> {
126
+ url(): StringPrimitive<TRequired, THasDefault> {
119
127
  return this.refine(
120
128
  (v) => {
121
129
  try {
@@ -129,22 +137,27 @@ export class StringPrimitive<TDefined extends boolean = false, THasDefault exten
129
137
  );
130
138
  }
131
139
 
132
- readonly _internal: PrimitiveInternal<string, StringProxy<TDefined>> = {
133
- createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StringProxy<TDefined> => {
140
+ readonly _internal: PrimitiveInternal<string, StringProxy<TRequired, THasDefault>> = {
141
+ createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StringProxy<TRequired, THasDefault> => {
134
142
  const defaultValue = this._schema.defaultValue;
135
143
  return {
136
- get: (): MaybeUndefined<string, TDefined> => {
144
+ get: (): MaybeUndefined<string, TRequired, THasDefault> => {
137
145
  const state = env.getState(operationPath) as string | undefined;
138
- return (state ?? defaultValue) as MaybeUndefined<string, TDefined>;
146
+ return (state ?? defaultValue) as MaybeUndefined<string, TRequired, THasDefault>;
147
+ },
148
+ set: (value: InferSetInput<TRequired, THasDefault>) => {
149
+ env.addOperation(
150
+ Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
151
+ );
139
152
  },
140
- set: (value: string) => {
153
+ update: (value: InferUpdateInput<TRequired, THasDefault>) => {
141
154
  env.addOperation(
142
155
  Operation.fromDefinition(operationPath, this._opDefinitions.set, value)
143
156
  );
144
157
  },
145
- toSnapshot: (): MaybeUndefined<string, TDefined> => {
158
+ toSnapshot: (): MaybeUndefined<string, TRequired, THasDefault> => {
146
159
  const state = env.getState(operationPath) as string | undefined;
147
- return (state ?? defaultValue) as MaybeUndefined<string, TDefined>;
160
+ return (state ?? defaultValue) as MaybeUndefined<string, TRequired, THasDefault>;
148
161
  },
149
162
  };
150
163
  },
@@ -4,9 +4,57 @@ 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, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot } from "../Primitive";
7
+ import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot, NeedsValue, InferUpdateInput, InferSetInput } from "../Primitive";
8
8
  import { ValidationError } from "../Primitive";
9
- import { runValidators, applyDefaults, StructSetInput } from "./shared";
9
+ import { runValidators, applyDefaults } from "./shared";
10
+
11
+ // =============================================================================
12
+ // Struct Set Input Types
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Determines if a field is required for set() operations.
17
+ * A field is required if: TRequired is true AND THasDefault is false
18
+ */
19
+ type IsRequiredForSet<T> = T extends Primitive<any, any, true, false> ? true : false;
20
+
21
+ /**
22
+ * Extract keys of fields that are required for set() (required without default).
23
+ */
24
+ type RequiredSetKeys<TFields extends Record<string, AnyPrimitive>> = {
25
+ [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? K : never;
26
+ }[keyof TFields];
27
+
28
+ /**
29
+ * Extract keys of fields that are optional for set() (has default OR not required).
30
+ */
31
+ type OptionalSetKeys<TFields extends Record<string, AnyPrimitive>> = {
32
+ [K in keyof TFields]: IsRequiredForSet<TFields[K]> extends true ? never : K;
33
+ }[keyof TFields];
34
+
35
+ /**
36
+ * Compute the input type for set() operations on a struct.
37
+ * Required fields (required without default) must be provided.
38
+ * Optional fields (has default or not required) can be omitted.
39
+ * Uses each field's TSetInput type to handle nested structs correctly.
40
+ */
41
+ export type StructSetInput<TFields extends Record<string, AnyPrimitive>> =
42
+ { readonly [K in RequiredSetKeys<TFields>]: InferSetInput<TFields[K]> } &
43
+ { readonly [K in OptionalSetKeys<TFields>]?: InferSetInput<TFields[K]> };
44
+
45
+ /**
46
+ * Input type for set() - respects required/default status of the struct.
47
+ * If the struct is required without a default, the value must be provided.
48
+ * The value itself uses StructSetInput which handles field-level required/default logic.
49
+ */
50
+ type InferStructSetInput<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean, THasDefault extends boolean> =
51
+ NeedsValue<StructSetInput<TFields>, TRequired, THasDefault>;
52
+
53
+ /**
54
+ * Input type for update() - always partial since update only modifies specified fields.
55
+ * For nested structs, allows recursive partial updates.
56
+ */
57
+ type InferStructUpdateInput<TFields extends Record<string, AnyPrimitive>> = StructUpdateValue<TFields>;
10
58
 
11
59
 
12
60
  /**
@@ -27,29 +75,27 @@ export type InferStructSnapshot<TFields extends Record<string, AnyPrimitive>> =
27
75
 
28
76
  /**
29
77
  * Maps a schema definition to a partial update type.
30
- * For nested structs, allows recursive partial updates.
78
+ * Uses each field's TUpdateInput type, which handles nested updates recursively.
31
79
  */
32
80
  export type StructUpdateValue<TFields extends Record<string, AnyPrimitive>> = {
33
- readonly [K in keyof TFields]?: TFields[K] extends StructPrimitive<infer F, any>
34
- ? StructUpdateValue<F> | InferState<TFields[K]>
35
- : InferState<TFields[K]>;
81
+ readonly [K in keyof TFields]?: InferUpdateInput<TFields[K]>;
36
82
  };
37
83
 
38
84
  /**
39
85
  * Maps a schema definition to its proxy type.
40
86
  * Provides nested field access + get()/set()/toSnapshot() methods for the whole struct.
41
87
  */
42
- export type StructProxy<TFields extends Record<string, AnyPrimitive>, TDefined extends boolean = false> = {
88
+ export type StructProxy<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false> = {
43
89
  readonly [K in keyof TFields]: InferProxy<TFields[K]>;
44
90
  } & {
45
91
  /** Gets the entire struct value */
46
- get(): MaybeUndefined<InferStructState<TFields>, TDefined>;
92
+ get(): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;
47
93
  /** Sets the entire struct value (only fields that are required without defaults must be provided) */
48
- set(value: StructSetInput<TFields>): void;
94
+ set(value: InferStructSetInput<TFields, TRequired, THasDefault>): void;
49
95
  /** Updates only the specified fields (partial update, handles nested structs recursively) */
50
- update(value: StructUpdateValue<TFields>): void;
96
+ update(value: InferStructUpdateInput<TFields>): void;
51
97
  /** Returns a readonly snapshot of the struct for rendering */
52
- toSnapshot(): MaybeUndefined<InferStructSnapshot<TFields>, TDefined>;
98
+ toSnapshot(): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;
53
99
  };
54
100
 
55
101
  interface StructPrimitiveSchema<TFields extends Record<string, AnyPrimitive>> {
@@ -59,14 +105,16 @@ interface StructPrimitiveSchema<TFields extends Record<string, AnyPrimitive>> {
59
105
  readonly validators: readonly Validator<InferStructState<TFields>>[];
60
106
  }
61
107
 
62
- export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefined extends boolean = false, THasDefault extends boolean = false>
63
- implements Primitive<InferStructState<TFields>, StructProxy<TFields, TDefined>, TDefined, THasDefault>
108
+ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TRequired extends boolean = false, THasDefault extends boolean = false>
109
+ implements Primitive<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>, TRequired, THasDefault, InferStructSetInput<TFields, TRequired, THasDefault>, InferStructUpdateInput<TFields>>
64
110
  {
65
111
  readonly _tag = "StructPrimitive" as const;
66
112
  readonly _State!: InferStructState<TFields>;
67
- readonly _Proxy!: StructProxy<TFields, TDefined>;
68
- readonly _TDefined!: TDefined;
113
+ readonly _Proxy!: StructProxy<TFields, TRequired, THasDefault>;
114
+ readonly _TRequired!: TRequired;
69
115
  readonly _THasDefault!: THasDefault;
116
+ readonly TSetInput!: InferStructSetInput<TFields, TRequired, THasDefault>;
117
+ readonly TUpdateInput!: InferStructUpdateInput<TFields>;
70
118
 
71
119
  private readonly _schema: StructPrimitiveSchema<TFields>;
72
120
 
@@ -92,7 +140,7 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
92
140
  }
93
141
 
94
142
  /** Set a default value for this struct */
95
- default(defaultValue: StructSetInput<TFields>): StructPrimitive<TFields, true, true> {
143
+ default(defaultValue: StructSetInput<TFields>): StructPrimitive<TFields, TRequired, true> {
96
144
  // Apply defaults to the provided value
97
145
  const merged = applyDefaults(this as AnyPrimitive, defaultValue as Partial<InferStructState<TFields>>) as InferStructState<TFields>;
98
146
  return new StructPrimitive({
@@ -107,15 +155,15 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
107
155
  }
108
156
 
109
157
  /** Add a custom validation rule (useful for cross-field validation) */
110
- refine(fn: (value: InferStructState<TFields>) => boolean, message: string): StructPrimitive<TFields, TDefined, THasDefault> {
158
+ refine(fn: (value: InferStructState<TFields>) => boolean, message: string): StructPrimitive<TFields, TRequired, THasDefault> {
111
159
  return new StructPrimitive({
112
160
  ...this._schema,
113
161
  validators: [...this._schema.validators, { validate: fn, message }],
114
162
  });
115
163
  }
116
164
 
117
- readonly _internal: PrimitiveInternal<InferStructState<TFields>, StructProxy<TFields, TDefined>> = {
118
- createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields, TDefined> => {
165
+ readonly _internal: PrimitiveInternal<InferStructState<TFields>, StructProxy<TFields, TRequired, THasDefault>> = {
166
+ createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields, TRequired, THasDefault> => {
119
167
  const fields = this._schema.fields;
120
168
  const defaultValue = this._schema.defaultValue;
121
169
 
@@ -148,18 +196,18 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
148
196
 
149
197
  // Create the base object with get/set/update/toSnapshot methods
150
198
  const base = {
151
- get: (): MaybeUndefined<InferStructState<TFields>, TDefined> => {
199
+ get: (): MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault> => {
152
200
  const state = env.getState(operationPath) as InferStructState<TFields> | undefined;
153
- return (state ?? defaultValue) as MaybeUndefined<InferStructState<TFields>, TDefined>;
201
+ return (state ?? defaultValue) as MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;
154
202
  },
155
- set: (value: StructSetInput<TFields>) => {
203
+ set: (value: InferStructSetInput<TFields, TRequired, THasDefault>) => {
156
204
  // Apply defaults for missing fields
157
205
  const merged = applyDefaults(this as AnyPrimitive, value as Partial<InferStructState<TFields>>) as InferStructState<TFields>;
158
206
  env.addOperation(
159
207
  Operation.fromDefinition(operationPath, this._opDefinitions.set, merged)
160
208
  );
161
209
  },
162
- update: (value: StructUpdateValue<TFields>) => {
210
+ update: (value: InferStructUpdateInput<TFields>) => {
163
211
  for (const key in value) {
164
212
  if (Object.prototype.hasOwnProperty.call(value, key)) {
165
213
  const fieldValue = value[key as keyof TFields];
@@ -187,14 +235,14 @@ export class StructPrimitive<TFields extends Record<string, AnyPrimitive>, TDefi
187
235
  }
188
236
  }
189
237
  },
190
- toSnapshot: (): MaybeUndefined<InferStructSnapshot<TFields>, TDefined> => {
238
+ toSnapshot: (): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault> => {
191
239
  const snapshot = buildSnapshot();
192
- return snapshot as MaybeUndefined<InferStructSnapshot<TFields>, TDefined>;
240
+ return snapshot as MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault>;
193
241
  },
194
242
  };
195
243
 
196
244
  // Use a JavaScript Proxy to intercept field access
197
- return new globalThis.Proxy(base as StructProxy<TFields, TDefined>, {
245
+ return new globalThis.Proxy(base as StructProxy<TFields, TRequired, THasDefault>, {
198
246
  get: (target, prop, _receiver) => {
199
247
  // Return base methods (get, set, update, toSnapshot)
200
248
  if (prop === "get") {
@@ -5,11 +5,12 @@ 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, Validator, InferProxy, AnyPrimitive, StructSetInput } from "./shared";
8
+ import type { Primitive, PrimitiveInternal, Validator, InferProxy, AnyPrimitive, InferSetInput, InferUpdateInput } from "./shared";
9
9
  import { ValidationError, applyDefaults } from "./shared";
10
10
  import { runValidators } from "./shared";
11
11
  import type { AnyTreeNodePrimitive, InferTreeNodeType, InferTreeNodeDataState, InferTreeNodeChildren } from "./TreeNode";
12
- import { InferStructState, StructUpdateValue, StructPrimitive } from "./Struct";
12
+ import { InferStructState, StructSetInput, StructUpdateValue } from "./Struct";
13
+ import { StructPrimitive } from "./Struct";
13
14
 
14
15
 
15
16
  /**
@@ -106,21 +107,28 @@ export type InferTreeSnapshot<T extends TreePrimitive<any>> =
106
107
  T extends TreePrimitive<infer TRoot> ? TreeNodeSnapshot<TRoot> : never;
107
108
 
108
109
  /**
109
- * Helper type to infer the update value type from a TreeNode's data
110
+ * Helper type to infer the update value type from a TreeNode's data.
111
+ * Uses StructUpdateValue directly to get field-level partial update semantics.
112
+ * All fields are optional in update operations.
110
113
  */
111
- export type TreeNodeUpdateValue<TNode extends AnyTreeNodePrimitive> =
114
+ export type TreeNodeUpdateValue<TNode extends AnyTreeNodePrimitive> =
112
115
  TNode["data"] extends StructPrimitive<infer TFields, any, any>
113
116
  ? StructUpdateValue<TFields>
114
- : never;
117
+ : InferUpdateInput<TNode["data"]>;
115
118
 
116
119
  /**
117
- * Helper type to infer the input type for node data (with defaults applied).
118
- * Uses StructSetInput for struct data, making fields with defaults optional.
120
+ * Helper type to infer the input type for node data (respects field defaults).
121
+ * Uses StructSetInput directly so that:
122
+ * - Fields that are required AND have no default must be provided
123
+ * - Fields that are optional OR have defaults can be omitted
124
+ *
125
+ * This bypasses the struct-level NeedsValue wrapper since tree inserts
126
+ * always require a data object (even if empty for all-optional fields).
119
127
  */
120
- export type TreeNodeDataSetInput<TNode extends AnyTreeNodePrimitive> =
128
+ export type TreeNodeDataSetInput<TNode extends AnyTreeNodePrimitive> =
121
129
  TNode["data"] extends StructPrimitive<infer TFields, any, any>
122
130
  ? StructSetInput<TFields>
123
- : InferTreeNodeDataState<TNode>;
131
+ : InferSetInput<TNode["data"]>;
124
132
 
125
133
  /**
126
134
  * Typed proxy for a specific node type - provides type-safe data access
@@ -255,14 +263,22 @@ interface TreePrimitiveSchema<TRoot extends AnyTreeNodePrimitive> {
255
263
  readonly validators: readonly Validator<TreeState<TRoot>>[];
256
264
  }
257
265
 
258
- export class TreePrimitive<TRoot extends AnyTreeNodePrimitive, TDefined extends boolean = false, THasDefault extends boolean = false>
259
- implements Primitive<TreeState<TRoot>, TreeProxy<TRoot>, TDefined, THasDefault>
266
+ /** Input type for tree set() - tree state */
267
+ export type TreeSetInput<TRoot extends AnyTreeNodePrimitive> = TreeState<TRoot>;
268
+
269
+ /** Input type for tree update() - same as set() for trees */
270
+ export type TreeUpdateInput<TRoot extends AnyTreeNodePrimitive> = TreeState<TRoot>;
271
+
272
+ export class TreePrimitive<TRoot extends AnyTreeNodePrimitive, TRequired extends boolean = false, THasDefault extends boolean = false>
273
+ implements Primitive<TreeState<TRoot>, TreeProxy<TRoot>, TRequired, THasDefault, TreeSetInput<TRoot>, TreeUpdateInput<TRoot>>
260
274
  {
261
275
  readonly _tag = "TreePrimitive" as const;
262
276
  readonly _State!: TreeState<TRoot>;
263
277
  readonly _Proxy!: TreeProxy<TRoot>;
264
- readonly _TDefined!: TDefined;
278
+ readonly _TRequired!: TRequired;
265
279
  readonly _THasDefault!: THasDefault;
280
+ readonly TSetInput!: TreeSetInput<TRoot>;
281
+ readonly TUpdateInput!: TreeUpdateInput<TRoot>;
266
282
 
267
283
  private readonly _schema: TreePrimitiveSchema<TRoot>;
268
284
  private _nodeTypeRegistry: Map<string, AnyTreeNodePrimitive> | undefined;
@@ -307,7 +323,7 @@ export class TreePrimitive<TRoot extends AnyTreeNodePrimitive, TDefined extends
307
323
  }
308
324
 
309
325
  /** Set a default value for this tree */
310
- default(defaultValue: TreeState<TRoot>): TreePrimitive<TRoot, true, true> {
326
+ default(defaultValue: TreeState<TRoot>): TreePrimitive<TRoot, TRequired, true> {
311
327
  return new TreePrimitive({
312
328
  ...this._schema,
313
329
  defaultValue,
@@ -320,7 +336,7 @@ export class TreePrimitive<TRoot extends AnyTreeNodePrimitive, TDefined extends
320
336
  }
321
337
 
322
338
  /** Add a custom validation rule */
323
- refine(fn: (value: TreeState<TRoot>) => boolean, message: string): TreePrimitive<TRoot, TDefined, THasDefault> {
339
+ refine(fn: (value: TreeState<TRoot>) => boolean, message: string): TreePrimitive<TRoot, TRequired, THasDefault> {
324
340
  return new TreePrimitive({
325
341
  ...this._schema,
326
342
  validators: [...this._schema.validators, { validate: fn, message }],