@axi-engine/fields 0.3.8 → 0.3.10

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.mjs CHANGED
@@ -318,6 +318,9 @@ var Fields = class _Fields {
318
318
  onRemove = new Emitter2();
319
319
  /**
320
320
  * Gets the read-only map of all `Field` instances in this container.
321
+ *
322
+ * @internal
323
+ *
321
324
  * @returns {Map<string, Field<any>>} The collection of fields.
322
325
  */
323
326
  get fields() {
@@ -464,11 +467,28 @@ var FieldTree = class _FieldTree {
464
467
  */
465
468
  onRemove = new Emitter3();
466
469
  /**
467
- * Gets the collection of direct child nodes of this tree branch.
470
+ * Provides direct access to the internal node storage.
471
+ *
472
+ * @remarks
473
+ * This is primarily intended for **serialization**, debugging, or low-level iteration.
474
+ * Avoid modifying this map directly to maintain internal consistency; use {@link addNode} or {@link removeNode} instead.
475
+ * @internal
468
476
  */
469
477
  get nodes() {
470
478
  return this._nodes;
471
479
  }
480
+ /**
481
+ * Exposes the internal factory instance used by this tree.
482
+ *
483
+ * @remarks
484
+ * Direct usage of this getter is generally unnecessary.
485
+ * Prefer using {@link createDetachedTree} or {@link createDetachedFields} to create isolated instances.
486
+ *
487
+ * @returns {FieldTreeFactory} The factory instance.
488
+ */
489
+ get factory() {
490
+ return this._factory;
491
+ }
472
492
  /**
473
493
  * Creates an instance of FieldTree.
474
494
  * @param {FieldTreeFactory} factory - A factory responsible for creating new nodes within the tree.
@@ -640,6 +660,23 @@ var FieldTree = class _FieldTree {
640
660
  this.onAdd.clear();
641
661
  this.onRemove.clear();
642
662
  }
663
+ /**
664
+ * Creates a new, detached FieldTree instance using the same factory as this tree.
665
+ * This new tree has no parent and is completely isolated.
666
+ *
667
+ * @returns A new instance of the same tree type.
668
+ */
669
+ createDetachedTree() {
670
+ return this._factory.tree();
671
+ }
672
+ /**
673
+ * Creates a new, detached Fields container using the same factory.
674
+ *
675
+ * @returns
676
+ */
677
+ createDetachedFields() {
678
+ return this._factory.fields();
679
+ }
643
680
  /**
644
681
  * @private
645
682
  * Navigates the tree to the parent of a target node.
@@ -837,13 +874,35 @@ var FieldSerializer = class {
837
874
  const fieldType = snapshot.__type;
838
875
  throwIfEmpty3(fieldType, 'Invalid field snapshot: missing "__type" identifier.');
839
876
  const Ctor = this.fieldRegistry.getOrThrow(fieldType);
840
- let policies;
841
- if (!isNullOrUndefined2(snapshot.policies)) {
842
- policies = [];
843
- snapshot.policies.forEach((p) => policies.push(this.policySerializer.hydrate(p)));
844
- }
877
+ let policies = this.hydratePolicies(snapshot);
845
878
  return new Ctor(snapshot.name, snapshot.value, { policies });
846
879
  }
880
+ /**
881
+ * Updates an existing Field instance with data from a snapshot.
882
+ *
883
+ * This method modifies the field in-place, preserving the object reference.
884
+ * It updates the field's value and completely replaces its current policies
885
+ * with the ones defined in the snapshot.
886
+ *
887
+ * @param {Field<any>} field - The existing Field instance to update.
888
+ * @param {FieldSnapshot} snapshot - The snapshot containing the new state.
889
+ */
890
+ patch(field, snapshot) {
891
+ let policies = this.hydratePolicies(snapshot);
892
+ field.policies.clear();
893
+ if (policies) {
894
+ policies.forEach((p) => field.policies.add(p));
895
+ }
896
+ field.value = snapshot.value;
897
+ }
898
+ hydratePolicies(snapshot) {
899
+ if (isNullOrUndefined2(snapshot.policies)) {
900
+ return void 0;
901
+ }
902
+ const policies = [];
903
+ snapshot.policies.forEach((p) => policies.push(this.policySerializer.hydrate(p)));
904
+ return policies;
905
+ }
847
906
  };
848
907
 
849
908
  // src/serializer/fields-serializer.ts
@@ -894,9 +953,17 @@ var FieldsSerializer = class {
894
953
  // src/serializer/field-tree-serializer.ts
895
954
  import { isString } from "@axi-engine/utils";
896
955
  var FieldTreeSerializer = class {
956
+ _factory;
957
+ _fieldsSerializer;
958
+ get factory() {
959
+ return this._factory;
960
+ }
961
+ get fieldsSerializer() {
962
+ return this._fieldsSerializer;
963
+ }
897
964
  constructor(fieldTreeNodeFactory, fieldsSerializer) {
898
- this.fieldTreeNodeFactory = fieldTreeNodeFactory;
899
- this.fieldsSerializer = fieldsSerializer;
965
+ this._factory = fieldTreeNodeFactory;
966
+ this._fieldsSerializer = fieldsSerializer;
900
967
  }
901
968
  /**
902
969
  * Creates a serializable snapshot of the entire tree and its contained fields.
@@ -922,7 +989,7 @@ var FieldTreeSerializer = class {
922
989
  */
923
990
  hydrate(snapshot) {
924
991
  const { __type, ...nodes } = snapshot;
925
- const tree = this.fieldTreeNodeFactory.tree();
992
+ const tree = this._factory.tree();
926
993
  for (const key in nodes) {
927
994
  const nodeData = nodes[key];
928
995
  if (isString(nodeData)) {
@@ -962,23 +1029,52 @@ var StringFieldResolver = class {
962
1029
  }
963
1030
  };
964
1031
 
1032
+ // src/guards.ts
1033
+ import { isNullOrUndefined as isNullOrUndefined3 } from "@axi-engine/utils";
1034
+ function isFields(value) {
1035
+ return !isNullOrUndefined3(value) && value.typeName === Fields.typeName;
1036
+ }
1037
+ function isFieldTree(value) {
1038
+ return !isNullOrUndefined3(value) && value.typeName === FieldTree.typeName;
1039
+ }
1040
+ function isDataStore(value) {
1041
+ return !isNullOrUndefined3(value) && value.typeName === DataStore.typeName;
1042
+ }
1043
+
965
1044
  // src/data-store.ts
966
- var DataStore = class {
967
- constructor(tree) {
968
- this.tree = tree;
1045
+ var DataStore = class _DataStore {
1046
+ static typeName = "dataStore";
1047
+ typeName = _DataStore.typeName;
1048
+ resolvers = [];
1049
+ _variables;
1050
+ _tree;
1051
+ _factory;
1052
+ get variables() {
1053
+ if (!this._variables) {
1054
+ this._variables = this._factory.fields();
1055
+ }
1056
+ return this._variables;
1057
+ }
1058
+ get tree() {
1059
+ if (!this._tree) {
1060
+ this._tree = this._factory.tree();
1061
+ }
1062
+ return this._tree;
1063
+ }
1064
+ constructor(treeOrFactory, variables) {
1065
+ if (!isFieldTree(treeOrFactory)) {
1066
+ this._factory = treeOrFactory;
1067
+ } else {
1068
+ this._tree = treeOrFactory;
1069
+ this._factory = this._tree.factory;
1070
+ }
1071
+ if (variables) {
1072
+ this._variables = variables;
1073
+ }
969
1074
  this.registerResolver(new NumericFieldResolver());
970
1075
  this.registerResolver(new BooleanFieldResolver());
971
1076
  this.registerResolver(new StringFieldResolver());
972
1077
  }
973
- resolvers = [];
974
- rootFieldsName = "__root_fields";
975
- _rootFields;
976
- get rootFields() {
977
- if (!this._rootFields) {
978
- this._rootFields = this.tree.getOrCreateFields(this.rootFieldsName);
979
- }
980
- return this._rootFields;
981
- }
982
1078
  registerResolver(resolver) {
983
1079
  this.resolvers.unshift(resolver);
984
1080
  }
@@ -1048,8 +1144,8 @@ var DataStore = class {
1048
1144
  getField(path) {
1049
1145
  const pathArr = ensurePathArray2(path);
1050
1146
  throwIfEmpty4(pathArr, `Wrong path or path is empty: ${ensurePathString2(path)}, should contain at least one path segment`);
1051
- if (this.isPathToRootFields(pathArr)) {
1052
- return this.rootFields.get(pathArr[0]);
1147
+ if (this.isPathToVariables(pathArr)) {
1148
+ return this.variables.get(pathArr[0]);
1053
1149
  }
1054
1150
  const fieldName = pathArr.pop();
1055
1151
  const fields = this.tree.getFields(pathArr);
@@ -1070,8 +1166,8 @@ var DataStore = class {
1070
1166
  remove(path) {
1071
1167
  const pathArr = ensurePathArray2(path);
1072
1168
  throwIfEmpty4(pathArr, `Wrong path or path is empty: ${ensurePathString2(path)}, should contain at least one path segment`);
1073
- if (this.isPathToRootFields(pathArr)) {
1074
- this.rootFields.remove(pathArr);
1169
+ if (this.isPathToVariables(pathArr)) {
1170
+ this.variables.remove(pathArr);
1075
1171
  return;
1076
1172
  }
1077
1173
  const node = this.tree.findParentNode(pathArr);
@@ -1082,25 +1178,26 @@ var DataStore = class {
1082
1178
  node.removeNode(leafName);
1083
1179
  }
1084
1180
  }
1085
- isPathToRootFields(path) {
1086
- return ensurePathArray2(path).length === 1;
1087
- }
1088
- getDestinationFields(path) {
1089
- const pathArr = ensurePathArray2(path);
1090
- if (this.isPathToRootFields(pathArr)) {
1091
- return { fields: this.rootFields, leafName: pathArr[0] };
1092
- }
1093
- const leafName = pathArr.pop();
1094
- return { fields: this.tree.getOrCreateFields(path), leafName };
1181
+ /**
1182
+ * Creates a new, independent instance of the Store with a fresh, empty data state (FieldsTree).
1183
+ *
1184
+ * The new instance retains the same capabilities (e.g., factory configuration)
1185
+ * as the current one but is completely detached from the existing data hierarchy.
1186
+ * This is useful for creating local scopes, stack frames, or temporary data contexts.
1187
+ *
1188
+ * @returns {DataStore} A new, isolated DataStore instance.
1189
+ */
1190
+ createIsolated() {
1191
+ return new _DataStore(this._factory);
1095
1192
  }
1193
+ /** code below -> implementation of the DataStore from utils */
1096
1194
  has(path) {
1097
1195
  const pathArr = ensurePathArray2(path);
1098
- if (this.isPathToRootFields(pathArr)) {
1099
- return this.rootFields.has(pathArr[0]);
1196
+ if (this.isPathToVariables(pathArr)) {
1197
+ return this.variables.has(pathArr[0]);
1100
1198
  }
1101
1199
  return this.tree.hasPath(pathArr);
1102
1200
  }
1103
- /** implementation of the DataStore from utils */
1104
1201
  get(path) {
1105
1202
  return this.getField(path).value;
1106
1203
  }
@@ -1116,19 +1213,86 @@ var DataStore = class {
1116
1213
  delete(path) {
1117
1214
  this.remove(path);
1118
1215
  }
1216
+ /**
1217
+ * @internal Used for serialization
1218
+ */
1219
+ getInternalVariables() {
1220
+ return this._variables;
1221
+ }
1222
+ /**
1223
+ * @internal Used for serialization
1224
+ */
1225
+ getInternalTree() {
1226
+ return this._tree;
1227
+ }
1228
+ /**
1229
+ * @private
1230
+ */
1231
+ isPathToVariables(path) {
1232
+ return ensurePathArray2(path).length === 1;
1233
+ }
1234
+ /**
1235
+ * @private
1236
+ */
1237
+ getDestinationFields(path) {
1238
+ const pathArr = ensurePathArray2(path);
1239
+ if (this.isPathToVariables(pathArr)) {
1240
+ return { fields: this.variables, leafName: pathArr[0] };
1241
+ }
1242
+ const leafName = pathArr.pop();
1243
+ return { fields: this.tree.getOrCreateFields(path), leafName };
1244
+ }
1119
1245
  };
1120
1246
 
1121
- // src/guards.ts
1122
- import { isNullOrUndefined as isNullOrUndefined3 } from "@axi-engine/utils";
1123
- function isFields(value) {
1124
- return value != null && value.typeName === Fields.typeName;
1125
- }
1126
- function isFieldTree(value) {
1127
- return value != null && value.typeName === FieldTree.typeName;
1128
- }
1129
- function isStore(value) {
1130
- return !isNullOrUndefined3(value) && typeof value.createFields === "function" && typeof value.createTree === "function";
1131
- }
1247
+ // src/serializer/data-store-serializer.ts
1248
+ import { isNullOrUndefined as isNullOrUndefined4 } from "@axi-engine/utils";
1249
+ var DataStoreSerializer = class {
1250
+ /**
1251
+ * Creates an instance of DataStoreSerializer.
1252
+ * @param {FieldTreeSerializer} fieldTreeSerializer - The serializer used for the underlying tree and fields.
1253
+ */
1254
+ constructor(fieldTreeSerializer) {
1255
+ this.fieldTreeSerializer = fieldTreeSerializer;
1256
+ }
1257
+ /**
1258
+ * Captures the current state of a DataStore into a serializable snapshot.
1259
+ *
1260
+ * It checks for the existence of internal variables and the internal tree,
1261
+ * serializing them only if they have been initialized (lazy serialization).
1262
+ *
1263
+ * @param {DataStore} store - The store instance to serialize.
1264
+ * @returns {DataStoreSnapshot} The snapshot object.
1265
+ */
1266
+ snapshot(store) {
1267
+ let snapshot = {
1268
+ __type: store.typeName
1269
+ };
1270
+ const variables = store.getInternalVariables();
1271
+ if (variables) {
1272
+ snapshot.variables = this.fieldTreeSerializer.fieldsSerializer.snapshot(variables);
1273
+ }
1274
+ const tree = store.getInternalTree();
1275
+ if (tree) {
1276
+ snapshot.tree = this.fieldTreeSerializer.snapshot(tree);
1277
+ }
1278
+ return snapshot;
1279
+ }
1280
+ /**
1281
+ * Reconstructs a DataStore instance from a snapshot.
1282
+ *
1283
+ * If the snapshot contains a tree, the store is initialized with it.
1284
+ * If not, the store is initialized with the factory (lazy mode), and the
1285
+ * detached variables are injected if present.
1286
+ *
1287
+ * @param {DataStoreSnapshot} snapshot - The snapshot to hydrate.
1288
+ * @returns {DataStore} A new, fully restored DataStore instance.
1289
+ */
1290
+ hydrate(snapshot) {
1291
+ const tree = isNullOrUndefined4(snapshot.tree) ? void 0 : this.fieldTreeSerializer.hydrate(snapshot.tree);
1292
+ const variables = isNullOrUndefined4(snapshot.variables) ? void 0 : this.fieldTreeSerializer.fieldsSerializer.hydrate(snapshot.variables);
1293
+ return new DataStore(tree ? tree : this.fieldTreeSerializer.factory, variables);
1294
+ }
1295
+ };
1132
1296
 
1133
1297
  // src/setup.ts
1134
1298
  function createCoreFieldRegistry() {
@@ -1180,6 +1344,7 @@ export {
1180
1344
  CoreStringField,
1181
1345
  CoreTreeNodeFactory,
1182
1346
  DataStore,
1347
+ DataStoreSerializer,
1183
1348
  FieldRegistry,
1184
1349
  FieldSerializer,
1185
1350
  FieldTree,
@@ -1197,7 +1362,7 @@ export {
1197
1362
  createCoreTreeNodeFactory,
1198
1363
  createCoreTreeSerializer,
1199
1364
  createTypedMethodsMixin,
1365
+ isDataStore,
1200
1366
  isFieldTree,
1201
- isFields,
1202
- isStore
1367
+ isFields
1203
1368
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axi-engine/fields",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "A compact, reactive state management library based on a tree of observable fields.",
5
5
  "license": "MIT",
6
6
  "repository": {