@voidhash/mimic 0.0.1-alpha.5 → 0.0.1-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic",
3
- "version": "0.0.1-alpha.5",
3
+ "version": "0.0.1-alpha.7",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "typescript": "5.8.3",
20
20
  "vite-tsconfig-paths": "^5.1.4",
21
21
  "vitest": "^3.2.4",
22
- "@voidhash/tsconfig": "0.0.1-alpha.5"
22
+ "@voidhash/tsconfig": "0.0.1-alpha.7"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "effect": "^3.19.12"
@@ -43,7 +43,7 @@ type InitState =
43
43
  /**
44
44
  * Listener for presence changes.
45
45
  */
46
- export interface PresenceListener<TData> {
46
+ export interface PresenceListener<_TData> {
47
47
  /** Called when any presence changes (self or others) */
48
48
  readonly onPresenceChange?: () => void;
49
49
  }
@@ -19,7 +19,7 @@ export class MimicClientError extends Error {
19
19
  * Error thrown when a transaction is rejected by the server.
20
20
  */
21
21
  export class TransactionRejectedError extends MimicClientError {
22
- readonly _tag = "TransactionRejectedError";
22
+ override readonly _tag = "TransactionRejectedError";
23
23
  readonly transaction: Transaction.Transaction;
24
24
  readonly reason: string;
25
25
 
@@ -35,7 +35,7 @@ export class TransactionRejectedError extends MimicClientError {
35
35
  * Error thrown when the transport is not connected.
36
36
  */
37
37
  export class NotConnectedError extends MimicClientError {
38
- readonly _tag = "NotConnectedError";
38
+ override readonly _tag = "NotConnectedError";
39
39
  constructor() {
40
40
  super("Transport is not connected");
41
41
  this.name = "NotConnectedError";
@@ -46,8 +46,8 @@ export class NotConnectedError extends MimicClientError {
46
46
  * Error thrown when connection to the server fails.
47
47
  */
48
48
  export class ConnectionError extends MimicClientError {
49
- readonly _tag = "ConnectionError";
50
- readonly cause?: Error;
49
+ override readonly _tag = "ConnectionError";
50
+ override readonly cause?: Error;
51
51
 
52
52
  constructor(message: string, cause?: Error) {
53
53
  super(message);
@@ -60,7 +60,7 @@ export class ConnectionError extends MimicClientError {
60
60
  * Error thrown when state drift is detected and cannot be recovered.
61
61
  */
62
62
  export class StateDriftError extends MimicClientError {
63
- readonly _tag = "StateDriftError";
63
+ override readonly _tag = "StateDriftError";
64
64
  readonly expectedVersion: number;
65
65
  readonly receivedVersion: number;
66
66
 
@@ -78,7 +78,7 @@ export class StateDriftError extends MimicClientError {
78
78
  * Error thrown when a pending transaction times out waiting for confirmation.
79
79
  */
80
80
  export class TransactionTimeoutError extends MimicClientError {
81
- readonly _tag = "TransactionTimeoutError";
81
+ override readonly _tag = "TransactionTimeoutError";
82
82
  readonly transaction: Transaction.Transaction;
83
83
  readonly timeoutMs: number;
84
84
 
@@ -96,7 +96,7 @@ export class TransactionTimeoutError extends MimicClientError {
96
96
  * Error thrown when rebasing operations fails.
97
97
  */
98
98
  export class RebaseError extends MimicClientError {
99
- readonly _tag = "RebaseError";
99
+ override readonly _tag = "RebaseError";
100
100
  readonly transactionId: string;
101
101
 
102
102
  constructor(transactionId: string, message: string) {
@@ -110,7 +110,7 @@ export class RebaseError extends MimicClientError {
110
110
  * Error thrown when the client document is in an invalid state.
111
111
  */
112
112
  export class InvalidStateError extends MimicClientError {
113
- readonly _tag = "InvalidStateError";
113
+ override readonly _tag = "InvalidStateError";
114
114
  constructor(message: string) {
115
115
  super(message);
116
116
  this.name = "InvalidStateError";
@@ -121,7 +121,7 @@ export class InvalidStateError extends MimicClientError {
121
121
  * Error thrown when WebSocket connection or communication fails.
122
122
  */
123
123
  export class WebSocketError extends MimicClientError {
124
- readonly _tag = "WebSocketError";
124
+ override readonly _tag = "WebSocketError";
125
125
  readonly code?: number;
126
126
  readonly reason?: string;
127
127
 
@@ -137,7 +137,7 @@ export class WebSocketError extends MimicClientError {
137
137
  * Error thrown when authentication fails.
138
138
  */
139
139
  export class AuthenticationError extends MimicClientError {
140
- readonly _tag = "AuthenticationError";
140
+ override readonly _tag = "AuthenticationError";
141
141
  constructor(message: string) {
142
142
  super(message);
143
143
  this.name = "AuthenticationError";
@@ -7,7 +7,8 @@ import * as Transform from "../Transform";
7
7
  import * as FractionalIndex from "../FractionalIndex";
8
8
  import type { Primitive, PrimitiveInternal, MaybeUndefined, AnyPrimitive, Validator, InferState, InferProxy, InferSnapshot } from "../Primitive";
9
9
  import { ValidationError } from "../Primitive";
10
- import { runValidators } from "./shared";
10
+ import { runValidators, applyDefaults, StructSetInput } from "./shared";
11
+ import type { StructPrimitive } from "./Struct";
11
12
 
12
13
 
13
14
  /**
@@ -46,15 +47,25 @@ 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
+ * If the element is a struct, uses StructSetInput (partial with defaults).
53
+ * Otherwise uses the full state type.
54
+ */
55
+ export type ArrayElementSetInput<TElement extends AnyPrimitive> =
56
+ TElement extends StructPrimitive<infer TFields, any, any>
57
+ ? StructSetInput<TFields>
58
+ : InferState<TElement>;
59
+
49
60
  export interface ArrayProxy<TElement extends AnyPrimitive> {
50
61
  /** Gets the current array entries (sorted by position) */
51
62
  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;
63
+ /** Replaces the entire array with new values (generates new IDs and positions, applies defaults) */
64
+ set(values: readonly ArrayElementSetInput<TElement>[]): void;
65
+ /** Appends a value to the end of the array (applies defaults for struct elements) */
66
+ push(value: ArrayElementSetInput<TElement>): void;
67
+ /** Inserts a value at the specified visual index (applies defaults for struct elements) */
68
+ insertAt(index: number, value: ArrayElementSetInput<TElement>): void;
58
69
  /** Removes the element with the specified ID */
59
70
  remove(id: string): void;
60
71
  /** Moves an element to a new visual index */
@@ -77,12 +88,14 @@ interface ArrayPrimitiveSchema<TElement extends AnyPrimitive> {
77
88
  readonly validators: readonly Validator<ArrayState<TElement>>[];
78
89
  }
79
90
 
80
- export class ArrayPrimitive<TElement extends AnyPrimitive>
81
- implements Primitive<ArrayState<TElement>, ArrayProxy<TElement>>
91
+ export class ArrayPrimitive<TElement extends AnyPrimitive, TDefined extends boolean = false, THasDefault extends boolean = false>
92
+ implements Primitive<ArrayState<TElement>, ArrayProxy<TElement>, TDefined, THasDefault>
82
93
  {
83
94
  readonly _tag = "ArrayPrimitive" as const;
84
95
  readonly _State!: ArrayState<TElement>;
85
96
  readonly _Proxy!: ArrayProxy<TElement>;
97
+ readonly _TDefined!: TDefined;
98
+ readonly _THasDefault!: THasDefault;
86
99
 
87
100
  private readonly _schema: ArrayPrimitiveSchema<TElement>;
88
101
 
@@ -118,7 +131,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
118
131
  }
119
132
 
120
133
  /** Mark this array as required */
121
- required(): ArrayPrimitive<TElement> {
134
+ required(): ArrayPrimitive<TElement, true, THasDefault> {
122
135
  return new ArrayPrimitive({
123
136
  ...this._schema,
124
137
  required: true,
@@ -126,7 +139,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
126
139
  }
127
140
 
128
141
  /** Set a default value for this array */
129
- default(defaultValue: ArrayState<TElement>): ArrayPrimitive<TElement> {
142
+ default(defaultValue: ArrayState<TElement>): ArrayPrimitive<TElement, true, true> {
130
143
  return new ArrayPrimitive({
131
144
  ...this._schema,
132
145
  defaultValue,
@@ -139,7 +152,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
139
152
  }
140
153
 
141
154
  /** Add a custom validation rule */
142
- refine(fn: (value: ArrayState<TElement>) => boolean, message: string): ArrayPrimitive<TElement> {
155
+ refine(fn: (value: ArrayState<TElement>) => boolean, message: string): ArrayPrimitive<TElement, TDefined, THasDefault> {
143
156
  return new ArrayPrimitive({
144
157
  ...this._schema,
145
158
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -147,7 +160,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
147
160
  }
148
161
 
149
162
  /** Minimum array length */
150
- minLength(length: number): ArrayPrimitive<TElement> {
163
+ minLength(length: number): ArrayPrimitive<TElement, TDefined, THasDefault> {
151
164
  return this.refine(
152
165
  (v) => v.length >= length,
153
166
  `Array must have at least ${length} elements`
@@ -155,7 +168,7 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
155
168
  }
156
169
 
157
170
  /** Maximum array length */
158
- maxLength(length: number): ArrayPrimitive<TElement> {
171
+ maxLength(length: number): ArrayPrimitive<TElement, TDefined, THasDefault> {
159
172
  return this.refine(
160
173
  (v) => v.length <= length,
161
174
  `Array must have at most ${length} elements`
@@ -173,12 +186,17 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
173
186
  return sortByPos(state);
174
187
  };
175
188
 
189
+ // Helper to apply defaults for element values
190
+ const applyElementDefaults = (value: ArrayElementSetInput<TElement>): InferState<TElement> => {
191
+ return applyDefaults(elementPrimitive, value as Partial<InferState<TElement>>) as InferState<TElement>;
192
+ };
193
+
176
194
  return {
177
195
  get: (): ArrayState<TElement> => {
178
196
  return getCurrentState();
179
197
  },
180
198
 
181
- set: (values: readonly InferState<TElement>[]) => {
199
+ set: (values: readonly ArrayElementSetInput<TElement>[]) => {
182
200
  // Generate entries with new IDs and sequential positions
183
201
  const entries: ArrayEntry<InferState<TElement>>[] = [];
184
202
  let prevPos: string | null = null;
@@ -186,7 +204,9 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
186
204
  for (const value of values) {
187
205
  const id = env.generateId();
188
206
  const pos = generatePosBetween(prevPos, null);
189
- entries.push({ id, pos, value });
207
+ // Apply defaults to element value
208
+ const mergedValue = applyElementDefaults(value);
209
+ entries.push({ id, pos, value: mergedValue });
190
210
  prevPos = pos;
191
211
  }
192
212
 
@@ -195,27 +215,31 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
195
215
  );
196
216
  },
197
217
 
198
- push: (value: InferState<TElement>) => {
218
+ push: (value: ArrayElementSetInput<TElement>) => {
199
219
  const sorted = getCurrentState();
200
220
  const lastPos = sorted.length > 0 ? sorted[sorted.length - 1]!.pos : null;
201
221
  const id = env.generateId();
202
222
  const pos = generatePosBetween(lastPos, null);
223
+ // Apply defaults to element value
224
+ const mergedValue = applyElementDefaults(value);
203
225
 
204
226
  env.addOperation(
205
- Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value })
227
+ Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value: mergedValue })
206
228
  );
207
229
  },
208
230
 
209
- insertAt: (index: number, value: InferState<TElement>) => {
231
+ insertAt: (index: number, value: ArrayElementSetInput<TElement>) => {
210
232
  const sorted = getCurrentState();
211
233
  const leftPos = index > 0 && sorted[index - 1] ? sorted[index - 1]!.pos : null;
212
234
  const rightPos = index < sorted.length && sorted[index] ? sorted[index]!.pos : null;
213
235
 
214
236
  const id = env.generateId();
215
237
  const pos = generatePosBetween(leftPos, rightPos);
238
+ // Apply defaults to element value
239
+ const mergedValue = applyElementDefaults(value);
216
240
 
217
241
  env.addOperation(
218
- Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value })
242
+ Operation.fromDefinition(operationPath, this._opDefinitions.insert, { id, pos, value: mergedValue })
219
243
  );
220
244
  },
221
245
 
@@ -452,6 +476,6 @@ export class ArrayPrimitive<TElement extends AnyPrimitive>
452
476
  }
453
477
 
454
478
  /** Creates a new ArrayPrimitive with the given element type */
455
- export const Array = <TElement extends AnyPrimitive>(element: TElement): ArrayPrimitive<TElement> =>
479
+ export const Array = <TElement extends AnyPrimitive>(element: TElement): ArrayPrimitive<TElement, false, false> =>
456
480
  new ArrayPrimitive({ required: false, defaultValue: undefined, element, validators: [] });
457
481
 
@@ -23,10 +23,12 @@ interface BooleanPrimitiveSchema {
23
23
  readonly validators: readonly Validator<boolean>[];
24
24
  }
25
25
 
26
- export class BooleanPrimitive<TDefined extends boolean = false> implements Primitive<boolean, BooleanProxy<TDefined>> {
26
+ export class BooleanPrimitive<TDefined extends boolean = false, THasDefault extends boolean = false> implements Primitive<boolean, BooleanProxy<TDefined>, TDefined, THasDefault> {
27
27
  readonly _tag = "BooleanPrimitive" as const;
28
28
  readonly _State!: boolean;
29
29
  readonly _Proxy!: BooleanProxy<TDefined>;
30
+ readonly _TDefined!: TDefined;
31
+ readonly _THasDefault!: THasDefault;
30
32
 
31
33
  private readonly _schema: BooleanPrimitiveSchema;
32
34
 
@@ -44,7 +46,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
44
46
  }
45
47
 
46
48
  /** Mark this boolean as required */
47
- required(): BooleanPrimitive<true> {
49
+ required(): BooleanPrimitive<true, THasDefault> {
48
50
  return new BooleanPrimitive({
49
51
  ...this._schema,
50
52
  required: true,
@@ -52,7 +54,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
52
54
  }
53
55
 
54
56
  /** Set a default value for this boolean */
55
- default(defaultValue: boolean): BooleanPrimitive<true> {
57
+ default(defaultValue: boolean): BooleanPrimitive<true, true> {
56
58
  return new BooleanPrimitive({
57
59
  ...this._schema,
58
60
  defaultValue,
@@ -60,7 +62,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
60
62
  }
61
63
 
62
64
  /** Add a custom validation rule */
63
- refine(fn: (value: boolean) => boolean, message: string): BooleanPrimitive<TDefined> {
65
+ refine(fn: (value: boolean) => boolean, message: string): BooleanPrimitive<TDefined, THasDefault> {
64
66
  return new BooleanPrimitive({
65
67
  ...this._schema,
66
68
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -87,7 +89,7 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
87
89
  };
88
90
  },
89
91
 
90
- applyOperation: (state: boolean | undefined, operation: Operation.Operation<any, any, any>): boolean => {
92
+ applyOperation: (_state: boolean | undefined, operation: Operation.Operation<any, any, any>): boolean => {
91
93
  if (operation.kind !== "boolean.set") {
92
94
  throw new ValidationError(`BooleanPrimitive cannot apply operation of kind: ${operation.kind}`);
93
95
  }
@@ -123,6 +125,6 @@ export class BooleanPrimitive<TDefined extends boolean = false> implements Primi
123
125
  }
124
126
 
125
127
  /** Creates a new BooleanPrimitive */
126
- export const Boolean = (): BooleanPrimitive<false> =>
128
+ export const Boolean = (): BooleanPrimitive<false, false> =>
127
129
  new BooleanPrimitive({ required: false, defaultValue: undefined, validators: [] });
128
130
 
@@ -19,10 +19,10 @@ import { LiteralPrimitive, LiteralValue } from "./Literal";
19
19
  * Scalar primitives that can be used as variants in Either
20
20
  */
21
21
  export type ScalarPrimitive =
22
- | StringPrimitive<any>
23
- | NumberPrimitive<any>
24
- | BooleanPrimitive<any>
25
- | LiteralPrimitive<any, any>;
22
+ | StringPrimitive<any, any>
23
+ | NumberPrimitive<any, any>
24
+ | BooleanPrimitive<any, any>
25
+ | LiteralPrimitive<any, any, any>;
26
26
 
27
27
  /**
28
28
  * Infer the union state type from a tuple of scalar primitives
@@ -69,12 +69,14 @@ interface EitherPrimitiveSchema<TVariants extends readonly ScalarPrimitive[]> {
69
69
  readonly variants: TVariants;
70
70
  }
71
71
 
72
- export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefined extends boolean = false>
73
- implements Primitive<InferEitherState<TVariants>, EitherProxy<TVariants, TDefined>>
72
+ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefined extends boolean = false, THasDefault extends boolean = false>
73
+ implements Primitive<InferEitherState<TVariants>, EitherProxy<TVariants, TDefined>, TDefined, THasDefault>
74
74
  {
75
75
  readonly _tag = "EitherPrimitive" as const;
76
76
  readonly _State!: InferEitherState<TVariants>;
77
77
  readonly _Proxy!: EitherProxy<TVariants, TDefined>;
78
+ readonly _TDefined!: TDefined;
79
+ readonly _THasDefault!: THasDefault;
78
80
 
79
81
  private readonly _schema: EitherPrimitiveSchema<TVariants>;
80
82
 
@@ -92,7 +94,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
92
94
  }
93
95
 
94
96
  /** Mark this either as required */
95
- required(): EitherPrimitive<TVariants, true> {
97
+ required(): EitherPrimitive<TVariants, true, THasDefault> {
96
98
  return new EitherPrimitive({
97
99
  ...this._schema,
98
100
  required: true,
@@ -100,7 +102,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
100
102
  }
101
103
 
102
104
  /** Set a default value for this either */
103
- default(defaultValue: InferEitherState<TVariants>): EitherPrimitive<TVariants, true> {
105
+ default(defaultValue: InferEitherState<TVariants>): EitherPrimitive<TVariants, true, true> {
104
106
  return new EitherPrimitive({
105
107
  ...this._schema,
106
108
  defaultValue,
@@ -121,7 +123,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
121
123
  // Check for literal matches first (they take priority)
122
124
  for (const variant of this._schema.variants) {
123
125
  if (variant._tag === "LiteralPrimitive") {
124
- const literalVariant = variant as LiteralPrimitive<any, any>;
126
+ const literalVariant = variant as LiteralPrimitive<any, any, any>;
125
127
  if (value === literalVariant.literal) {
126
128
  return "literal";
127
129
  }
@@ -166,7 +168,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
166
168
  // Check for literal matches first (they take priority)
167
169
  for (const variant of this._schema.variants) {
168
170
  if (variant._tag === "LiteralPrimitive") {
169
- const literalVariant = variant as LiteralPrimitive<any, any>;
171
+ const literalVariant = variant as LiteralPrimitive<any, any, any>;
170
172
  if (value === literalVariant.literal) {
171
173
  return variant;
172
174
  }
@@ -350,7 +352,7 @@ export class EitherPrimitive<TVariants extends readonly ScalarPrimitive[], TDefi
350
352
  */
351
353
  export function Either<TVariants extends readonly ScalarPrimitive[]>(
352
354
  ...variants: TVariants
353
- ): EitherPrimitive<TVariants, false> {
355
+ ): EitherPrimitive<TVariants, false, false> {
354
356
  if (variants.length === 0) {
355
357
  throw new ValidationError("Either requires at least one variant");
356
358
  }
@@ -27,10 +27,12 @@ interface LiteralPrimitiveSchema<T extends LiteralValue> {
27
27
  readonly literal: T;
28
28
  }
29
29
 
30
- export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean = false> implements Primitive<T, LiteralProxy<T, TDefined>> {
30
+ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean = false, THasDefault extends boolean = false> implements Primitive<T, LiteralProxy<T, TDefined>, TDefined, THasDefault> {
31
31
  readonly _tag = "LiteralPrimitive" as const;
32
32
  readonly _State!: T;
33
33
  readonly _Proxy!: LiteralProxy<T, TDefined>;
34
+ readonly _TDefined!: TDefined;
35
+ readonly _THasDefault!: THasDefault;
34
36
 
35
37
  private readonly _schema: LiteralPrimitiveSchema<T>;
36
38
 
@@ -48,7 +50,7 @@ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean =
48
50
  }
49
51
 
50
52
  /** Mark this literal as required */
51
- required(): LiteralPrimitive<T, true> {
53
+ required(): LiteralPrimitive<T, true, THasDefault> {
52
54
  return new LiteralPrimitive({
53
55
  ...this._schema,
54
56
  required: true,
@@ -56,7 +58,7 @@ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean =
56
58
  }
57
59
 
58
60
  /** Set a default value for this literal */
59
- default(defaultValue: T): LiteralPrimitive<T, true> {
61
+ default(defaultValue: T): LiteralPrimitive<T, true, true> {
60
62
  return new LiteralPrimitive({
61
63
  ...this._schema,
62
64
  defaultValue,
@@ -88,7 +90,7 @@ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean =
88
90
  };
89
91
  },
90
92
 
91
- applyOperation: (state: T | undefined, operation: Operation.Operation<any, any, any>): T => {
93
+ applyOperation: (_state: T | undefined, operation: Operation.Operation<any, any, any>): T => {
92
94
  if (operation.kind !== "literal.set") {
93
95
  throw new ValidationError(`LiteralPrimitive cannot apply operation of kind: ${operation.kind}`);
94
96
  }
@@ -123,6 +125,6 @@ export class LiteralPrimitive<T extends LiteralValue, TDefined extends boolean =
123
125
  }
124
126
 
125
127
  /** Creates a new LiteralPrimitive with the given literal value */
126
- export const Literal = <T extends LiteralValue>(literal: T): LiteralPrimitive<T, false> =>
128
+ export const Literal = <T extends LiteralValue>(literal: T): LiteralPrimitive<T, false, false> =>
127
129
  new LiteralPrimitive({ required: false, defaultValue: undefined, literal });
128
130
 
@@ -24,10 +24,12 @@ interface NumberPrimitiveSchema {
24
24
  readonly validators: readonly Validator<number>[];
25
25
  }
26
26
 
27
- export class NumberPrimitive<TDefined extends boolean = false> implements Primitive<number, NumberProxy<TDefined>> {
27
+ export class NumberPrimitive<TDefined extends boolean = false, THasDefault extends boolean = false> implements Primitive<number, NumberProxy<TDefined>, TDefined, THasDefault> {
28
28
  readonly _tag = "NumberPrimitive" as const;
29
29
  readonly _State!: number;
30
30
  readonly _Proxy!: NumberProxy<TDefined>;
31
+ readonly _TDefined!: TDefined;
32
+ readonly _THasDefault!: THasDefault;
31
33
 
32
34
  private readonly _schema: NumberPrimitiveSchema;
33
35
 
@@ -45,7 +47,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
45
47
  }
46
48
 
47
49
  /** Mark this number as required */
48
- required(): NumberPrimitive<true> {
50
+ required(): NumberPrimitive<true, THasDefault> {
49
51
  return new NumberPrimitive({
50
52
  ...this._schema,
51
53
  required: true,
@@ -53,7 +55,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
53
55
  }
54
56
 
55
57
  /** Set a default value for this number */
56
- default(defaultValue: number): NumberPrimitive<true> {
58
+ default(defaultValue: number): NumberPrimitive<true, true> {
57
59
  return new NumberPrimitive({
58
60
  ...this._schema,
59
61
  defaultValue,
@@ -61,7 +63,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
61
63
  }
62
64
 
63
65
  /** Add a custom validation rule */
64
- refine(fn: (value: number) => boolean, message: string): NumberPrimitive<TDefined> {
66
+ refine(fn: (value: number) => boolean, message: string): NumberPrimitive<TDefined, THasDefault> {
65
67
  return new NumberPrimitive({
66
68
  ...this._schema,
67
69
  validators: [...this._schema.validators, { validate: fn, message }],
@@ -69,7 +71,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
69
71
  }
70
72
 
71
73
  /** Minimum value (inclusive) */
72
- min(value: number): NumberPrimitive<TDefined> {
74
+ min(value: number): NumberPrimitive<TDefined, THasDefault> {
73
75
  return this.refine(
74
76
  (v) => v >= value,
75
77
  `Number must be at least ${value}`
@@ -77,7 +79,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
77
79
  }
78
80
 
79
81
  /** Maximum value (inclusive) */
80
- max(value: number): NumberPrimitive<TDefined> {
82
+ max(value: number): NumberPrimitive<TDefined, THasDefault> {
81
83
  return this.refine(
82
84
  (v) => v <= value,
83
85
  `Number must be at most ${value}`
@@ -85,7 +87,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
85
87
  }
86
88
 
87
89
  /** Must be positive (> 0) */
88
- positive(): NumberPrimitive<TDefined> {
90
+ positive(): NumberPrimitive<TDefined, THasDefault> {
89
91
  return this.refine(
90
92
  (v) => v > 0,
91
93
  "Number must be positive"
@@ -93,7 +95,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
93
95
  }
94
96
 
95
97
  /** Must be negative (< 0) */
96
- negative(): NumberPrimitive<TDefined> {
98
+ negative(): NumberPrimitive<TDefined, THasDefault> {
97
99
  return this.refine(
98
100
  (v) => v < 0,
99
101
  "Number must be negative"
@@ -101,7 +103,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
101
103
  }
102
104
 
103
105
  /** Must be an integer */
104
- int(): NumberPrimitive<TDefined> {
106
+ int(): NumberPrimitive<TDefined, THasDefault> {
105
107
  return this.refine(
106
108
  (v) => globalThis.Number.isInteger(v),
107
109
  "Number must be an integer"
@@ -128,7 +130,7 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
128
130
  };
129
131
  },
130
132
 
131
- applyOperation: (state: number | undefined, operation: Operation.Operation<any, any, any>): number => {
133
+ applyOperation: (_state: number | undefined, operation: Operation.Operation<any, any, any>): number => {
132
134
  if (operation.kind !== "number.set") {
133
135
  throw new ValidationError(`NumberPrimitive cannot apply operation of kind: ${operation.kind}`);
134
136
  }
@@ -164,6 +166,6 @@ export class NumberPrimitive<TDefined extends boolean = false> implements Primit
164
166
  }
165
167
 
166
168
  /** Creates a new NumberPrimitive */
167
- export const Number = (): NumberPrimitive<false> =>
169
+ export const Number = (): NumberPrimitive<false, false> =>
168
170
  new NumberPrimitive({ required: false, defaultValue: undefined, validators: [] });
169
171