@axi-engine/fields 0.1.5 → 0.2.1

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/index.d.mts CHANGED
@@ -1,18 +1,13 @@
1
- import { ReadonlySignal, Signal } from '@preact/signals-core';
2
- import { AxiEventEmitter } from '@axi-engine/events';
3
- import { PathType } from '@axi-engine/utils';
1
+ import * as _axi_engine_utils from '@axi-engine/utils';
2
+ import { Subscribable, Constructor, Emitter, PathType } from '@axi-engine/utils';
4
3
 
5
- declare const enum FieldsNodeType {
6
- fieldTree = "FieldTree",
7
- fields = "Fields"
8
- }
9
-
10
- interface FieldPolicy<T> {
4
+ interface Policy<T> {
11
5
  readonly id: string;
12
6
  apply: (val: T) => T;
13
7
  destroy?: () => void;
14
8
  }
15
- declare class ClampPolicy implements FieldPolicy<number> {
9
+
10
+ declare class ClampPolicy implements Policy<number> {
16
11
  min: number;
17
12
  max: number;
18
13
  static readonly id = "clamp";
@@ -21,15 +16,9 @@ declare class ClampPolicy implements FieldPolicy<number> {
21
16
  apply(val: number): number;
22
17
  updateBounds(min: number, max: number): void;
23
18
  }
24
- declare class ClampMinPolicy implements FieldPolicy<number> {
25
- min: number;
26
- static readonly id = "clampMin";
27
- readonly id = "clampMin";
28
- constructor(min: number);
29
- apply(val: number): number;
30
- updateBounds(min: number): void;
31
- }
32
- declare class ClampMaxPolicy implements FieldPolicy<number> {
19
+ declare function clampPolicy(min: number, max: number): ClampPolicy;
20
+
21
+ declare class ClampMaxPolicy implements Policy<number> {
33
22
  max: number;
34
23
  static readonly id = "clampMax";
35
24
  readonly id = "clampMax";
@@ -37,48 +26,21 @@ declare class ClampMaxPolicy implements FieldPolicy<number> {
37
26
  apply(val: number): number;
38
27
  updateBounds(max: number): void;
39
28
  }
40
- declare function clampPolicy(min: number, max: number): ClampPolicy;
41
- declare function clampMinPolicy(min: number): ClampMinPolicy;
42
29
  declare function clampMaxPolicy(max: number): ClampMaxPolicy;
43
30
 
44
- /**
45
- * A reactive state container that wraps a value, making it observable through a Preact Signal.
46
- * It allows applying a pipeline of transformation or validation "policies" before any new value is set.
47
- *
48
- * @template T The type of the value this field holds.
49
- *
50
- */
51
- declare class Field<T> {
31
+ declare class ClampMinPolicy implements Policy<number> {
32
+ min: number;
33
+ static readonly id = "clampMin";
34
+ readonly id = "clampMin";
35
+ constructor(min: number);
36
+ apply(val: number): number;
37
+ updateBounds(min: number): void;
38
+ }
39
+ declare function clampMinPolicy(min: number): ClampMinPolicy;
40
+
41
+ declare class Policies<T> {
52
42
  private readonly policies;
53
- private readonly _val;
54
- /** A unique identifier for the field. */
55
- name: string;
56
- /**
57
- * Creates an instance of a Field.
58
- * @param name A unique identifier for the field.
59
- * @param initialVal The initial value of the field.
60
- * @param options Optional configuration for the field.
61
- * @param options.policies An array of policies to apply to the field's value on every `set` operation.
62
- */
63
- constructor(name: string, initialVal: T, options?: {
64
- policies?: FieldPolicy<T>[];
65
- });
66
- /**
67
- * Gets the current raw value of the field.
68
- * For reactive updates, it's recommended to use the `.signal` property instead.
69
- */
70
- get val(): T;
71
- /**
72
- * Provides readonly access to the underlying Preact Signal.
73
- * Subscribe to this signal to react to value changes.
74
- */
75
- get signal(): ReadonlySignal<T>;
76
- /**
77
- * Sets a new value for the field.
78
- * The provided value will be processed by all registered policies before the underlying signal is updated.
79
- * @param val The new value to set.
80
- */
81
- set(val: T): void;
43
+ get items(): Map<string, Policy<T>>;
82
44
  /**
83
45
  * Retrieves a specific policy instance by its ID.
84
46
  * Useful for accessing a policy's internal state or methods.
@@ -86,30 +48,106 @@ declare class Field<T> {
86
48
  * @param id The unique ID of the policy to retrieve.
87
49
  * @returns The policy instance, or `undefined` if not found.
88
50
  */
89
- getPolicy<P extends FieldPolicy<T>>(id: string): P | undefined;
51
+ get<P extends Policy<T>>(id: string): P | undefined;
90
52
  /**
91
53
  * Adds a new policy to the field or replaces an existing one with the same ID.
92
54
  * The new policy will be applied on the next `set()` operation.
93
55
  * If a policy with the same ID already exists, its `destroy` method will be called before it is replaced.
94
56
  * @param policy The policy instance to add.
95
57
  */
96
- addPolicy(policy: FieldPolicy<T>): void;
58
+ add(policy: Policy<T>): this;
97
59
  /**
98
60
  * Removes a policy from the field by its ID and call `destroy` method.
99
61
  * @param policyId The unique ID of the policy to remove.
100
62
  * @returns `true` if the policy was found and removed, otherwise `false`.
101
63
  */
102
- removePolicy(policyId: string): boolean;
64
+ remove(policyId: string): boolean;
65
+ isEmpty(): boolean;
103
66
  /**
104
67
  * Removes all policies from the field.
105
68
  * After this, `set()` will no longer apply any transformations to the value until new policies are added.
106
69
  */
107
- clearPolicies(): void;
70
+ clear(): void;
108
71
  /**
109
72
  * Forces the current value to be re-processed by all policies.
110
73
  * Useful if a policy's logic has changed and you need to re-evaluate the current state.
111
74
  */
112
- reapplyPolicies(): void;
75
+ apply(val: T): T;
76
+ }
77
+
78
+ interface FieldOptions<T> {
79
+ policies?: Policy<T>[];
80
+ }
81
+ interface Field<T> {
82
+ readonly typeName: string;
83
+ readonly name: string;
84
+ value: T;
85
+ policies: Policies<T>;
86
+ setValueSilently(val: T): void;
87
+ batchUpdate(updateFn: (currentValue: T) => T): void;
88
+ onChange: Subscribable<[newValue: T, oldValue: T]>;
89
+ destroy(): void;
90
+ }
91
+ interface NumericField extends Field<number> {
92
+ readonly min: number | undefined;
93
+ readonly max: number | undefined;
94
+ isMin(): boolean;
95
+ isMax(): boolean;
96
+ inc(val: number): void;
97
+ dec(val: number): void;
98
+ }
99
+ interface BooleanField extends Field<boolean> {
100
+ toggle(): boolean;
101
+ }
102
+ interface StringField extends Field<string> {
103
+ append(str: string | number): this;
104
+ prepend(str: string | number): this;
105
+ trim(): this;
106
+ isEmpty(): boolean;
107
+ clear(): void;
108
+ }
109
+
110
+ /**
111
+ * A state container that wraps a value.
112
+ * It allows applying a pipeline of transformation or validation "policies" before any new value is set.
113
+ *
114
+ * @template T The type of the value this field holds.
115
+ *
116
+ */
117
+ declare class DefaultField<T> implements Field<T> {
118
+ /** A type keyword of the field */
119
+ static readonly typeName: string;
120
+ readonly typeName: string;
121
+ /** A unique identifier for the field. */
122
+ private readonly _name;
123
+ private _value;
124
+ private readonly _onChange;
125
+ readonly onChange: Subscribable<[newValue: T, oldvalue: T]>;
126
+ readonly policies: Policies<T>;
127
+ get name(): string;
128
+ /**
129
+ * Gets the current raw value of the field.
130
+ * For reactive updates, it's recommended to use the `.signal` property instead.
131
+ */
132
+ get value(): T;
133
+ /**
134
+ * Sets a new value for the field.
135
+ * The provided value will be processed by all registered policies before the underlying signal is updated.
136
+ * @param val The new value to set.
137
+ */
138
+ set value(val: T);
139
+ /**
140
+ * Creates an instance of a Field.
141
+ * @param name A unique identifier for the field.
142
+ * @param initialVal The initial value of the field.
143
+ * @param options Optional configuration for the field.
144
+ * @param options.policies An array of policies to apply to the field's value on every `set` operation.
145
+ * @param options.isEqual An function for compare old and new value, by default uses the strictEquals from `utils`
146
+ *
147
+ */
148
+ constructor(name: string, initialVal: T, options?: FieldOptions<T>);
149
+ setValueSilently(val: T): void;
150
+ batchUpdate(updateFn: (currentValue: T) => T): void;
113
151
  /**
114
152
  * Cleans up resources used by the field and its policies.
115
153
  * This should be called when the field is no longer needed to prevent memory leaks from reactive policies.
@@ -117,240 +155,687 @@ declare class Field<T> {
117
155
  destroy(): void;
118
156
  }
119
157
 
120
- interface NumberFieldOptions {
158
+ interface DefaultBooleanFieldOptions extends FieldOptions<boolean> {
159
+ }
160
+ declare class DefaultBooleanField extends DefaultField<boolean> implements BooleanField {
161
+ static readonly typeName: string;
162
+ readonly typeName: string;
163
+ constructor(name: string, initialVal: boolean, options?: DefaultBooleanFieldOptions);
164
+ toggle(): boolean;
165
+ }
166
+
167
+ interface DefaultStringFieldOptions extends FieldOptions<string> {
168
+ }
169
+ declare class DefaultStringField extends DefaultField<string> implements StringField {
170
+ static readonly typeName: string;
171
+ readonly typeName: string;
172
+ constructor(name: string, initialVal: string, options?: DefaultStringFieldOptions);
173
+ append(str: string | number): this;
174
+ prepend(str: string | number): this;
175
+ trim(): this;
176
+ isEmpty(): boolean;
177
+ clear(): void;
178
+ }
179
+
180
+ interface DefaultNumericFieldOptions extends FieldOptions<number> {
121
181
  min?: number;
122
182
  max?: number;
123
- policies?: FieldPolicy<number>[];
124
183
  }
125
- declare class NumberField extends Field<number> {
184
+ declare class DefaultNumericField extends DefaultField<number> implements NumericField {
185
+ static readonly typeName: string;
186
+ readonly typeName: string;
126
187
  get min(): number | undefined;
127
188
  get max(): number | undefined;
128
- get isMin(): boolean;
129
- get isMax(): boolean;
130
- constructor(name: string, initialVal: number, options?: NumberFieldOptions);
189
+ constructor(name: string, initialVal: number, options?: DefaultNumericFieldOptions);
190
+ isMin(): boolean;
191
+ isMax(): boolean;
131
192
  inc(amount?: number): void;
132
193
  dec(amount?: number): void;
133
194
  }
134
195
 
135
196
  /**
136
- * An abstract base class for managing a reactive collection of `Field` instances.
197
+ * A generic registry for mapping string identifiers to class constructors.
198
+ *
199
+ * This utility is fundamental for building extensible systems like dependency injection containers,
200
+ * factories, and serialization engines where types need to be dynamically resolved.
137
201
  *
138
- * This class is designed to be the foundation for state management systems,
139
- * such as managing stats, flags, or items.
202
+ * @template T - A base type that all registered constructors must produce an instance of.
203
+ */
204
+ declare class ConstructorRegistry<T> {
205
+ private readonly items;
206
+ /**
207
+ * Registers a constructor with a unique string identifier.
208
+ *
209
+ * @param typeId - The unique identifier for the constructor (e.g., a static `typeName` property from a class).
210
+ * @param ctor - The class constructor to register.
211
+ * @returns The registry instance for chainable calls.
212
+ * @throws If a constructor with the same `typeId` is already registered.
213
+ */
214
+ register(typeId: string, ctor: Constructor<T>): this;
215
+ /**
216
+ * Retrieves a constructor by its identifier.
217
+ *
218
+ * @param typeId - The identifier of the constructor to retrieve.
219
+ * @returns The found class constructor.
220
+ * @throws If no constructor is found for the given `typeId`.
221
+ */
222
+ get(typeId: string): Constructor<T>;
223
+ /**
224
+ * Checks if a constructor for a given identifier is registered.
225
+ * @param typeId - The identifier to check.
226
+ * @returns `true` if a constructor is registered, otherwise `false`.
227
+ */
228
+ has(typeId: string): boolean;
229
+ /**
230
+ * Clears all registered constructors from the registry.
231
+ */
232
+ clear(): void;
233
+ }
234
+
235
+ declare class FieldRegistry extends ConstructorRegistry<Field<any>> {
236
+ }
237
+
238
+ /**
239
+ * A container for a collection of named `Field` instances.
140
240
  *
141
- * @template T The common base type for the values held by the fields in this collection.
241
+ * This class acts as a "leaf" node in the `FieldTree` hierarchy, managing a flat
242
+ * key-value store of reactive data points. It uses a `FieldRegistry` to dynamically
243
+ * create `Field` instances of different types.
142
244
  */
143
- declare abstract class BaseFields<T> {
144
- protected readonly _fields: Signal<Map<string, Field<T>>>;
145
- readonly events: AxiEventEmitter<"created" | "removed">;
245
+ declare class Fields {
246
+ static readonly typeName = "fields";
247
+ readonly typeName = "fields";
248
+ readonly _fields: Map<string, Field<any>>;
249
+ readonly _fieldRegistry: FieldRegistry;
250
+ /**
251
+ * An event emitter that fires when a new field is added to the collection.
252
+ * @event
253
+ * @param {object} event - The event payload.
254
+ * @param {string} event.name - The name of the added field.
255
+ * @param {Field<any>} event.field - The `Field` instance that was added.
256
+ */
257
+ onAdd: Emitter<[event: {
258
+ name: string;
259
+ field: Field<any>;
260
+ }]>;
146
261
  /**
147
- * A readonly signal providing access to the current map of fields.
148
- * Use this signal with `effect` to react when fields are added or removed from the collection.
149
- * Avoid to change any data in the map manually.
262
+ * An event emitter that fires after one or more fields have been removed.
263
+ * @event
264
+ * @param {object} event - The event payload.
265
+ * @param {string[]} event.names - An array of names of the fields that were successfully removed.
150
266
  */
151
- get fields(): ReadonlySignal<ReadonlyMap<string, Field<any>>>;
267
+ onRemove: Emitter<[event: {
268
+ names: string[];
269
+ }]>;
270
+ /**
271
+ * Gets the read-only map of all `Field` instances in this container.
272
+ * @returns {Map<string, Field<any>>} The collection of fields.
273
+ */
274
+ get fields(): Map<string, Field<any>>;
275
+ /**
276
+ * Creates an instance of Fields.
277
+ * @param {FieldRegistry} fieldRegistry - The registry used to create new `Field` instances.
278
+ */
279
+ constructor(fieldRegistry: FieldRegistry);
152
280
  /**
153
281
  * Checks if a field with the given name exists in the collection.
154
- * @param name The name of the field to check.
155
- * @returns `true` if the field exists, otherwise `false`.
282
+ * @param {string} name The name of the field to check.
283
+ * @returns {boolean} `true` if the field exists, otherwise `false`.
156
284
  */
157
285
  has(name: string): boolean;
158
286
  /**
159
- * Creates and adds a new `Field` to the collection.
160
- * @param name The unique name for the new field.
161
- * @param initialValue The initial value for the new field.
162
- * @returns The newly created `Field` instance.
287
+ * Adds a pre-existing `Field` instance to the collection and fires the `onAdd` event.
288
+ * @template T - The specific `Field` type being added.
289
+ * @param {Field<any>} field - The `Field` instance to add.
290
+ * @returns {T} The added `Field` instance, cast to type `T`.
291
+ * @throws If a field with the same name already exists.
163
292
  */
164
- create(name: string, initialValue: T): Field<T>;
293
+ add<T extends Field<any>>(field: Field<any>): T;
165
294
  /**
166
- * Adds a pre-existing `Field` instance to the collection.
167
- * Throws an error if a field with the same name already exists.
168
- * @param field The `Field` instance to add.
169
- * @returns The added `Field` instance.
295
+ * Creates a new `Field` instance of a specified type, adds it to the collection, and returns it.
296
+ * This is the primary factory method for creating fields within this container.
297
+ * @template T - The expected `Field` type to be returned.
298
+ * @param {string} typeName - The registered type name of the field to create (e.g., 'numeric', 'boolean').
299
+ * @param {string} name - The unique name for the new field.
300
+ * @param {*} initialValue - The initial value for the new field.
301
+ * @param {*} [options] - Optional configuration passed to the field's constructor.
302
+ * @returns {T} The newly created `Field` instance.
170
303
  */
171
- add(field: Field<T>): Field<T>;
304
+ create<T extends Field<any>>(typeName: string, name: string, initialValue: any, options?: any): T;
172
305
  /**
173
- * Retrieves a field by its name.
174
- * Throws an error if the field does not exist.
175
- * @param name The name of the field to retrieve.
176
- * @returns The `Field` instance.
306
+ * Updates an existing field's value or creates a new one if it doesn't exist.
307
+ * @template T - The expected `Field` type.
308
+ * @param {string} typeName - The type name to use if a new field needs to be created.
309
+ * @param {string} name - The name of the field to update or create.
310
+ * @param {*} value - The new value to set.
311
+ * @param {*} [options] - Optional configuration, used only if a new field is created.
312
+ * @returns {T} The existing or newly created `Field` instance.
177
313
  */
178
- get(name: string): Field<T>;
314
+ upset<T extends Field<any>>(typeName: string, name: string, value: any, options?: any): T;
179
315
  /**
180
- * "Update or Insert": Updates a field's value if it exists, or creates a new one if it doesn't.
181
- * @param name The name of the field.
182
- * @param value The value to set.
183
- * @returns The existing or newly created `Field` instance.
316
+ * Retrieves a field by its name.
317
+ * @template T - The expected `Field` type to be returned.
318
+ * @param {string} name - The name of the field to retrieve.
319
+ * @returns {T} The `Field` instance.
320
+ * @throws If the field does not exist.
184
321
  */
185
- upset(name: string, value: T): Field<T>;
322
+ get<T extends Field<any>>(name: string): T;
186
323
  /**
187
324
  * Removes one or more fields from the collection.
188
325
  * This method ensures that the `destroy` method of each removed field is called to clean up its resources.
189
- * @param names A single name or an array of names to remove.
326
+ * @param {string| string[]} names A single name or an array of names to remove.
190
327
  */
191
328
  remove(names: string | string[]): void;
192
329
  /**
193
330
  * Removes all fields from the collection, ensuring each is properly destroyed.
194
331
  */
195
332
  clear(): void;
196
- /**
197
- * Creates a serializable snapshot of the current state of all fields.
198
- * @returns A plain JavaScript object representing the values of all fields.
199
- */
200
- snapshot(): Record<string, any>;
201
- /**
202
- * Restores the state of the fields from a snapshot.
203
- * It uses the `upset` logic to create or update fields based on the snapshot data.
204
- * @param snapshot The snapshot object to load.
205
- */
206
- hydrate(snapshot: any): void;
333
+ destroy(): void;
207
334
  }
208
335
 
209
- declare class Fields extends BaseFields<any> {
210
- createNumber(name: string, initialValue: number, options?: NumberFieldOptions): NumberField;
211
- upsetNumber(name: string, value: number, options?: NumberFieldOptions): NumberField;
212
- getNumber(name: string): NumberField;
213
- create<T>(name: string, initialValue: T): Field<T>;
214
- upset<T>(name: string, value: T): Field<T>;
215
- get<T>(name: string): Field<T>;
336
+ declare const DefaultFields_base: {
337
+ new (...args: any[]): {
338
+ createBoolean(name: string, initialValue: boolean, options?: DefaultBooleanFieldOptions): DefaultBooleanField;
339
+ upsetBoolean(name: string, value: boolean, options?: DefaultBooleanFieldOptions): DefaultBooleanField;
340
+ getBoolean(name: string): DefaultBooleanField;
341
+ readonly typeName: "fields";
342
+ readonly _fields: Map<string, Field<any>>;
343
+ readonly _fieldRegistry: FieldRegistry;
344
+ onAdd: _axi_engine_utils.Emitter<[event: {
345
+ name: string;
346
+ field: Field<any>;
347
+ }]>;
348
+ onRemove: _axi_engine_utils.Emitter<[event: {
349
+ names: string[];
350
+ }]>;
351
+ get fields(): Map<string, Field<any>>;
352
+ has(name: string): boolean;
353
+ add<T extends Field<any>>(field: Field<any>): T;
354
+ create<T extends Field<any>>(typeName: string, name: string, initialValue: any, options?: any): T;
355
+ upset<T extends Field<any>>(typeName: string, name: string, value: any, options?: any): T;
356
+ get<T extends Field<any>>(name: string): T;
357
+ remove(names: string | string[]): void;
358
+ clear(): void;
359
+ destroy(): void;
360
+ };
361
+ } & {
362
+ new (...args: any[]): {
363
+ createString(name: string, initialValue: string, options?: DefaultStringFieldOptions): DefaultStringField;
364
+ upsetString(name: string, value: string, options?: DefaultStringFieldOptions): DefaultStringField;
365
+ getString(name: string): DefaultStringField;
366
+ readonly typeName: "fields";
367
+ readonly _fields: Map<string, Field<any>>;
368
+ readonly _fieldRegistry: FieldRegistry;
369
+ onAdd: _axi_engine_utils.Emitter<[event: {
370
+ name: string;
371
+ field: Field<any>;
372
+ }]>;
373
+ onRemove: _axi_engine_utils.Emitter<[event: {
374
+ names: string[];
375
+ }]>;
376
+ get fields(): Map<string, Field<any>>;
377
+ has(name: string): boolean;
378
+ add<T extends Field<any>>(field: Field<any>): T;
379
+ create<T extends Field<any>>(typeName: string, name: string, initialValue: any, options?: any): T;
380
+ upset<T extends Field<any>>(typeName: string, name: string, value: any, options?: any): T;
381
+ get<T extends Field<any>>(name: string): T;
382
+ remove(names: string | string[]): void;
383
+ clear(): void;
384
+ destroy(): void;
385
+ };
386
+ } & {
387
+ new (...args: any[]): {
388
+ createNumeric(name: string, initialValue: number, options?: DefaultNumericFieldOptions): DefaultNumericField;
389
+ upsetNumeric(name: string, value: number, options?: DefaultNumericFieldOptions): DefaultNumericField;
390
+ getNumeric(name: string): DefaultNumericField;
391
+ readonly typeName: "fields";
392
+ readonly _fields: Map<string, Field<any>>;
393
+ readonly _fieldRegistry: FieldRegistry;
394
+ onAdd: _axi_engine_utils.Emitter<[event: {
395
+ name: string;
396
+ field: Field<any>;
397
+ }]>;
398
+ onRemove: _axi_engine_utils.Emitter<[event: {
399
+ names: string[];
400
+ }]>;
401
+ get fields(): Map<string, Field<any>>;
402
+ has(name: string): boolean;
403
+ add<T extends Field<any>>(field: Field<any>): T;
404
+ create<T extends Field<any>>(typeName: string, name: string, initialValue: any, options?: any): T;
405
+ upset<T extends Field<any>>(typeName: string, name: string, value: any, options?: any): T;
406
+ get<T extends Field<any>>(name: string): T;
407
+ remove(names: string | string[]): void;
408
+ clear(): void;
409
+ destroy(): void;
410
+ };
411
+ } & {
412
+ new (...args: any[]): {
413
+ createDefault<T>(name: string, initialValue: T, options?: FieldOptions<T> | undefined): DefaultField<T>;
414
+ upsetDefault<T>(name: string, value: T, options?: FieldOptions<T> | undefined): DefaultField<T>;
415
+ readonly typeName: "fields";
416
+ readonly _fields: Map<string, Field<any>>;
417
+ readonly _fieldRegistry: FieldRegistry;
418
+ onAdd: _axi_engine_utils.Emitter<[event: {
419
+ name: string;
420
+ field: Field<any>;
421
+ }]>;
422
+ onRemove: _axi_engine_utils.Emitter<[event: {
423
+ names: string[];
424
+ }]>;
425
+ get fields(): Map<string, Field<any>>;
426
+ has(name: string): boolean;
427
+ add<T extends Field<any>>(field: Field<any>): T;
428
+ create<T extends Field<any>>(typeName: string, name: string, initialValue: any, options?: any): T;
429
+ upset<T extends Field<any>>(typeName: string, name: string, value: any, options?: any): T;
430
+ get<T extends Field<any>>(name: string): T;
431
+ remove(names: string | string[]): void;
432
+ clear(): void;
433
+ destroy(): void;
434
+ };
435
+ } & typeof Fields;
436
+ declare class DefaultFields extends DefaultFields_base {
216
437
  }
217
438
 
218
- declare class TypedFields<T> extends BaseFields<T> {
439
+ interface FieldsFactory<TFields extends Fields> {
440
+ fields(): TFields;
441
+ }
442
+ declare class DefaultFieldsFactory implements FieldsFactory<DefaultFields> {
443
+ private readonly fieldRegistry;
444
+ constructor(fieldRegistry: FieldRegistry);
445
+ fields(): DefaultFields;
446
+ }
447
+ /**
448
+ * Defines the contract for a factory that creates nodes for a FieldTree.
449
+ * This allows for custom implementations of Fields and FieldTree to be used.
450
+ */
451
+ interface TreeNodeFactory<TFields extends Fields> extends FieldsFactory<TFields> {
452
+ fields(): TFields;
453
+ tree(): FieldTree<TFields>;
454
+ }
455
+ /**
456
+ * The default factory implementation that creates standard DefaultFields and FieldTree instances.
457
+ */
458
+ declare class DefaultTreeNodeFactory extends DefaultFieldsFactory implements TreeNodeFactory<DefaultFields> {
459
+ constructor(fieldRegistry: FieldRegistry);
460
+ tree(): FieldTree<DefaultFields>;
219
461
  }
220
462
 
221
463
  /** A type alias for any container that can be a child node in a FieldTree */
222
- type TreeOrFieldsContainer = FieldTree | Fields | TypedFields<any>;
223
- /** Describes the payload for events emitted when a container is created or removed from a FieldTree. */
224
- type FieldTreeContainerEvent = {
225
- type: 'created' | 'removed';
226
- name: string;
227
- path: [];
228
- node: TreeOrFieldsContainer;
229
- };
464
+ type TreeNode<F extends Fields> = FieldTree<F> | F;
230
465
  /**
231
- * Represents the global, persistent state of the entire game.
232
- * This service acts as the single source of truth for long-term data that exists
466
+ * Represents a hierarchical data structure for managing the global state of the system.
467
+ *
468
+ * This class acts as the single source of truth for long-term data that exists
233
469
  * across different scenes and scripts, such as player stats, inventory,
234
- * and overall game progress.
235
- * It is designed to be the foundational data layer,
236
- * independent of any single script's / minigames execution lifecycle.
470
+ * and overall game progress. It uses a path-based system for accessing and
471
+ * manipulating nested data, similar to a file system.
237
472
  *
238
- * @todo:
239
- * - add node removing
240
473
  */
241
- declare class FieldTree {
242
- private readonly _items;
243
- readonly events: AxiEventEmitter<"created" | "removed">;
244
- /**
245
- * A readonly signal providing access to the map of child nodes.
246
- * Use this with `effect` to react to structural changes in the tree (e.g., adding a new `Fields` container).
474
+ declare class FieldTree<TFields extends Fields> {
475
+ static readonly typeName = "fieldTree";
476
+ readonly typeName = "fieldTree";
477
+ /** @private The internal map storing child nodes (branches or leaves). */
478
+ private readonly _nodes;
479
+ /** @private The factory used to create new child nodes. */
480
+ private readonly _factory;
481
+ /**
482
+ * An event emitter that fires immediately after a new node is added to this tree branch.
483
+ * @event
484
+ * @param {object} event - The event payload.
485
+ * @param {string} event.name - The name (key) of the added node.
486
+ * @param event.node - The node instance that was added.
487
+ * @example
488
+ * myTree.onAdd.subscribe(({ name, node }) => {
489
+ * console.log(`Node '${name}' was added.`, node);
490
+ * });
491
+ */
492
+ onAdd: Emitter<[event: {
493
+ name: string;
494
+ node: TreeNode<TFields>;
495
+ }]>;
496
+ /**
497
+ * An event emitter that fires once after one or more nodes have been successfully removed.
498
+ * @event
499
+ * @param {object} event - The event payload.
500
+ * @param {string[]} event.names - An array of names of the nodes that were removed.
501
+ * @example
502
+ * myTree.onRemove.subscribe(({ names }) => {
503
+ * console.log(`Nodes removed: ${names.join(', ')}`);
504
+ * });
505
+ */
506
+ onRemove: Emitter<[event: {
507
+ names: string[];
508
+ }]>;
509
+ /**
510
+ * Gets the collection of direct child nodes of this tree branch.
511
+ */
512
+ get nodes(): Map<string, TreeNode<TFields>>;
513
+ /**
514
+ * Creates an instance of FieldTree.
515
+ * @param {TreeNodeFactory} factory - A factory responsible for creating new nodes within the tree.
516
+ */
517
+ constructor(factory: TreeNodeFactory<TFields>);
518
+ /**
519
+ * Checks if a direct child node with the given name exists.
520
+ * @param {string} name - The name of the direct child node.
521
+ * @returns {boolean} `true` if the node exists, otherwise `false`.
247
522
  */
248
- get items(): ReadonlySignal<ReadonlyMap<string, TreeOrFieldsContainer>>;
249
- constructor();
523
+ has(name: string): boolean;
250
524
  /**
251
- * Checks if a path to a node or fields container or field exists without creating it.
252
- * @returns true if the entire path exists, false otherwise.
525
+ * Checks if a node exists at a given path, traversing the tree.
526
+ * @param {PathType} path - The path to check (e.g., 'player/stats' or ['player', 'stats']).
527
+ * @returns {boolean} `true` if the entire path resolves to a node, otherwise `false`.
253
528
  */
254
529
  hasPath(path: PathType): boolean;
255
530
  /**
256
- * Retrieves a child node and asserts that it is an instance of `FieldTree`.
257
- * @param name The name of the child node.
258
- * @returns The `FieldTree` instance.
259
- * @throws If the node does not exist or is not a `FieldTree`.
531
+ * Adds a pre-existing node as a direct child of this tree branch.
532
+ * @param {string} name - The name to assign to the new child node.
533
+ * @param {TreeNode} node - The node instance to add.
534
+ * @returns {TreeNode} The added node.
535
+ * @throws If a node with the same name already exists.
260
536
  */
261
- getFieldTree(name: string): FieldTree;
537
+ addNode(name: string, node: TreeNode<TFields>): TreeNode<TFields>;
262
538
  /**
263
- * Retrieves a child node and asserts that it is an instance of `Fields`.
264
- * @param name The name of the child node.
265
- * @returns The `Fields` instance.
266
- * @throws If the node does not exist or is not a `Fields` container.
539
+ * Retrieves a direct child node by its name.
540
+ * @param {string} name - The name of the child node.
541
+ * @returns {TreeNode} The retrieved node.
542
+ * @throws If a node with the given name cannot be found.
267
543
  */
268
- getFields(name: string): Fields;
544
+ getNode(name: string): TreeNode<TFields>;
269
545
  /**
270
- * Retrieves a child node and asserts that it is an instance of `TypedFields`.
271
- * @param name The name of the child node.
272
- * @returns The `TypedFields` instance.
273
- * @throws If the node does not exist or is not a `TypedFields` container.
546
+ * Removes one or more nodes from this tree branch.
547
+ *
548
+ * This method first validates that all specified nodes exist. If validation passes,
549
+ * it recursively calls `destroy()` on each node to ensure proper cleanup of the entire subtree.
550
+ * Finally, it emits a single `onRemove` event with the names of all successfully removed nodes.
551
+ *
552
+ * @param {string | string[]} names - A single name or an array of names of the nodes to remove.
553
+ * @throws If any of the specified names do not correspond to an existing node.
274
554
  */
275
- getTypedFields<T>(name: string): TypedFields<T>;
555
+ removeNode(names: string | string[]): void;
276
556
  /**
277
- * Retrieves a child node from this tree level without type checking.
278
- * @param name The name of the child node.
279
- * @returns The retrieved node, which can be a `FieldTree` or a `Fields` container.
280
- * @throws If a node with the given name cannot be found.
557
+ * Creates a new `FieldTree` (branch) node at the specified path.
558
+ * @param {PathType} path - The path where the new `FieldTree` should be created.
559
+ * @param {boolean} [createPath=false] - If `true`, any missing parent branches in the path will be created automatically.
560
+ * @returns {FieldTree} The newly created `FieldTree` instance.
561
+ * @throws If the path is invalid or a node already exists at the target location.
281
562
  */
282
- getNode(name: string): TreeOrFieldsContainer;
563
+ createFieldTree<T extends FieldTree<TFields>>(path: PathType, createPath?: boolean): T;
283
564
  /**
284
- * Creates and adds a new `FieldTree` node as a child of this one.
285
- * @param name The unique name for the new `FieldTree` node.
286
- * @returns The newly created `FieldTree` instance.
565
+ * Creates a new `Fields` (leaf) container at the specified path.
566
+ * @param {PathType} path - The path where the new `Fields` container should be created.
567
+ * @param {boolean} [createPath=false] - If `true`, any missing parent branches in the path will be created automatically.
568
+ * @returns {Fields} The newly created `Fields` instance.
569
+ * @throws If the path is invalid or a node already exists at the target location.
287
570
  */
288
- createFieldTree(name: string): FieldTree;
571
+ createFields(path: PathType, createPath?: boolean): TFields;
289
572
  /**
290
- * Creates and adds a new `Fields` container as a child of this one.
291
- * @param name The unique name for the new `Fields` container.
292
- * @returns The newly created `Fields` instance.
573
+ * Retrieves a `FieldTree` (branch) node from a specified path.
574
+ * @param {PathType} path - The path to the `FieldTree` node.
575
+ * @returns {FieldTree} The `FieldTree` instance at the specified path.
576
+ * @throws If the path is invalid or the node at the path is not a `FieldTree`.
293
577
  */
294
- createFields(name: string): Fields;
578
+ getFieldTree(path: PathType): FieldTree<TFields>;
295
579
  /**
296
- * Creates and adds a new `TypedFields` container as a child of this one.
297
- * @param name The unique name for the new `TypedFields` container.
298
- * @returns The newly created `TypedFields` instance.
580
+ * Retrieves a `Fields` (leaf) container from a specified path.
581
+ * @param {PathType} path - The path to the `Fields` container.
582
+ * @returns {Fields} The `Fields` instance at the specified path.
583
+ * @throws If the path is invalid or the node at the path is not a `Fields` container.
299
584
  */
300
- createTypedFields<T>(name: string): TypedFields<T>;
585
+ getFields(path: PathType): TFields;
301
586
  /**
302
- * Navigates through the tree using a path and returns the `Fields` container at the end.
303
- * @param path The path to the `Fields` container (e.g., 'player/stats').
304
- * @returns The `Fields` container at the specified path.
305
- * @throws If the path is empty, or any intermediate node is not a `FieldTree`.
587
+ * Retrieves a `FieldTree` at the specified path. If it or any part of the path doesn't exist, it will be created.
588
+ * @param {PathType} path - The path to the `FieldTree` node.
589
+ * @returns {FieldTree} The existing or newly created `FieldTree` instance.
306
590
  */
307
- getFieldsByPath(path: PathType): Fields;
591
+ getOrCreateFieldTree(path: PathType): FieldTree<TFields>;
308
592
  /**
309
- * Creates a `Field` at a deeply nested path.
310
- * The last part of the path is treated as the field name, and the preceding parts as the path to its container.
311
- * @param path The full path to the new field (e.g., 'player/stats/health').
312
- * @param initialValue The initial value for the new field.
313
- * @returns The newly created `Field` instance.
593
+ * Retrieves a `Fields` container at the specified path. If it or any part of the path doesn't exist, it will be created.
594
+ * @param {PathType} path - The path to the `Fields` container.
595
+ * @returns {Fields} The existing or newly created `Fields` instance.
314
596
  */
315
- create<T>(path: PathType, initialValue: T): Field<T>;
597
+ getOrCreateFields(path: PathType): Fields;
316
598
  /**
317
- * Creates a `NumberField` at a deeply nested path.
318
- * @param path The full path to the new field (e.g., 'player/stats/mana').
319
- * @param initialValue The initial numeric value.
320
- * @returns The newly created `NumberField` instance.
599
+ * Removes all child nodes from this tree branch.
600
+ * This method ensures that `destroy()` is called on each child node, allowing for
601
+ * a full, recursive cleanup of the entire subtree.
321
602
  */
322
- createNumber(path: PathType, initialValue: number): NumberField;
603
+ clear(): void;
323
604
  /**
324
- * Retrieves a `Field` from a deeply nested path.
325
- * @param path The full path to the field (e.g., 'player/stats/name').
326
- * @returns The `Field` instance at the specified path.
605
+ * Performs a complete cleanup of this node and its entire subtree.
606
+ *
607
+ * It recursively destroys all child nodes by calling `clear()` and then
608
+ * unsubscribes all listeners from its own event emitters.
609
+ * This method should be called when a node is no longer needed.
327
610
  */
328
- get<T>(path: PathType): Field<T>;
611
+ destroy(): void;
329
612
  /**
330
- * Retrieves a `NumberField` from a deeply nested path.
331
- * @param path The full path to the number field (e.g., 'player/stats/level').
332
- * @returns The `NumberField` instance at the specified path.
613
+ * @private
614
+ * Navigates the tree to the parent of a target node.
615
+ * This is the core traversal logic for all path-based operations.
616
+ * @param {PathType} path - The full path to the target node.
617
+ * @param {boolean} [createPath=false] - If `true`, creates missing `FieldTree` branches along the path.
618
+ * @returns {{branch: FieldTree, leafName: string}} An object containing the final branch (parent node) and the name of the leaf (target node).
619
+ * @throws If the path is empty, invalid, or contains a `Fields` container as an intermediate segment.
620
+ */
621
+ private traversePath;
622
+ }
623
+
624
+ /**
625
+ * Defines the contract for a handler that can serialize and deserialize a specific type of Policy.
626
+ * @template T - The specific Policy class this handler manages.
627
+ * @template S - The shape of the plain object this handler produces/consumes.
628
+ */
629
+ interface PolicySerializerHandler<T extends Policy<any>, S extends object> {
630
+ /**
631
+ * Converts a Policy instance into a serializable plain object.
632
+ * @param policy The Policy instance to serialize.
633
+ * @returns A plain object representing the policy's state.
634
+ */
635
+ snapshot(policy: T): S;
636
+ /**
637
+ * Creates a new Policy instance from a plain object.
638
+ * @param snapshotData The plain object containing the policy's state.
639
+ * @returns A new instance of the Policy.
640
+ */
641
+ hydrate(snapshotData: S): T;
642
+ }
643
+ declare class PolicySerializer {
644
+ private readonly handlers;
645
+ register(policyId: string, handler: PolicySerializerHandler<any, any>): this;
646
+ clearHandlers(): void;
647
+ /**
648
+ * Creates a serializable snapshot of a policy instance.
649
+ * The snapshot includes the policy's state and a `__type` identifier.
650
+ * @param policy The policy instance to snapshot.
651
+ * @returns A plain object ready for JSON serialization.
652
+ * @throws If no handler is registered for the policy's ID.
333
653
  */
334
- getNumber(path: PathType): NumberField;
654
+ snapshot(policy: Policy<any>): object;
655
+ /**
656
+ * Restores a policy instance from its snapshot representation.
657
+ * @param snapshot The plain object snapshot, which must contain a `__type` property.
658
+ * @returns A new, fully functional policy instance.
659
+ * @throws If the snapshot is invalid or no handler is registered for its `__type`.
660
+ */
661
+ hydrate(snapshot: any): Policy<any>;
662
+ }
663
+
664
+ declare class ClampPolicySerializerHandler implements PolicySerializerHandler<ClampPolicy, {
665
+ min: number;
666
+ max: number;
667
+ }> {
668
+ snapshot(policy: ClampPolicy): {
669
+ min: number;
670
+ max: number;
671
+ };
672
+ hydrate(data: {
673
+ min: number;
674
+ max: number;
675
+ }): ClampPolicy;
676
+ }
677
+
678
+ declare class ClampMaxPolicySerializerHandler implements PolicySerializerHandler<ClampMaxPolicy, {
679
+ max: number;
680
+ }> {
681
+ snapshot(policy: ClampMaxPolicy): {
682
+ max: number;
683
+ };
684
+ hydrate(data: {
685
+ max: number;
686
+ }): ClampMaxPolicy;
687
+ }
688
+
689
+ declare class ClampMinPolicySerializerHandler implements PolicySerializerHandler<ClampMinPolicy, {
690
+ min: number;
691
+ }> {
692
+ snapshot(policy: ClampMinPolicy): {
693
+ min: number;
694
+ };
695
+ hydrate(data: {
696
+ min: number;
697
+ }): ClampMinPolicy;
698
+ }
699
+
700
+ /**
701
+ * A plain object representation of a Field's state for serialization.
702
+ */
703
+ interface FieldSnapshot {
704
+ __type: string;
705
+ name: string;
706
+ value: any;
707
+ policies?: object[];
708
+ }
709
+ /**
710
+ * Orchestrates the serialization and deserialization of Field instances.
711
+ *
712
+ * This class acts as a central point for converting complex Field objects into
713
+ * plain, storable data (snapshots) and vice-versa. It uses a `FieldRegistry`
714
+ * to resolve class constructors and a `PolicySerializer` to handle the state
715
+ * of any attached policies.
716
+ *
717
+ * @todo Implement a `patch(field, snapshot)` method.
718
+ * Unlike `hydrate`, which creates a new
719
+ * instance, `patch` should update the state of an *existing* field instance
720
+ * without breaking external references to it.
721
+ */
722
+ declare class FieldSerializer {
723
+ private readonly fieldRegistry;
724
+ private readonly policySerializer;
725
+ /**
726
+ * Creates an instance of FieldSerializer.
727
+ * @param {FieldRegistry} fieldRegistry - A registry that maps string type names to Field constructors.
728
+ * @param {PolicySerializer} policySerializer - A serializer dedicated to handling Policy instances.
729
+ */
730
+ constructor(fieldRegistry: FieldRegistry, policySerializer: PolicySerializer);
731
+ /**
732
+ * Creates a serializable snapshot of a Field instance.
733
+ * The snapshot includes the field's type, name, current value, and the state of all its policies.
734
+ * @param {Field<any>} field - The Field instance to serialize.
735
+ * @returns {FieldSnapshot} A plain object ready for JSON serialization.
736
+ */
737
+ snapshot(field: Field<any>): FieldSnapshot;
738
+ /**
739
+ * Restores a Field instance from its snapshot representation.
740
+ * It uses the `__type` property to find the correct constructor and hydrates
741
+ * the field with its value and all its policies.
742
+ * @param {FieldSnapshot} snapshot - The plain object snapshot to deserialize.
743
+ * @returns {Field<any>} A new, fully functional Field instance.
744
+ * @throws If the snapshot is invalid, missing a `__type`, or if the type is not registered.
745
+ */
746
+ hydrate(snapshot: FieldSnapshot): Field<any>;
747
+ }
748
+
749
+ /**
750
+ * A plain object representation of a Fields container's state for serialization.
751
+ */
752
+ interface FieldsSnapshot {
753
+ __type: string;
754
+ [fieldName: string]: FieldSnapshot | string;
755
+ }
756
+ /**
757
+ * Orchestrates the serialization and deserialization of `Fields` container instances.
758
+ *
759
+ * This class acts as a high-level composer, responsible for converting an entire `Fields` object
760
+ * into a storable snapshot and back.
761
+ * It delegates the actual serialization of each `Field` and `Policy` to their respective serializers.
762
+ *
763
+ * @todo Implement a `patch(fields, snapshot)` method. It should perform a non-destructive
764
+ * update, creating new fields, removing missing ones, and patching existing ones
765
+ * in place, preserving the container instance itself.
766
+ */
767
+ declare class FieldsSerializer<TFields extends Fields> {
768
+ private readonly fieldsFactory;
769
+ private readonly fieldSerializer;
770
+ /**
771
+ * Creates an instance of FieldsSerializer.
772
+ * @param {FieldsFactory} fieldsFactory - A registry that maps string type names to Field constructors.
773
+ * @param {FieldSerializer} fieldSerializer - A serializer of field instances.
774
+ */
775
+ constructor(fieldsFactory: FieldsFactory<TFields>, fieldSerializer: FieldSerializer);
776
+ /**
777
+ * Creates a serializable snapshot of a `Fields` container.
778
+ *
779
+ * The snapshot includes a `__type` identifier (currently hardcoded) and an array of snapshots
780
+ * for each `Field` within the container.
781
+ * @param {Fields} fields - The `Fields` instance to serialize.
782
+ * @returns {FieldsSnapshot} A plain object ready for JSON serialization.
783
+ */
784
+ snapshot(fields: Fields): FieldsSnapshot;
785
+ /**
786
+ * Restores a `Fields` container instance from its snapshot representation.
787
+ *
788
+ * It iterates through the field snapshots and hydrates them individually, adding them to the new container.
789
+ * @param {FieldsSnapshot} snapshot - The plain object snapshot to deserialize.
790
+ * @returns {Fields} A new `DefaultFields` instance populated with the restored fields.
791
+ */
792
+ hydrate(snapshot: FieldsSnapshot): TFields;
793
+ }
794
+
795
+ /**
796
+ * Represents the serializable state of a `FieldTree` container.
797
+ *
798
+ * This type describes a plain object that has:
799
+ * 1. A required `__type` property to identify the tree's class.
800
+ * 2. An arbitrary number of other properties, where each key is the `name`
801
+ * of a child node, and the value is the snapshot of that child node.
802
+ * The `| string` is included to ensure compatibility with the `__type` property.
803
+ */
804
+ interface FieldTreeSnapshot {
805
+ __type: string;
806
+ [fieldName: string]: FieldsSnapshot | FieldTreeSnapshot | string;
807
+ }
808
+ /**
809
+ * Orchestrates the recursive serialization and deserialization of `FieldTree` instances.
810
+ *
811
+ * This class handles the conversion of an entire `FieldTree` object graph into a
812
+ * plain, storable snapshot and vice-versa. It delegates the processing of `Fields`
813
+ * leaf nodes to a dedicated `FieldsSerializer`.
814
+ * @todo Refactoring: The current implementation uses `if/else` logic in `snapshot` and `hydrate`
815
+ * to process different node types. A more extensible approach would be to use a
816
+ * registry of dedicated handlers for each node type.
817
+ * This would allow new node types to be supported without
818
+ * modifying this class, adhering to the Open/Closed Principle.
819
+ *
820
+ * @todo Implement a `patch(tree, snapshot)` method for recursive, non-destructive
821
+ * updates. This method should traverse the existing tree and the snapshot,
822
+ * patching nodes in place to maintain object references.
823
+ */
824
+ declare class FieldTreeSerializer<TFields extends Fields> {
825
+ private readonly fieldTreeNodeFactory;
826
+ private readonly fieldsSerializer;
827
+ constructor(fieldTreeNodeFactory: TreeNodeFactory<TFields>, fieldsSerializer: FieldsSerializer<TFields>);
335
828
  /**
336
829
  * Creates a serializable snapshot of the entire tree and its contained fields.
337
830
  * @returns A plain JavaScript object representing the complete state managed by this tree.
338
831
  */
339
- snapshot(): Record<string, any>;
832
+ snapshot(tree: FieldTree<TFields>): FieldTreeSnapshot;
340
833
  /**
341
834
  * Restores the state of the tree from a snapshot.
342
835
  * It intelligently creates missing nodes based on `__type` metadata and delegates hydration to child nodes.
343
836
  * @param snapshot The snapshot object to load.
344
837
  */
345
- hydrate(snapshot: any): void;
346
- /**
347
- * @private
348
- * Generic internal method for creating and adding a new node to the tree.
349
- * @param name The name of the node to create.
350
- * @param ctor The constructor for the node type (e.g., `FieldTree` or `Fields`).
351
- * @returns The newly created node instance.
352
- */
353
- private createNode;
838
+ hydrate(snapshot: FieldTreeSnapshot): FieldTree<TFields>;
354
839
  }
355
840
 
356
- export { BaseFields, ClampMaxPolicy, ClampMinPolicy, ClampPolicy, Field, type FieldPolicy, FieldTree, type FieldTreeContainerEvent, Fields, FieldsNodeType, NumberField, type NumberFieldOptions, type TreeOrFieldsContainer, TypedFields, clampMaxPolicy, clampMinPolicy, clampPolicy };
841
+ export { type BooleanField, ClampMaxPolicy, ClampMaxPolicySerializerHandler, ClampMinPolicy, ClampMinPolicySerializerHandler, ClampPolicy, ClampPolicySerializerHandler, DefaultBooleanField, type DefaultBooleanFieldOptions, DefaultField, DefaultFields, DefaultFieldsFactory, DefaultNumericField, type DefaultNumericFieldOptions, DefaultStringField, type DefaultStringFieldOptions, DefaultTreeNodeFactory, type Field, type FieldOptions, FieldRegistry, FieldSerializer, type FieldSnapshot, FieldTree, FieldTreeSerializer, type FieldTreeSnapshot, Fields, type FieldsFactory, FieldsSerializer, type FieldsSnapshot, type NumericField, Policies, type Policy, PolicySerializer, type PolicySerializerHandler, type StringField, type TreeNode, type TreeNodeFactory, clampMaxPolicy, clampMinPolicy, clampPolicy };