@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.
- package/.turbo/turbo-build.log +43 -15
- package/dist/Document-ChuFrTk1.cjs +571 -0
- package/dist/Document-CwiAFTIq.mjs +438 -0
- package/dist/Document-CwiAFTIq.mjs.map +1 -0
- package/dist/Presence-DKKP4v5X.d.cts +91 -0
- package/dist/Presence-DKKP4v5X.d.cts.map +1 -0
- package/dist/Presence-DdMVKcOv.mjs +110 -0
- package/dist/Presence-DdMVKcOv.mjs.map +1 -0
- package/dist/Presence-N8u7Eppr.d.mts +91 -0
- package/dist/Presence-N8u7Eppr.d.mts.map +1 -0
- package/dist/Presence-gWrmGBeu.cjs +126 -0
- package/dist/Primitive-BK7kfHJZ.d.cts +1165 -0
- package/dist/Primitive-BK7kfHJZ.d.cts.map +1 -0
- package/dist/Primitive-D1kdB6za.d.mts +1165 -0
- package/dist/Primitive-D1kdB6za.d.mts.map +1 -0
- package/dist/client/index.cjs +1456 -0
- package/dist/client/index.d.cts +692 -0
- package/dist/client/index.d.cts.map +1 -0
- package/dist/client/index.d.mts +692 -0
- package/dist/client/index.d.mts.map +1 -0
- package/dist/client/index.mjs +1413 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.cjs +224 -765
- package/dist/index.d.cts +5 -1152
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -1152
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +69 -569
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.cjs +191 -0
- package/dist/server/index.d.cts +148 -0
- package/dist/server/index.d.cts.map +1 -0
- package/dist/server/index.d.mts +148 -0
- package/dist/server/index.d.mts.map +1 -0
- package/dist/server/index.mjs +182 -0
- package/dist/server/index.mjs.map +1 -0
- package/package.json +16 -4
- package/src/primitives/Array.ts +25 -14
- package/src/primitives/Boolean.ts +29 -17
- package/src/primitives/Either.ts +30 -17
- package/src/primitives/Lazy.ts +16 -2
- package/src/primitives/Literal.ts +29 -18
- package/src/primitives/Number.ts +35 -24
- package/src/primitives/String.ts +36 -23
- package/src/primitives/Struct.ts +74 -26
- package/src/primitives/Tree.ts +30 -14
- package/src/primitives/Union.ts +21 -21
- package/src/primitives/shared.ts +27 -34
- package/tests/primitives/Array.test.ts +108 -0
- package/tests/primitives/Struct.test.ts +2 -2
- package/tests/primitives/Tree.test.ts +128 -0
- package/tsdown.config.ts +1 -1
- /package/dist/{chunk-C6wwvPpM.mjs → chunk-CLMFDpHK.mjs} +0 -0
package/src/primitives/Number.ts
CHANGED
|
@@ -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 "
|
|
8
|
-
import { ValidationError } from "
|
|
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
|
-
|
|
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,
|
|
16
|
+
get(): MaybeUndefined<number, TRequired, THasDefault>;
|
|
15
17
|
/** Sets the number value, generating a number.set operation */
|
|
16
|
-
set(value:
|
|
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,
|
|
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<
|
|
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<
|
|
31
|
-
readonly
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
114
|
-
createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): NumberProxy<
|
|
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,
|
|
123
|
+
get: (): MaybeUndefined<number, TRequired, THasDefault> => {
|
|
118
124
|
const state = env.getState(operationPath) as number | undefined;
|
|
119
|
-
return (state ?? defaultValue) as MaybeUndefined<number,
|
|
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
|
-
|
|
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,
|
|
137
|
+
toSnapshot: (): MaybeUndefined<number, TRequired, THasDefault> => {
|
|
127
138
|
const state = env.getState(operationPath) as number | undefined;
|
|
128
|
-
return (state ?? defaultValue) as MaybeUndefined<number,
|
|
139
|
+
return (state ?? defaultValue) as MaybeUndefined<number, TRequired, THasDefault>;
|
|
129
140
|
},
|
|
130
141
|
};
|
|
131
142
|
},
|
package/src/primitives/String.ts
CHANGED
|
@@ -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<
|
|
18
|
+
export interface StringProxy<TRequired extends boolean = false, THasDefault extends boolean = false> {
|
|
15
19
|
/** Gets the current string value */
|
|
16
|
-
get(): MaybeUndefined<string,
|
|
20
|
+
get(): MaybeUndefined<string, TRequired, THasDefault>;
|
|
17
21
|
/** Sets the string value, generating a string.set operation */
|
|
18
|
-
set(value:
|
|
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,
|
|
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<
|
|
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<
|
|
33
|
-
readonly
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
133
|
-
createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StringProxy<
|
|
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,
|
|
144
|
+
get: (): MaybeUndefined<string, TRequired, THasDefault> => {
|
|
137
145
|
const state = env.getState(operationPath) as string | undefined;
|
|
138
|
-
return (state ?? defaultValue) as MaybeUndefined<string,
|
|
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
|
-
|
|
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,
|
|
158
|
+
toSnapshot: (): MaybeUndefined<string, TRequired, THasDefault> => {
|
|
146
159
|
const state = env.getState(operationPath) as string | undefined;
|
|
147
|
-
return (state ?? defaultValue) as MaybeUndefined<string,
|
|
160
|
+
return (state ?? defaultValue) as MaybeUndefined<string, TRequired, THasDefault>;
|
|
148
161
|
},
|
|
149
162
|
};
|
|
150
163
|
},
|
package/src/primitives/Struct.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
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]
|
|
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>,
|
|
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>,
|
|
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:
|
|
94
|
+
set(value: InferStructSetInput<TFields, TRequired, THasDefault>): void;
|
|
49
95
|
/** Updates only the specified fields (partial update, handles nested structs recursively) */
|
|
50
|
-
update(value:
|
|
96
|
+
update(value: InferStructUpdateInput<TFields>): void;
|
|
51
97
|
/** Returns a readonly snapshot of the struct for rendering */
|
|
52
|
-
toSnapshot(): MaybeUndefined<InferStructSnapshot<TFields>,
|
|
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>,
|
|
63
|
-
implements Primitive<InferStructState<TFields>, StructProxy<TFields,
|
|
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,
|
|
68
|
-
readonly
|
|
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,
|
|
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,
|
|
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,
|
|
118
|
-
createProxy: (env: ProxyEnvironment.ProxyEnvironment, operationPath: OperationPath.OperationPath): StructProxy<TFields,
|
|
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>,
|
|
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>,
|
|
201
|
+
return (state ?? defaultValue) as MaybeUndefined<InferStructState<TFields>, TRequired, THasDefault>;
|
|
154
202
|
},
|
|
155
|
-
set: (value:
|
|
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:
|
|
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>,
|
|
238
|
+
toSnapshot: (): MaybeUndefined<InferStructSnapshot<TFields>, TRequired, THasDefault> => {
|
|
191
239
|
const snapshot = buildSnapshot();
|
|
192
|
-
return snapshot as MaybeUndefined<InferStructSnapshot<TFields>,
|
|
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,
|
|
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") {
|
package/src/primitives/Tree.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
-
:
|
|
117
|
+
: InferUpdateInput<TNode["data"]>;
|
|
115
118
|
|
|
116
119
|
/**
|
|
117
|
-
* Helper type to infer the input type for node data (
|
|
118
|
-
* Uses StructSetInput
|
|
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
|
-
:
|
|
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
|
-
|
|
259
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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 }],
|