@player-tools/fluent 0.12.1--canary.241.6077
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/dist/cjs/index.cjs +2396 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +2276 -0
- package/dist/index.mjs +2276 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
- package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2423 -0
- package/src/core/base-builder/__tests__/fluent-partial.test.ts +179 -0
- package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
- package/src/core/base-builder/__tests__/registry.test.ts +534 -0
- package/src/core/base-builder/__tests__/resolution-mixed-arrays.test.ts +319 -0
- package/src/core/base-builder/__tests__/resolution-pipeline.test.ts +416 -0
- package/src/core/base-builder/__tests__/resolution-switches.test.ts +468 -0
- package/src/core/base-builder/__tests__/resolution-templates.test.ts +255 -0
- package/src/core/base-builder/__tests__/switch.test.ts +815 -0
- package/src/core/base-builder/__tests__/template.test.ts +596 -0
- package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
- package/src/core/base-builder/__tests__/value-storage.test.ts +459 -0
- package/src/core/base-builder/conditional/index.ts +64 -0
- package/src/core/base-builder/context.ts +152 -0
- package/src/core/base-builder/errors.ts +69 -0
- package/src/core/base-builder/fluent-builder-base.ts +308 -0
- package/src/core/base-builder/guards.ts +137 -0
- package/src/core/base-builder/id/generator.ts +290 -0
- package/src/core/base-builder/id/registry.ts +152 -0
- package/src/core/base-builder/index.ts +72 -0
- package/src/core/base-builder/resolution/path-resolver.ts +116 -0
- package/src/core/base-builder/resolution/pipeline.ts +103 -0
- package/src/core/base-builder/resolution/steps/__tests__/nested-asset-wrappers.test.ts +206 -0
- package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
- package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
- package/src/core/base-builder/resolution/steps/builders.ts +84 -0
- package/src/core/base-builder/resolution/steps/mixed-arrays.ts +95 -0
- package/src/core/base-builder/resolution/steps/nested-asset-wrappers.ts +124 -0
- package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
- package/src/core/base-builder/resolution/steps/switches.ts +71 -0
- package/src/core/base-builder/resolution/steps/templates.ts +40 -0
- package/src/core/base-builder/resolution/value-resolver.ts +333 -0
- package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
- package/src/core/base-builder/storage/value-storage.ts +282 -0
- package/src/core/base-builder/types.ts +266 -0
- package/src/core/base-builder/utils.ts +10 -0
- package/src/core/flow/__tests__/index.test.ts +292 -0
- package/src/core/flow/index.ts +118 -0
- package/src/core/index.ts +8 -0
- package/src/core/mocks/generated/action.builder.ts +92 -0
- package/src/core/mocks/generated/choice-item.builder.ts +120 -0
- package/src/core/mocks/generated/choice.builder.ts +134 -0
- package/src/core/mocks/generated/collection.builder.ts +93 -0
- package/src/core/mocks/generated/field-collection.builder.ts +86 -0
- package/src/core/mocks/generated/index.ts +10 -0
- package/src/core/mocks/generated/info.builder.ts +64 -0
- package/src/core/mocks/generated/input.builder.ts +63 -0
- package/src/core/mocks/generated/overview-collection.builder.ts +65 -0
- package/src/core/mocks/generated/splash-collection.builder.ts +93 -0
- package/src/core/mocks/generated/text.builder.ts +47 -0
- package/src/core/mocks/index.ts +1 -0
- package/src/core/mocks/types/action.ts +92 -0
- package/src/core/mocks/types/choice.ts +129 -0
- package/src/core/mocks/types/collection.ts +140 -0
- package/src/core/mocks/types/info.ts +7 -0
- package/src/core/mocks/types/input.ts +7 -0
- package/src/core/mocks/types/text.ts +5 -0
- package/src/core/schema/__tests__/index.test.ts +127 -0
- package/src/core/schema/index.ts +195 -0
- package/src/core/schema/types.ts +7 -0
- package/src/core/switch/__tests__/index.test.ts +156 -0
- package/src/core/switch/index.ts +81 -0
- package/src/core/tagged-template/README.md +448 -0
- package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
- package/src/core/tagged-template/__tests__/index.test.ts +190 -0
- package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
- package/src/core/tagged-template/binding.ts +95 -0
- package/src/core/tagged-template/expression.ts +92 -0
- package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
- package/src/core/tagged-template/index.ts +5 -0
- package/src/core/tagged-template/std.ts +472 -0
- package/src/core/tagged-template/types.ts +123 -0
- package/src/core/template/__tests__/index.test.ts +380 -0
- package/src/core/template/index.ts +196 -0
- package/src/core/utils/index.ts +160 -0
- package/src/fp/README.md +411 -0
- package/src/fp/__tests__/index.test.ts +1178 -0
- package/src/fp/index.ts +386 -0
- package/src/gen/common.ts +15 -0
- package/src/index.ts +5 -0
- package/src/types.ts +203 -0
- package/types/core/base-builder/conditional/index.d.ts +21 -0
- package/types/core/base-builder/context.d.ts +39 -0
- package/types/core/base-builder/errors.d.ts +45 -0
- package/types/core/base-builder/fluent-builder-base.d.ts +147 -0
- package/types/core/base-builder/guards.d.ts +58 -0
- package/types/core/base-builder/id/generator.d.ts +69 -0
- package/types/core/base-builder/id/registry.d.ts +93 -0
- package/types/core/base-builder/index.d.ts +9 -0
- package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
- package/types/core/base-builder/resolution/pipeline.d.ts +27 -0
- package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/nested-asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
- package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
- package/types/core/base-builder/resolution/value-resolver.d.ts +62 -0
- package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
- package/types/core/base-builder/storage/value-storage.d.ts +82 -0
- package/types/core/base-builder/types.d.ts +183 -0
- package/types/core/base-builder/utils.d.ts +2 -0
- package/types/core/flow/index.d.ts +23 -0
- package/types/core/index.d.ts +8 -0
- package/types/core/mocks/index.d.ts +2 -0
- package/types/core/mocks/types/action.d.ts +58 -0
- package/types/core/mocks/types/choice.d.ts +95 -0
- package/types/core/mocks/types/collection.d.ts +102 -0
- package/types/core/mocks/types/info.d.ts +7 -0
- package/types/core/mocks/types/input.d.ts +7 -0
- package/types/core/mocks/types/text.d.ts +5 -0
- package/types/core/schema/index.d.ts +34 -0
- package/types/core/schema/types.d.ts +5 -0
- package/types/core/switch/index.d.ts +21 -0
- package/types/core/tagged-template/binding.d.ts +19 -0
- package/types/core/tagged-template/expression.d.ts +11 -0
- package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
- package/types/core/tagged-template/index.d.ts +6 -0
- package/types/core/tagged-template/std.d.ts +174 -0
- package/types/core/tagged-template/types.d.ts +69 -0
- package/types/core/template/index.d.ts +97 -0
- package/types/core/utils/index.d.ts +47 -0
- package/types/fp/index.d.ts +149 -0
- package/types/gen/common.d.ts +6 -0
- package/types/index.d.ts +3 -0
- package/types/types.d.ts +163 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages auxiliary data storage for builders
|
|
3
|
+
*
|
|
4
|
+
* Auxiliary data is metadata that doesn't appear in the final built object
|
|
5
|
+
* but is used during the build process. This includes:
|
|
6
|
+
* - Templates (stored under "__templates__")
|
|
7
|
+
* - Switches (stored under "__switches__")
|
|
8
|
+
* - Custom metadata
|
|
9
|
+
*
|
|
10
|
+
* This separation keeps builder state clean and makes the build process more explicit
|
|
11
|
+
*/
|
|
12
|
+
export class AuxiliaryStorage {
|
|
13
|
+
private data: Map<string, unknown> = new Map();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Sets auxiliary data with a given key
|
|
17
|
+
*/
|
|
18
|
+
set<T>(key: string, value: T): void {
|
|
19
|
+
this.data.set(key, value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Gets auxiliary data with type assertion
|
|
24
|
+
* The caller is responsible for knowing the correct type
|
|
25
|
+
*/
|
|
26
|
+
get<T>(key: string): T | undefined {
|
|
27
|
+
const value = this.data.get(key);
|
|
28
|
+
return value as T | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pushes an item to an auxiliary data array
|
|
33
|
+
* Creates the array if it doesn't exist
|
|
34
|
+
*/
|
|
35
|
+
push<T>(key: string, item: T): void {
|
|
36
|
+
const existing = this.data.get(key);
|
|
37
|
+
if (Array.isArray(existing)) {
|
|
38
|
+
existing.push(item);
|
|
39
|
+
} else {
|
|
40
|
+
this.data.set(key, [item]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Gets an auxiliary data array
|
|
46
|
+
* Returns empty array if doesn't exist or isn't an array
|
|
47
|
+
*/
|
|
48
|
+
getArray<T>(key: string): T[] {
|
|
49
|
+
const existing = this.data.get(key);
|
|
50
|
+
return Array.isArray(existing) ? existing : [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks if auxiliary data exists for a key
|
|
55
|
+
*/
|
|
56
|
+
has(key: string): boolean {
|
|
57
|
+
return this.data.has(key);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Deletes auxiliary data for a key
|
|
62
|
+
*/
|
|
63
|
+
delete(key: string): boolean {
|
|
64
|
+
return this.data.delete(key);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Clears all auxiliary data
|
|
69
|
+
*/
|
|
70
|
+
clear(): void {
|
|
71
|
+
this.data.clear();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Clones the auxiliary storage, creating an independent copy
|
|
76
|
+
*/
|
|
77
|
+
clone(): AuxiliaryStorage {
|
|
78
|
+
const cloned = new AuxiliaryStorage();
|
|
79
|
+
cloned.data = new Map(this.data);
|
|
80
|
+
return cloned;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BaseBuildContext,
|
|
3
|
+
FluentBuilder,
|
|
4
|
+
MixedArrayMetadata,
|
|
5
|
+
} from "../types";
|
|
6
|
+
import { isFluentBuilder } from "../guards";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages storage for builder property values
|
|
10
|
+
*
|
|
11
|
+
* Values are stored in three different maps based on their type:
|
|
12
|
+
* - values: Static values (strings, numbers, plain objects without builders)
|
|
13
|
+
* - builders: FluentBuilder instances and objects containing builders
|
|
14
|
+
* - mixedArrays: Arrays containing both static values and builders
|
|
15
|
+
*
|
|
16
|
+
* This separation allows efficient resolution during the build process
|
|
17
|
+
*/
|
|
18
|
+
export class ValueStorage<T> {
|
|
19
|
+
private values: Partial<T> = {};
|
|
20
|
+
private builders: Map<
|
|
21
|
+
string,
|
|
22
|
+
FluentBuilder<unknown, BaseBuildContext> | Record<string, unknown>
|
|
23
|
+
> = new Map();
|
|
24
|
+
private mixedArrays: Map<string, MixedArrayMetadata> = new Map();
|
|
25
|
+
|
|
26
|
+
constructor(initial?: Partial<T>) {
|
|
27
|
+
if (initial) {
|
|
28
|
+
Object.entries(initial).forEach(([key, value]) => {
|
|
29
|
+
this.set(key as keyof T, value);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sets a property value, intelligently routing it to the appropriate storage
|
|
36
|
+
*
|
|
37
|
+
* This method performs runtime type checking to determine how to store the value:
|
|
38
|
+
* - FluentBuilder instances → builders Map
|
|
39
|
+
* - Arrays with builders → mixedArrays Map
|
|
40
|
+
* - Objects with builders → builders Map
|
|
41
|
+
* - Everything else → values (static storage)
|
|
42
|
+
*/
|
|
43
|
+
set<K extends keyof T>(key: K, value: unknown): void {
|
|
44
|
+
const keyStr = String(key);
|
|
45
|
+
|
|
46
|
+
if (isFluentBuilder(value)) {
|
|
47
|
+
this.builders.set(keyStr, value);
|
|
48
|
+
delete this.values[key];
|
|
49
|
+
this.mixedArrays.delete(keyStr);
|
|
50
|
+
} else if (Array.isArray(value)) {
|
|
51
|
+
this.handleArrayValue(key, keyStr, value);
|
|
52
|
+
} else if (
|
|
53
|
+
typeof value === "object" &&
|
|
54
|
+
value !== null &&
|
|
55
|
+
this.containsBuilder(value)
|
|
56
|
+
) {
|
|
57
|
+
this.builders.set(keyStr, value as Record<string, unknown>);
|
|
58
|
+
delete this.values[key];
|
|
59
|
+
this.mixedArrays.delete(keyStr);
|
|
60
|
+
} else {
|
|
61
|
+
this.setStaticValue(key, keyStr, value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handles array value storage, detecting mixed arrays with builders
|
|
67
|
+
*/
|
|
68
|
+
private handleArrayValue<K extends keyof T>(
|
|
69
|
+
key: K,
|
|
70
|
+
keyStr: string,
|
|
71
|
+
value: readonly unknown[],
|
|
72
|
+
): void {
|
|
73
|
+
const builderIndices = new Set<number>();
|
|
74
|
+
const objectIndices = new Set<number>();
|
|
75
|
+
|
|
76
|
+
value.forEach((item, index) => {
|
|
77
|
+
if (isFluentBuilder(item)) {
|
|
78
|
+
builderIndices.add(index);
|
|
79
|
+
} else if (
|
|
80
|
+
typeof item === "object" &&
|
|
81
|
+
item !== null &&
|
|
82
|
+
this.containsBuilder(item)
|
|
83
|
+
) {
|
|
84
|
+
objectIndices.add(index);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const hasBuilders = builderIndices.size > 0 || objectIndices.size > 0;
|
|
89
|
+
|
|
90
|
+
if (hasBuilders) {
|
|
91
|
+
this.mixedArrays.set(keyStr, {
|
|
92
|
+
array: value,
|
|
93
|
+
builderIndices,
|
|
94
|
+
objectIndices,
|
|
95
|
+
});
|
|
96
|
+
delete this.values[key];
|
|
97
|
+
} else {
|
|
98
|
+
this.setStaticValue(key, keyStr, value);
|
|
99
|
+
this.mixedArrays.delete(keyStr);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.builders.delete(keyStr);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Sets a static value with proper type handling
|
|
107
|
+
*/
|
|
108
|
+
private setStaticValue<K extends keyof T>(
|
|
109
|
+
key: K,
|
|
110
|
+
keyStr: string,
|
|
111
|
+
value: unknown,
|
|
112
|
+
): void {
|
|
113
|
+
// For known types in T, TypeScript will validate at compile time
|
|
114
|
+
// At runtime, we trust the caller has provided a compatible type
|
|
115
|
+
this.values[key] = value as T[K];
|
|
116
|
+
this.builders.delete(keyStr);
|
|
117
|
+
this.mixedArrays.delete(keyStr);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if an object contains any builders recursively
|
|
122
|
+
* Handles circular references gracefully
|
|
123
|
+
*/
|
|
124
|
+
private containsBuilder(
|
|
125
|
+
obj: unknown,
|
|
126
|
+
visited: WeakSet<object> = new WeakSet(),
|
|
127
|
+
): boolean {
|
|
128
|
+
if (isFluentBuilder(obj)) return true;
|
|
129
|
+
|
|
130
|
+
if (!obj || typeof obj !== "object") return false;
|
|
131
|
+
|
|
132
|
+
if (visited.has(obj)) return false;
|
|
133
|
+
visited.add(obj);
|
|
134
|
+
|
|
135
|
+
if (Array.isArray(obj)) {
|
|
136
|
+
return obj.some((item) => this.containsBuilder(item, visited));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const proto = Object.getPrototypeOf(obj);
|
|
140
|
+
if (proto === Object.prototype || proto === null) {
|
|
141
|
+
return Object.values(obj).some((val) =>
|
|
142
|
+
this.containsBuilder(val, visited),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Checks if a property has been set
|
|
151
|
+
*/
|
|
152
|
+
has<K extends keyof T>(key: K): boolean {
|
|
153
|
+
const keyStr = String(key);
|
|
154
|
+
return (
|
|
155
|
+
key in this.values ||
|
|
156
|
+
this.builders.has(keyStr) ||
|
|
157
|
+
this.mixedArrays.has(keyStr)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Peeks at a property value without resolving builders
|
|
163
|
+
* Returns the raw value if it's static, the array if it's mixed, or undefined if it's a builder
|
|
164
|
+
*/
|
|
165
|
+
peek<K extends keyof T>(key: K): T[K] | undefined {
|
|
166
|
+
const keyStr = String(key);
|
|
167
|
+
|
|
168
|
+
const mixedArray = this.mixedArrays.get(keyStr);
|
|
169
|
+
if (mixedArray) {
|
|
170
|
+
return mixedArray.array as T[K];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (this.builders.has(keyStr)) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return this.values[key];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Gets builder for a property if one is set
|
|
182
|
+
*/
|
|
183
|
+
peekBuilder<K extends keyof T, C extends BaseBuildContext>(
|
|
184
|
+
key: K,
|
|
185
|
+
): FluentBuilder<T[K], C> | undefined {
|
|
186
|
+
const keyStr = String(key);
|
|
187
|
+
const entry = this.builders.get(keyStr);
|
|
188
|
+
if (!entry) return undefined;
|
|
189
|
+
|
|
190
|
+
if (isFluentBuilder<T[K], C>(entry)) {
|
|
191
|
+
return entry;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the type of value stored for a property
|
|
199
|
+
*/
|
|
200
|
+
getValueType<K extends keyof T>(
|
|
201
|
+
key: K,
|
|
202
|
+
): "static" | "builder" | "mixed-array" | "unset" {
|
|
203
|
+
const keyStr = String(key);
|
|
204
|
+
|
|
205
|
+
if (this.mixedArrays.has(keyStr)) {
|
|
206
|
+
return "mixed-array";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (this.builders.has(keyStr)) {
|
|
210
|
+
return "builder";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (key in this.values) {
|
|
214
|
+
return "static";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return "unset";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets all static values for the build pipeline
|
|
222
|
+
*/
|
|
223
|
+
getValues(): Readonly<Partial<T>> {
|
|
224
|
+
return this.values;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Gets all builder entries for the build pipeline
|
|
229
|
+
*/
|
|
230
|
+
getBuilders(): ReadonlyMap<
|
|
231
|
+
string,
|
|
232
|
+
FluentBuilder<unknown, BaseBuildContext> | Record<string, unknown>
|
|
233
|
+
> {
|
|
234
|
+
return this.builders;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Gets all mixed array entries for the build pipeline
|
|
239
|
+
*/
|
|
240
|
+
getMixedArrays(): ReadonlyMap<string, MixedArrayMetadata> {
|
|
241
|
+
return this.mixedArrays;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Unsets a property, removing it from storage
|
|
246
|
+
*/
|
|
247
|
+
unset<K extends keyof T>(key: K): void {
|
|
248
|
+
const keyStr = String(key);
|
|
249
|
+
delete this.values[key];
|
|
250
|
+
this.builders.delete(keyStr);
|
|
251
|
+
this.mixedArrays.delete(keyStr);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clears all properties from storage
|
|
256
|
+
*/
|
|
257
|
+
clear(): void {
|
|
258
|
+
this.values = {};
|
|
259
|
+
this.builders.clear();
|
|
260
|
+
this.mixedArrays.clear();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Clones the storage, creating an independent copy
|
|
265
|
+
*/
|
|
266
|
+
clone(): ValueStorage<T> {
|
|
267
|
+
const cloned = new ValueStorage<T>();
|
|
268
|
+
cloned.values = { ...this.values };
|
|
269
|
+
cloned.builders = new Map(this.builders);
|
|
270
|
+
cloned.mixedArrays = new Map(
|
|
271
|
+
Array.from(this.mixedArrays.entries()).map(([key, metadata]) => [
|
|
272
|
+
key,
|
|
273
|
+
{
|
|
274
|
+
array: metadata.array,
|
|
275
|
+
builderIndices: new Set(metadata.builderIndices),
|
|
276
|
+
objectIndices: new Set(metadata.objectIndices),
|
|
277
|
+
},
|
|
278
|
+
]),
|
|
279
|
+
);
|
|
280
|
+
return cloned;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { Asset, AssetWrapper } from "@player-ui/types";
|
|
2
|
+
import type { TaggedTemplateValue } from "../tagged-template/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Unique symbol to identify FluentBuilder instances
|
|
6
|
+
* Used for runtime type checking of builder objects
|
|
7
|
+
*/
|
|
8
|
+
export const FLUENT_BUILDER_SYMBOL: unique symbol =
|
|
9
|
+
Symbol.for("fluent-builder");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Constants for branch type discriminators
|
|
13
|
+
* Use these instead of string literals to prevent typos
|
|
14
|
+
*/
|
|
15
|
+
export const BranchTypes = {
|
|
16
|
+
SLOT: "slot",
|
|
17
|
+
ARRAY_ITEM: "array-item",
|
|
18
|
+
TEMPLATE: "template",
|
|
19
|
+
SWITCH: "switch",
|
|
20
|
+
CUSTOM: "custom",
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Constants for internal storage keys
|
|
25
|
+
* Used by AuxiliaryStorage to store templates and switches
|
|
26
|
+
*/
|
|
27
|
+
export const StorageKeys = {
|
|
28
|
+
TEMPLATES: "__templates__",
|
|
29
|
+
SWITCHES: "__switches__",
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Constants for common property keys used in asset building
|
|
34
|
+
*/
|
|
35
|
+
export const PropertyKeys = {
|
|
36
|
+
ID: "id",
|
|
37
|
+
TYPE: "type",
|
|
38
|
+
VALUE: "value",
|
|
39
|
+
BINDING: "binding",
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Core interface for all fluent builders
|
|
44
|
+
* Provides build(), peek(), and has() methods for all builder types
|
|
45
|
+
*/
|
|
46
|
+
export interface FluentBuilder<
|
|
47
|
+
T,
|
|
48
|
+
C extends BaseBuildContext = BaseBuildContext,
|
|
49
|
+
> {
|
|
50
|
+
readonly [FLUENT_BUILDER_SYMBOL]: true;
|
|
51
|
+
build(context?: C): T;
|
|
52
|
+
peek<K extends keyof T>(key: K): T[K] | undefined;
|
|
53
|
+
has<K extends keyof T>(key: K): boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Type-erased asset builder interface for generic asset handling
|
|
58
|
+
*/
|
|
59
|
+
export type AnyAssetBuilder<C extends BaseBuildContext = BaseBuildContext> = {
|
|
60
|
+
readonly [FLUENT_BUILDER_SYMBOL]: true;
|
|
61
|
+
build(context?: C): Asset;
|
|
62
|
+
peek(key: string): unknown;
|
|
63
|
+
has(key: string): boolean;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parameters for creating nested build contexts
|
|
68
|
+
* Used by nested context generators to create child contexts
|
|
69
|
+
*/
|
|
70
|
+
export interface NestedContextParams<C extends BaseBuildContext> {
|
|
71
|
+
readonly parentContext: C;
|
|
72
|
+
readonly parameterName: string;
|
|
73
|
+
readonly index?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Function type for custom nested context generation
|
|
78
|
+
* Allows users to customize how child contexts are created
|
|
79
|
+
*/
|
|
80
|
+
export type NestedContextGenerator<C extends BaseBuildContext> = (
|
|
81
|
+
params: NestedContextParams<C>,
|
|
82
|
+
) => C;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Metadata about an asset used for ID generation and context tracking
|
|
86
|
+
*/
|
|
87
|
+
export interface AssetMetadata {
|
|
88
|
+
readonly type?: string;
|
|
89
|
+
readonly binding?: string;
|
|
90
|
+
readonly value?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Base build context interface containing common fields for all builders
|
|
95
|
+
* Extended by specific builder implementations for custom context needs
|
|
96
|
+
*/
|
|
97
|
+
export interface BaseBuildContext {
|
|
98
|
+
readonly parentId?: string;
|
|
99
|
+
readonly parameterName?: string;
|
|
100
|
+
readonly index?: number;
|
|
101
|
+
readonly branch?: IdBranch;
|
|
102
|
+
readonly nestedContextGenerator?: NestedContextGenerator<BaseBuildContext>;
|
|
103
|
+
readonly assetMetadata?: AssetMetadata;
|
|
104
|
+
readonly [key: string]: unknown;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Slot branch for named properties (e.g., "label", "action")
|
|
109
|
+
* Creates IDs like: parent-label, parent-action
|
|
110
|
+
*/
|
|
111
|
+
export interface SlotBranch {
|
|
112
|
+
type: "slot";
|
|
113
|
+
name: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Array item branch for indexed elements
|
|
118
|
+
* Creates IDs like: parent-0, parent-1
|
|
119
|
+
*/
|
|
120
|
+
export interface ArrayItemBranch {
|
|
121
|
+
type: "array-item";
|
|
122
|
+
index: number;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Template branch for template placeholders
|
|
127
|
+
* Creates IDs like: parent-_index_, parent-_index1_
|
|
128
|
+
*/
|
|
129
|
+
export interface TemplateBranch {
|
|
130
|
+
type: "template";
|
|
131
|
+
depth?: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Switch branch for conditional cases
|
|
136
|
+
* Creates IDs like: parent-staticSwitch-0, parent-dynamicSwitch-1
|
|
137
|
+
*/
|
|
138
|
+
export interface SwitchBranch {
|
|
139
|
+
type: "switch";
|
|
140
|
+
index: number;
|
|
141
|
+
kind: "static" | "dynamic";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Custom branch for user-defined ID patterns
|
|
146
|
+
*/
|
|
147
|
+
export interface CustomBranch {
|
|
148
|
+
type: "custom";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Union of all branch types for type-safe ID generation
|
|
153
|
+
*/
|
|
154
|
+
export type IdBranch =
|
|
155
|
+
| SlotBranch
|
|
156
|
+
| ArrayItemBranch
|
|
157
|
+
| TemplateBranch
|
|
158
|
+
| SwitchBranch
|
|
159
|
+
| CustomBranch;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Metadata for arrays containing mixed static values and builders
|
|
163
|
+
* Tracks which indices contain builders for selective resolution
|
|
164
|
+
*/
|
|
165
|
+
export interface MixedArrayMetadata {
|
|
166
|
+
readonly array: readonly unknown[];
|
|
167
|
+
readonly builderIndices: ReadonlySet<number>;
|
|
168
|
+
readonly objectIndices: ReadonlySet<number>;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Metadata for template storage in FluentBuilderBase
|
|
173
|
+
*/
|
|
174
|
+
export interface TemplateMetadata {
|
|
175
|
+
readonly data: string;
|
|
176
|
+
readonly output: string;
|
|
177
|
+
readonly dynamic?: boolean;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Path type for targeting where to inject values in nested structures
|
|
182
|
+
* Example: ["actions", 0, "label"] targets actions[0].label
|
|
183
|
+
*/
|
|
184
|
+
export type ValuePath = ReadonlyArray<string | number>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Metadata for switch storage in FluentBuilderBase
|
|
188
|
+
*/
|
|
189
|
+
export interface SwitchMetadata<C extends BaseBuildContext = BaseBuildContext> {
|
|
190
|
+
readonly path: ValuePath;
|
|
191
|
+
readonly switchFn: (context: C, globalCaseIndex?: number) => unknown;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Helper type for conditional property values in if/ifElse methods
|
|
196
|
+
* Allows passing unwrapped Asset builders to AssetWrapper properties
|
|
197
|
+
* Enables: .if(() => true, "label", text().withValue("..."))
|
|
198
|
+
* Instead of: .if(() => true, "label", { asset: text().withValue("...") })
|
|
199
|
+
*/
|
|
200
|
+
export type ConditionalValue<T, C extends BaseBuildContext> =
|
|
201
|
+
// Case 1: Single AssetWrapper<A> property
|
|
202
|
+
T extends AssetWrapper<infer A> | undefined
|
|
203
|
+
?
|
|
204
|
+
| T
|
|
205
|
+
| FluentBuilder<T, C>
|
|
206
|
+
| FluentBuilder<A, C>
|
|
207
|
+
| A
|
|
208
|
+
| Array<FluentBuilder<A, C> | A>
|
|
209
|
+
| (() =>
|
|
210
|
+
| T
|
|
211
|
+
| FluentBuilder<T, C>
|
|
212
|
+
| FluentBuilder<A, C>
|
|
213
|
+
| A
|
|
214
|
+
| Array<FluentBuilder<A, C> | A>)
|
|
215
|
+
: // Case 2: Array<AssetWrapper<A>> property
|
|
216
|
+
T extends Array<AssetWrapper<infer A>>
|
|
217
|
+
?
|
|
218
|
+
| T
|
|
219
|
+
| Array<
|
|
220
|
+
| AssetWrapper<A>
|
|
221
|
+
| FluentBuilder<AssetWrapper<A>, C>
|
|
222
|
+
| FluentBuilder<A, C>
|
|
223
|
+
| A
|
|
224
|
+
>
|
|
225
|
+
| (() =>
|
|
226
|
+
| T
|
|
227
|
+
| Array<
|
|
228
|
+
| AssetWrapper<A>
|
|
229
|
+
| FluentBuilder<AssetWrapper<A>, C>
|
|
230
|
+
| FluentBuilder<A, C>
|
|
231
|
+
| A
|
|
232
|
+
>)
|
|
233
|
+
: // Case 3: Other properties
|
|
234
|
+
T | FluentBuilder<T, C> | (() => T | FluentBuilder<T, C>);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Transforms property types to allow TaggedTemplateValue for scalars
|
|
238
|
+
* and FluentBuilder for AssetWrapper properties.
|
|
239
|
+
*/
|
|
240
|
+
export type FluentPartialValue<
|
|
241
|
+
T,
|
|
242
|
+
C extends BaseBuildContext = BaseBuildContext,
|
|
243
|
+
> = T extends string
|
|
244
|
+
? T | TaggedTemplateValue<string>
|
|
245
|
+
: T extends number
|
|
246
|
+
? T | TaggedTemplateValue<number>
|
|
247
|
+
: T extends boolean
|
|
248
|
+
? T | TaggedTemplateValue<boolean>
|
|
249
|
+
: T extends bigint
|
|
250
|
+
? T | TaggedTemplateValue<bigint>
|
|
251
|
+
: T extends AssetWrapper<infer A>
|
|
252
|
+
? T | FluentBuilder<A, C> | A
|
|
253
|
+
: T extends Array<AssetWrapper<infer A>>
|
|
254
|
+
? Array<AssetWrapper<A> | FluentBuilder<A, C> | A>
|
|
255
|
+
: T extends Array<infer E>
|
|
256
|
+
? Array<FluentPartialValue<E, C>>
|
|
257
|
+
: T extends object
|
|
258
|
+
? { [K in keyof T]: FluentPartialValue<T[K], C> }
|
|
259
|
+
: T;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Partial type for builder constructors that allows TaggedTemplateValue for scalars.
|
|
263
|
+
*/
|
|
264
|
+
export type FluentPartial<T, C extends BaseBuildContext = BaseBuildContext> = {
|
|
265
|
+
[K in keyof T]?: FluentPartialValue<T[K], C>;
|
|
266
|
+
};
|