palistor 0.0.25 → 0.0.27

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.
Files changed (255) hide show
  1. package/README.md +609 -542
  2. package/README.ru.md +1015 -0
  3. package/dist/index.d.ts +16 -27
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +17 -28
  6. package/dist/index.js.map +1 -1
  7. package/dist/react/createTrackingProxy.d.ts +27 -27
  8. package/dist/react/createTrackingProxy.d.ts.map +1 -1
  9. package/dist/react/createTrackingProxy.js +54 -53
  10. package/dist/react/createTrackingProxy.js.map +1 -1
  11. package/dist/react/useForm.d.ts +42 -43
  12. package/dist/react/useForm.d.ts.map +1 -1
  13. package/dist/react/useForm.js +38 -38
  14. package/dist/react/useForm.js.map +1 -1
  15. package/dist/react/usePersist.d.ts +14 -14
  16. package/dist/react/usePersist.js +18 -18
  17. package/dist/react/usePersist.js.map +1 -1
  18. package/dist/react/useStoreContext.d.ts +11 -11
  19. package/dist/react/useStoreContext.js +11 -11
  20. package/dist/react/useTranslator.d.ts +12 -12
  21. package/dist/react/useTranslator.js +12 -12
  22. package/dist/store/applyPatch/applyPatch.d.ts +8 -8
  23. package/dist/store/applyPatch/applyPatch.js +12 -12
  24. package/dist/store/applyPatch/applyPatch.js.map +1 -1
  25. package/dist/store/buildProxy/buildEntityProjectionProxy.js +6 -6
  26. package/dist/store/buildProxy/buildEntityProjectionProxy.js.map +1 -1
  27. package/dist/store/buildProxy/buildListProxy.d.ts +8 -8
  28. package/dist/store/buildProxy/buildListProxy.js +30 -30
  29. package/dist/store/buildProxy/buildListProxy.js.map +1 -1
  30. package/dist/store/buildProxy/buildProxy.d.ts +8 -8
  31. package/dist/store/buildProxy/buildProxy.js +39 -39
  32. package/dist/store/buildProxy/buildProxy.js.map +1 -1
  33. package/dist/store/buildProxy/computeProxyKeys.d.ts +8 -8
  34. package/dist/store/buildProxy/computeProxyKeys.js +9 -9
  35. package/dist/store/buildProxy/computeProxyKeys.js.map +1 -1
  36. package/dist/store/buildProxy/handleLazyResolve.d.ts +3 -3
  37. package/dist/store/buildProxy/handleLazyResolve.js +3 -3
  38. package/dist/store/buildProxy/initProxyCaches.d.ts +5 -5
  39. package/dist/store/buildProxy/initProxyCaches.js +5 -5
  40. package/dist/store/compute/computeFieldState.d.ts +3 -3
  41. package/dist/store/compute/computeFieldState.js +8 -8
  42. package/dist/store/compute/computeFieldState.js.map +1 -1
  43. package/dist/store/compute/fieldStateChanged.d.ts +1 -1
  44. package/dist/store/compute/fieldStateChanged.js +1 -1
  45. package/dist/store/compute/recompute/collectGroupComputeNodes.d.ts +4 -4
  46. package/dist/store/compute/recompute/collectGroupComputeNodes.js +7 -7
  47. package/dist/store/compute/recompute/collectGroupComputeNodes.js.map +1 -1
  48. package/dist/store/compute/recompute/recomputeAndNotify.d.ts +3 -3
  49. package/dist/store/compute/recompute/recomputeAndNotify.js +3 -3
  50. package/dist/store/compute/recompute/recomputeLeaves.d.ts +11 -10
  51. package/dist/store/compute/recompute/recomputeLeaves.d.ts.map +1 -1
  52. package/dist/store/compute/recompute/recomputeLeaves.js +26 -27
  53. package/dist/store/compute/recompute/recomputeLeaves.js.map +1 -1
  54. package/dist/store/compute/recompute/recomputeTargeted.d.ts +7 -7
  55. package/dist/store/compute/recompute/recomputeTargeted.js +12 -12
  56. package/dist/store/compute/recompute/recomputeTargeted.js.map +1 -1
  57. package/dist/store/compute/recompute/topologicalSortComputed.d.ts +4 -4
  58. package/dist/store/compute/recompute/topologicalSortComputed.d.ts.map +1 -1
  59. package/dist/store/compute/recompute/topologicalSortComputed.js +11 -13
  60. package/dist/store/compute/recompute/topologicalSortComputed.js.map +1 -1
  61. package/dist/store/compute/recompute/types.d.ts +4 -4
  62. package/dist/store/compute/resolveFlag.d.ts +2 -2
  63. package/dist/store/compute/resolveFlag.js +2 -2
  64. package/dist/store/compute/resolveString.d.ts +5 -5
  65. package/dist/store/compute/resolveString.js +6 -6
  66. package/dist/store/compute/resolveString.js.map +1 -1
  67. package/dist/store/compute/types.d.ts +3 -3
  68. package/dist/store/constants.d.ts +48 -48
  69. package/dist/store/constants.d.ts.map +1 -1
  70. package/dist/store/constants.js +45 -45
  71. package/dist/store/defineFieldMapping.d.ts +21 -19
  72. package/dist/store/defineFieldMapping.d.ts.map +1 -1
  73. package/dist/store/defineFieldMapping.js +21 -19
  74. package/dist/store/defineFieldMapping.js.map +1 -1
  75. package/dist/store/dirtyTracking/collectInitialSnapshot.js +1 -1
  76. package/dist/store/dirtyTracking/collectInitialSnapshot.js.map +1 -1
  77. package/dist/store/dirtyTracking/recomputeDirtyTargeted.d.ts +3 -3
  78. package/dist/store/dirtyTracking/recomputeDirtyTargeted.js +11 -11
  79. package/dist/store/dirtyTracking/recomputeDirtyTargeted.js.map +1 -1
  80. package/dist/store/dirtyTracking/setGroupRevalidate.d.ts +6 -6
  81. package/dist/store/dirtyTracking/setGroupRevalidate.js +7 -7
  82. package/dist/store/dirtyTracking/setGroupRevalidate.js.map +1 -1
  83. package/dist/store/entityRegistry/entityRegistry.d.ts +72 -72
  84. package/dist/store/entityRegistry/entityRegistry.d.ts.map +1 -1
  85. package/dist/store/entityRegistry/entityRegistry.js +91 -91
  86. package/dist/store/entityRegistry/entityRegistry.js.map +1 -1
  87. package/dist/store/entityRegistry/generateId.d.ts +3 -3
  88. package/dist/store/entityRegistry/generateId.js +3 -3
  89. package/dist/store/entityRegistry/types.d.ts +17 -17
  90. package/dist/store/entityRegistry/types.d.ts.map +1 -1
  91. package/dist/store/flow/buildFlowStepsProxy.d.ts +9 -9
  92. package/dist/store/flow/buildFlowStepsProxy.js +13 -13
  93. package/dist/store/flow/buildFlowStepsProxy.js.map +1 -1
  94. package/dist/store/flow/defineFlow.d.ts +27 -25
  95. package/dist/store/flow/defineFlow.d.ts.map +1 -1
  96. package/dist/store/flow/defineFlow.js +15 -13
  97. package/dist/store/flow/defineFlow.js.map +1 -1
  98. package/dist/store/flow/flowApi.d.ts +3 -2
  99. package/dist/store/flow/flowApi.d.ts.map +1 -1
  100. package/dist/store/flow/flowApi.js +1 -1
  101. package/dist/store/flow/flowApi.js.map +1 -1
  102. package/dist/store/flow/flowNavigation.d.ts +65 -63
  103. package/dist/store/flow/flowNavigation.d.ts.map +1 -1
  104. package/dist/store/flow/flowNavigation.js +74 -72
  105. package/dist/store/flow/flowNavigation.js.map +1 -1
  106. package/dist/store/flow/flowState.d.ts +16 -16
  107. package/dist/store/flow/flowState.d.ts.map +1 -1
  108. package/dist/store/flow/flowState.js +5 -5
  109. package/dist/store/flow/flowState.js.map +1 -1
  110. package/dist/store/groupDeps/createGroupDeps.d.ts +3 -3
  111. package/dist/store/groupDeps/createGroupDeps.js +6 -6
  112. package/dist/store/groupDeps/createGroupDeps.js.map +1 -1
  113. package/dist/store/groupDeps/createTrackingValues.d.ts +9 -9
  114. package/dist/store/groupDeps/createTrackingValues.js +11 -11
  115. package/dist/store/groupDeps/createTrackingValues.js.map +1 -1
  116. package/dist/store/groupDeps/getNodeGroupPath.d.ts +3 -3
  117. package/dist/store/groupDeps/getNodeGroupPath.js +5 -5
  118. package/dist/store/groupDeps/getNodeGroupPath.js.map +1 -1
  119. package/dist/store/groupDeps/getRecipientGroups.d.ts +1 -1
  120. package/dist/store/groupDeps/getRecipientGroups.js +1 -1
  121. package/dist/store/groupDeps/groupDeps.d.ts +7 -7
  122. package/dist/store/groupDeps/groupDeps.js +7 -7
  123. package/dist/store/groupDeps/pairKey.d.ts +3 -3
  124. package/dist/store/groupDeps/pairKey.d.ts.map +1 -1
  125. package/dist/store/groupDeps/pairKey.js +4 -4
  126. package/dist/store/groupDeps/pairKey.js.map +1 -1
  127. package/dist/store/groupDeps/resolveGroupByPath.d.ts +3 -3
  128. package/dist/store/groupDeps/resolveGroupByPath.js +3 -3
  129. package/dist/store/init/createNotificationHub.d.ts +31 -31
  130. package/dist/store/init/createNotificationHub.d.ts.map +1 -1
  131. package/dist/store/init/createNotificationHub.js +36 -37
  132. package/dist/store/init/createNotificationHub.js.map +1 -1
  133. package/dist/store/init/createResolveManager.d.ts +72 -69
  134. package/dist/store/init/createResolveManager.d.ts.map +1 -1
  135. package/dist/store/init/createResolveManager.js +101 -98
  136. package/dist/store/init/createResolveManager.js.map +1 -1
  137. package/dist/store/init/initGroupSubmitting.d.ts +2 -2
  138. package/dist/store/init/initGroupSubmitting.js +4 -4
  139. package/dist/store/init/initGroupSubmitting.js.map +1 -1
  140. package/dist/store/normalizeConfig.d.ts +20 -18
  141. package/dist/store/normalizeConfig.d.ts.map +1 -1
  142. package/dist/store/normalizeConfig.js +31 -29
  143. package/dist/store/normalizeConfig.js.map +1 -1
  144. package/dist/store/onChangePipeline/computeFieldKey.d.ts +2 -2
  145. package/dist/store/onChangePipeline/computeFieldKey.js +2 -2
  146. package/dist/store/onChangePipeline/findOnChangeAncestors.d.ts +2 -2
  147. package/dist/store/onChangePipeline/findOnChangeAncestors.js +4 -4
  148. package/dist/store/onChangePipeline/findOnChangeAncestors.js.map +1 -1
  149. package/dist/store/onChangePipeline/onChangePipeline.d.ts +13 -12
  150. package/dist/store/onChangePipeline/onChangePipeline.d.ts.map +1 -1
  151. package/dist/store/onChangePipeline/onChangePipeline.js +14 -13
  152. package/dist/store/onChangePipeline/onChangePipeline.js.map +1 -1
  153. package/dist/store/persist/drivers.d.ts +7 -7
  154. package/dist/store/persist/drivers.js +8 -8
  155. package/dist/store/persist/drivers.js.map +1 -1
  156. package/dist/store/persist/index.d.ts +1 -1
  157. package/dist/store/persist/persistManager.d.ts +23 -23
  158. package/dist/store/persist/persistManager.d.ts.map +1 -1
  159. package/dist/store/persist/persistManager.js +51 -51
  160. package/dist/store/persist/persistManager.js.map +1 -1
  161. package/dist/store/persist/types.d.ts +23 -23
  162. package/dist/store/persist/types.d.ts.map +1 -1
  163. package/dist/store/persist/types.js +4 -4
  164. package/dist/store/resetPipeline/buildResetPatch.d.ts +5 -5
  165. package/dist/store/resetPipeline/buildResetPatch.js +5 -5
  166. package/dist/store/resetPipeline/collectDefaults.d.ts +5 -5
  167. package/dist/store/resetPipeline/collectDefaults.js +6 -6
  168. package/dist/store/resetPipeline/collectDefaults.js.map +1 -1
  169. package/dist/store/resetPipeline/resetPipeline.d.ts +7 -7
  170. package/dist/store/resetPipeline/resetPipeline.js +15 -15
  171. package/dist/store/resetPipeline/resetPipeline.js.map +1 -1
  172. package/dist/store/resolvePipeline/applyPendingWrites.d.ts +10 -10
  173. package/dist/store/resolvePipeline/applyPendingWrites.d.ts.map +1 -1
  174. package/dist/store/resolvePipeline/applyPendingWrites.js +35 -38
  175. package/dist/store/resolvePipeline/applyPendingWrites.js.map +1 -1
  176. package/dist/store/resolvePipeline/createContextTrackingProxy.d.ts +8 -7
  177. package/dist/store/resolvePipeline/createContextTrackingProxy.d.ts.map +1 -1
  178. package/dist/store/resolvePipeline/createContextTrackingProxy.js +6 -5
  179. package/dist/store/resolvePipeline/createContextTrackingProxy.js.map +1 -1
  180. package/dist/store/resolvePipeline/createValuesTrackingProxy.d.ts +43 -43
  181. package/dist/store/resolvePipeline/createValuesTrackingProxy.d.ts.map +1 -1
  182. package/dist/store/resolvePipeline/createValuesTrackingProxy.js +70 -70
  183. package/dist/store/resolvePipeline/createValuesTrackingProxy.js.map +1 -1
  184. package/dist/store/resolvePipeline/executeListResolve.d.ts +14 -14
  185. package/dist/store/resolvePipeline/executeListResolve.js +27 -27
  186. package/dist/store/resolvePipeline/executeListResolve.js.map +1 -1
  187. package/dist/store/resolvePipeline/executeResolve.d.ts +15 -15
  188. package/dist/store/resolvePipeline/executeResolve.js +46 -46
  189. package/dist/store/resolvePipeline/executeResolve.js.map +1 -1
  190. package/dist/store/store/NodeRegistry/nodeRegistry.d.ts +61 -61
  191. package/dist/store/store/NodeRegistry/nodeRegistry.d.ts.map +1 -1
  192. package/dist/store/store/NodeRegistry/nodeRegistry.js +85 -86
  193. package/dist/store/store/NodeRegistry/nodeRegistry.js.map +1 -1
  194. package/dist/store/store/NodeRegistry/nodeUtils.d.ts +5 -5
  195. package/dist/store/store/NodeRegistry/nodeUtils.d.ts.map +1 -1
  196. package/dist/store/store/NodeRegistry/nodeUtils.js +5 -5
  197. package/dist/store/store/NodeRegistry/nodeUtils.js.map +1 -1
  198. package/dist/store/store/groupDepsMap.d.ts +23 -23
  199. package/dist/store/store/groupDepsMap.d.ts.map +1 -1
  200. package/dist/store/store/groupDepsMap.js +35 -34
  201. package/dist/store/store/groupDepsMap.js.map +1 -1
  202. package/dist/store/store/hasComputedProps.d.ts +2 -2
  203. package/dist/store/store/hasComputedProps.js +2 -2
  204. package/dist/store/store/nodeMap.d.ts +5 -5
  205. package/dist/store/store/nodeMap.js +7 -7
  206. package/dist/store/store/nodeMap.js.map +1 -1
  207. package/dist/store/store/palistor.d.ts +80 -80
  208. package/dist/store/store/palistor.d.ts.map +1 -1
  209. package/dist/store/store/palistor.js +149 -169
  210. package/dist/store/store/palistor.js.map +1 -1
  211. package/dist/store/store/registerNodes.d.ts +10 -10
  212. package/dist/store/store/registerNodes.js +15 -15
  213. package/dist/store/store/registerNodes.js.map +1 -1
  214. package/dist/store/store/serviceRegistry.d.ts +8 -8
  215. package/dist/store/store/serviceRegistry.d.ts.map +1 -1
  216. package/dist/store/store/serviceRegistry.js +8 -8
  217. package/dist/store/store/serviceRegistry.js.map +1 -1
  218. package/dist/store/store/types.d.ts +236 -234
  219. package/dist/store/store/types.d.ts.map +1 -1
  220. package/dist/store/submitPipeline/applyLeafBeforeSubmit.d.ts +2 -2
  221. package/dist/store/submitPipeline/applyLeafBeforeSubmit.js +2 -2
  222. package/dist/store/submitPipeline/collectLeafStates.d.ts +2 -2
  223. package/dist/store/submitPipeline/collectLeafStates.js +2 -2
  224. package/dist/store/submitPipeline/submitPipeline.d.ts +8 -8
  225. package/dist/store/submitPipeline/submitPipeline.js +12 -12
  226. package/dist/store/submitPipeline/submitPipeline.js.map +1 -1
  227. package/dist/store/submitPipeline/types.d.ts +1 -1
  228. package/dist/store/submitPipeline/types.d.ts.map +1 -1
  229. package/dist/store/traversal/nodeClassifier.d.ts +9 -9
  230. package/dist/store/traversal/nodeClassifier.d.ts.map +1 -1
  231. package/dist/store/traversal/nodeClassifier.js +11 -11
  232. package/dist/store/traversal/nodeClassifier.js.map +1 -1
  233. package/dist/store/traversal/walkFull.d.ts +14 -14
  234. package/dist/store/traversal/walkFull.js +2 -2
  235. package/dist/store/valuesCache/valuesCache.d.ts +9 -9
  236. package/dist/store/valuesCache/valuesCache.d.ts.map +1 -1
  237. package/dist/store/valuesCache/valuesCache.js +6 -6
  238. package/dist/store/writePipeline/formatPatch.d.ts +5 -5
  239. package/dist/store/writePipeline/formatPatch.js +7 -7
  240. package/dist/store/writePipeline/formatPatch.js.map +1 -1
  241. package/dist/store/writePipeline/formatValue.d.ts +5 -5
  242. package/dist/store/writePipeline/formatValue.js +5 -5
  243. package/dist/store/writePipeline/mergeChanged.d.ts +4 -4
  244. package/dist/store/writePipeline/mergeChanged.js +4 -4
  245. package/dist/store/writePipeline/runSetter.d.ts +6 -6
  246. package/dist/store/writePipeline/runSetter.js +8 -8
  247. package/dist/store/writePipeline/storeValue.d.ts +3 -3
  248. package/dist/store/writePipeline/storeValue.js +3 -3
  249. package/dist/store/writePipeline/types.d.ts +4 -4
  250. package/dist/store/writePipeline/types.d.ts.map +1 -1
  251. package/dist/store/writePipeline/writePipeline.d.ts +7 -7
  252. package/dist/store/writePipeline/writePipeline.d.ts.map +1 -1
  253. package/dist/store/writePipeline/writePipeline.js +15 -15
  254. package/dist/store/writePipeline/writePipeline.js.map +1 -1
  255. package/package.json +11 -4
@@ -22,8 +22,8 @@ import { normalizeConfig } from "../normalizeConfig";
22
22
  import { initFlows } from "../flow/flowNavigation";
23
23
  // ─── Palistor ─────────────────────────────────────────────────────────────────
24
24
  /**
25
- * Публичный класс формы. Одновременно является DI-контейнером для всех
26
- * внутренних подсистем и реализует публичный интерфейс `ProxyStore`.
25
+ * The public form class. Acts as the DI container for all internal
26
+ * subsystems and implements the public `ProxyStore` interface.
27
27
  *
28
28
  * @example
29
29
  * const store = new Palistor({ config: myConfig, initialValues: {...} });
@@ -31,7 +31,7 @@ import { initFlows } from "../flow/flowNavigation";
31
31
  * store.submit();
32
32
  */
33
33
  export class Palistor {
34
- // ─── Конструктор ──────────────────────────────────────────────────────────
34
+ // ─── Constructor ──────────────────────────────────────────────────────────
35
35
  constructor(options) {
36
36
  /**
37
37
  * @internal Plain POJO mirrors for each entity — used in valuesCache.values
@@ -40,16 +40,16 @@ export class Palistor {
40
40
  */
41
41
  this.entityProjectionObjs = new Map();
42
42
  /**
43
- * Нереактивный контекстпроизвольные данные, доступные через `store.context`.
44
- * Устанавливается через `setContext()` или хук `useStoreContext()`.
43
+ * Non-reactive contextarbitrary data available via `store.context`.
44
+ * Set via `setContext()` or the `useStoreContext()` hook.
45
45
  */
46
46
  this._context = {};
47
47
  const { config, initialValues = {} } = options;
48
- // ─── Field mapping (две проекции карты) ──────────────────────────────────
49
- // fwd: internal → external (для ownKeys/spread).
50
- // externalToInternal: external → internal (для GET/SET/tracking).
51
- // Обе пусты, когда fieldMapping не передан → `?? key` возвращает ключ как есть
52
- // → нулевой оверхед по умолчанию.
48
+ // ─── Field mapping (two projections of the map) ──────────────────────────
49
+ // fwd: internal → external (for ownKeys/spread).
50
+ // externalToInternal: external → internal (for GET/SET/tracking).
51
+ // Both are empty when fieldMapping is not provided → `?? key` returns the
52
+ // key as-is zero overhead by default.
53
53
  const fwd = options.fieldMapping ?? {};
54
54
  this.fieldMapping = fwd;
55
55
  this.externalToInternal = {};
@@ -58,14 +58,14 @@ export class Palistor {
58
58
  if (external !== undefined)
59
59
  this.externalToInternal[external] = internal;
60
60
  }
61
- // ─── Нормализация конфига (external → internal) ───────────────────────────
62
- // Конфиг пишется в ПУБЛИЧНЫХ (замапленных) именах. Один проход приводит его
63
- // к internal-именам ДО init/compute/traversal — всё ядро ниже работает с
64
- // internal-именами без изменений. Пустая картавозвращается исходный
65
- // config без копий (нулевой оверхед).
61
+ // ─── Config normalization (external → internal) ──────────────────────────
62
+ // The config is authored in PUBLIC (mapped) names. A single pass converts
63
+ // it to internal names BEFORE init/compute/traversal — everything below
64
+ // works with internal names unchanged. Empty mapthe original config is
65
+ // returned without copying (zero overhead).
66
66
  const rootConfig = normalizeConfig(config, this.externalToInternal, fwd);
67
67
  this.rootConfig = rootConfig;
68
- // ─── Сервисы ────────────────────────────────────────────────────────────
68
+ // ─── Services ────────────────────────────────────────────────────────────
69
69
  this.services = new ServiceRegistry();
70
70
  const { translate, notify } = this.services;
71
71
  // ─── NodeRegistry ────────────────────────────────────────────────────────
@@ -81,9 +81,9 @@ export class Palistor {
81
81
  for (const ls of this.nodes.allListStates) {
82
82
  this.entityRegistry.registerList(ls);
83
83
  }
84
- // ─── GroupDepsMap + первый recompute ──────────────────────────────────────
84
+ // ─── GroupDepsMap + first recompute ──────────────────────────────────────
85
85
  this.groupDepsMap = new GroupDepsMap(rootConfig, nodePaths, nodeParents);
86
- this.recompute(); // первый полный пересчётстроит карту зависимостей
86
+ this.recompute(); // first full recomputebuilds the dependency map
87
87
  this.dirty.capture(rootConfig, nodeState);
88
88
  // ─── NotificationHub ────────────────────────────────────────────────────
89
89
  this.hub = new NotificationHub({ computeNodes, nodePaths });
@@ -102,7 +102,7 @@ export class Palistor {
102
102
  syncListValuesCache: (listState) => this.syncListValuesCache(listState),
103
103
  entityRegistry: this.entityRegistry,
104
104
  });
105
- // ─── Pipeline классы ─────────────────────────────────────────────────────
105
+ // ─── Pipeline classes ─────────────────────────────────────────────────────
106
106
  this.writePipeline = new WritePipeline(this);
107
107
  this.resetPipeline = new ResetPipeline(this);
108
108
  this.submitPipeline = new SubmitPipeline(this);
@@ -115,26 +115,26 @@ export class Palistor {
115
115
  const postNotifyHook = this.resolveManager.createPostNotifyHook();
116
116
  if (postNotifyHook)
117
117
  this.hub.setPostNotifyHook(postNotifyHook);
118
- // ─── Начальный контекст (Phase 4) ────────────────────────────────────────
118
+ // ─── Initial context ─────────────────────────────────────────────────────
119
119
  if (options.context) {
120
120
  this._context = options.context;
121
121
  }
122
- // ─── Flow: entry lifecycle первого шага ──────────────────────────────────
123
- // Первый шаг каждого флоу «входится» при создании store:
124
- // onEnter → resolve (eager) → onReady. До launchEager чтобы флоу сам
125
- // триггерил idle-resolve шага и корректно прикреплял onReady.
122
+ // ─── Flow: entry lifecycle of the first step ─────────────────────────────
123
+ // The first step of every flow is "entered" at store creation:
124
+ // onEnter → resolve (eager) → onReady. Runs before launchEager so the flow
125
+ // itself triggers the step's idle resolve and attaches onReady correctly.
126
126
  initFlows(this);
127
127
  // ─── Launch eager resolvers ──────────────────────────────────────────────
128
128
  this.resolveManager.launchEager();
129
129
  }
130
- // ─── @internal методы-фасады ──────────────────────────────────────────────
130
+ // ─── @internal facade methods ─────────────────────────────────────────────
131
131
  /**
132
- * Пересчитать состояние узлов.
132
+ * Recompute node state.
133
133
  *
134
- * - `changedNodes` передан и не пуст таргетированный пересчёт (быстро)
135
- * - иначеполный пересчёт всего дерева (init, reset, resolve-завершение)
134
+ * - `changedNodes` provided and non-emptytargeted recompute (fast)
135
+ * - otherwisefull recompute of the whole tree (init, reset, resolve completion)
136
136
  *
137
- * При первом вызове без changedNodes строит карту групповых зависимостей.
137
+ * The first call without changedNodes builds the group dependency map.
138
138
  *
139
139
  * @internal
140
140
  */
@@ -163,8 +163,8 @@ export class Palistor {
163
163
  return recomputeLeaves(computeNodes, nodeState, this.values, translate);
164
164
  }
165
165
  /**
166
- * Уведомить подписчиков об изменённых узлах.
167
- * Пересчитывает dirty-флаги и инкрементирует версии.
166
+ * Notify subscribers about changed nodes.
167
+ * Recomputes dirty flags and bumps versions.
168
168
  *
169
169
  * @internal
170
170
  */
@@ -178,14 +178,14 @@ export class Palistor {
178
178
  nodePaths: this.nodes.nodePaths,
179
179
  });
180
180
  }
181
- // ─── @internal pipeline-методы ────────────────────────────────────────────
182
- /** @internal Применить bulk-патч к узлу (один recompute + notify). */
181
+ // ─── @internal pipeline methods ───────────────────────────────────────────
182
+ /** @internal Apply a bulk patch to a node (single recompute + notify). */
183
183
  setValuesNode(node, patch) {
184
184
  const formatted = formatPatch(node, patch, this.values.values);
185
185
  const changed = applyPatch(node, this.nodes.nodeState, formatted, new Set(), this.values);
186
186
  _recomputeAndNotify(changed, () => this.recompute(), (c) => this.notifyChanged(c));
187
187
  }
188
- // ─── ProxyStore — публичный API ───────────────────────────────────────────
188
+ // ─── ProxyStore — public API ──────────────────────────────────────────────
189
189
  get proxy() {
190
190
  return this._proxy;
191
191
  }
@@ -241,11 +241,11 @@ export class Palistor {
241
241
  this.setValuesNode(this.rootConfig, patch);
242
242
  }
243
243
  /**
244
- * Создать или обновить entity (или массив entities) в реестре.
244
+ * Create or update an entity (or an array of entities) in the registry.
245
245
  *
246
- * - Если entity с таким id не существует создаётся и регистрируются leaf-ноды.
247
- * - Если существуетрекурсивный merge; обновлённые leaf-ноды маркируются как изменённые.
248
- * - Batch-режим: массив entities обрабатывается одним recompute + notifyChanged.
246
+ * - If no entity with the id existsit is created and its leaf nodes registered.
247
+ * - If it exists recursive merge; updated leaf nodes are marked as changed.
248
+ * - Batch mode: an array of entities is processed in one recompute + notifyChanged.
249
249
  */
250
250
  set(data) {
251
251
  const items = Array.isArray(data) ? data : [data];
@@ -260,14 +260,14 @@ export class Palistor {
260
260
  this.notifyChanged(recomputed);
261
261
  }
262
262
  /**
263
- * Переименовать entity: перенести с oldId на newId.
263
+ * Rename an entity: move it from oldId to newId.
264
264
  *
265
- * - Обновляет EntityRegistry (entities Map, bindings, resolvedCache, id leaf value).
266
- * - Обновляет itemIds во всех ListState-объектах.
267
- * - Обновляет entityProjectionObjs (переносит POJO-зеркало).
268
- * - Уведомляет подписчиков об изменении id leaf.
265
+ * - Updates EntityRegistry (entities Map, bindings, resolvedCache, id leaf value).
266
+ * - Updates itemIds in all ListState objects.
267
+ * - Updates entityProjectionObjs (moves the POJO mirror).
268
+ * - Notifies subscribers about the id leaf change.
269
269
  *
270
- * No-op если entity с oldId не существует.
270
+ * No-op when no entity with oldId exists.
271
271
  */
272
272
  rekey(oldId, newId) {
273
273
  const entity = this.entityRegistry.get(oldId);
@@ -297,57 +297,56 @@ export class Palistor {
297
297
  this.notifyChanged(recomputed);
298
298
  }
299
299
  /**
300
- * Удалить entity из реестра по ID.
300
+ * Delete an entity from the registry by ID.
301
301
  *
302
- * - Удаляет leaf-ноды entity из NodeRegistry (computeNodes, groupComputeMap).
303
- * - Очищает bindings и resolvedCache.
304
- * - Уведомляет подписчиков.
302
+ * - Removes the entity's leaf nodes from NodeRegistry (computeNodes, groupComputeMap).
303
+ * - Clears bindings and resolvedCache.
304
+ * - Notifies subscribers.
305
305
  *
306
- * No-op если entity не существует.
306
+ * No-op when the entity does not exist.
307
307
  */
308
308
  delete(id) {
309
309
  const entityNode = this.entityRegistry.get(id);
310
310
  if (!entityNode)
311
311
  return;
312
- // C2: каскадное удаление child-entity, принадлежащих этой entity.
313
- // Снимаем копию множества — childrenByOwner мутируется при рекурсивном delete.
312
+ // Cascade-delete child entities owned by this entity.
313
+ // Copy the set — childrenByOwner is mutated during the recursive delete.
314
314
  const childIds = this.entityRegistry.getChildrenByOwner(id);
315
315
  if (childIds && childIds.size > 0) {
316
316
  for (const childId of [...childIds]) {
317
317
  this.delete(childId);
318
318
  }
319
319
  }
320
- // Собрать все leaf-ноды entity
320
+ // Collect all leaf nodes of the entity
321
321
  const deletedLeaves = new Set();
322
322
  this.collectEntityLeaves(entityNode, deletedLeaves);
323
- // Удалить leaf-ноды из NodeRegistry (предотвращает утечку памяти)
323
+ // Remove leaf nodes from NodeRegistry (prevents a memory leak)
324
324
  for (const leaf of deletedLeaves) {
325
325
  this.nodes.unregisterLeaf(leaf);
326
326
  }
327
- // Phase 4: cleanup per-entity field resolve states
327
+ // Cleanup per-entity field resolve states
328
328
  this.resolveManager.cleanupEntityResolveStates(id);
329
- // Удалить entity из реестра (очищает bindings + resolvedCache)
329
+ // Remove the entity from the registry (clears bindings + resolvedCache)
330
330
  this.entityRegistry.delete(id);
331
- // Уведомить подписчиков
332
331
  this.notifyChanged(deletedLeaves);
333
332
  }
334
333
  /**
335
- * Сбросить resolved-кэш для entity (все template или конкретный).
334
+ * Clear the resolved cache for an entity (all templates or a specific one).
336
335
  *
337
- * - `invalidate(id)` — очистить весь кэш для entity
338
- * - `invalidate(id, templateNode)` — очистить только для конкретной пары
336
+ * - `invalidate(id)` — clear the whole cache for the entity
337
+ * - `invalidate(id, templateNode)` — clear only for that specific pair
339
338
  *
340
- * При следующем mount useForm(entity, template) resolve будет перезапущен.
339
+ * On the next mount of useForm(entity, template) the resolve re-runs.
341
340
  */
342
341
  invalidate(id, templateNode) {
343
342
  this.entityRegistry.clearResolved(id, templateNode);
344
343
  }
345
344
  /**
346
- * Submit entity через template.
347
- * Вызывается из EntityProjectionProxy.submit().
345
+ * Submit an entity through a template.
346
+ * Called from EntityProjectionProxy.submit().
348
347
  *
349
348
  * 1. submitting: true → notify
350
- * 2. Валидация через template field rules (validate)
349
+ * 2. Validation via template field rules (validate)
351
350
  * 3. templateNode.onSubmit(entityProxy, store)
352
351
  * 4. templateNode.afterSubmit(result, { reset })
353
352
  * 5. submitting: false → notify
@@ -355,7 +354,6 @@ export class Palistor {
355
354
  * @internal
356
355
  */
357
356
  async executeEntityTemplateSubmit(entityId, templateNode, entityProxy) {
358
- // Шаг 1: Найти entity в реестре. Если не найдена — ранний выход с ошибкой.
359
357
  const entityNode = this.entityRegistry.get(entityId);
360
358
  if (!entityNode) {
361
359
  return {
@@ -363,34 +361,25 @@ export class Palistor {
363
361
  errors: [{ path: "", message: `Entity "${entityId}" not found` }],
364
362
  };
365
363
  }
366
- // Шаг 2: Получить (или создать) объект состояния привязки { loading, submitting }
367
- // для пары (entityId, templateNode). Используется EntityProjectionProxy
368
- // для отображения спиннера в UI.
364
+ // Binding state { loading, submitting } for the (entityId, templateNode)
365
+ // pair read by EntityProjectionProxy to render a spinner in the UI.
369
366
  const bindingState = this.resolveManager.entityStates.getOrCreate(entityId, templateNode);
370
367
  const entityNodeObj = entityNode;
371
- // Шаг 3: Поднять флаг submitting и уведомить подписчиков (React перерендерит
372
- // компоненты, привязанные к этой entity — например, кнопка покажет спиннер).
373
368
  bindingState.submitting = true;
374
369
  this.notifyChanged(new Set([entityNodeObj]));
375
370
  try {
376
- // Шаг 4: Валидация рекурсивно обойти template-поля и вызвать validate()
377
- // для каждого leaf-поля, у которого есть валидатор. Текущие значения
378
- // берутся из nodeState entity.
371
+ // Validationrecursively walk template fields and call validate() on
372
+ // every leaf that has one. Current values come from the entity's nodeState.
379
373
  const errors = [];
380
374
  this.collectEntityTemplateErrors(templateNode, entityNode, errors, "");
381
- // Если есть ошибки валидации — вернуть их, не вызывая onSubmit.
382
375
  if (errors.length > 0) {
383
376
  return { success: false, errors };
384
377
  }
385
- // Шаг 5: Вызвать пользовательский onSubmit(entityProxy, store).
386
- // entityProxy — это Proxy entity с template-правилами, store — сам Palistor.
387
- // Обычно здесь выполняется API-запрос на сервер.
378
+ // User onSubmit(entityProxy, store) — typically the API call.
388
379
  let result;
389
380
  if (typeof templateNode.onSubmit === "function") {
390
381
  result = await templateNode.onSubmit(entityProxy, this);
391
382
  }
392
- // Шаг 6: Вызвать afterSubmit(result, { reset }) — хук после успешного submit.
393
- // reset для entity template — no-op (у entity нет встроенного сброса, в отличие от form).
394
383
  if (typeof templateNode.afterSubmit === "function") {
395
384
  const reset = () => void 0; // entity template has no built-in reset
396
385
  await templateNode.afterSubmit(result, { reset });
@@ -398,13 +387,12 @@ export class Palistor {
398
387
  return { success: true, result };
399
388
  }
400
389
  finally {
401
- // Шаг 7 (always): Снять флаг submitting и уведомить подписчиков.
402
- // finally гарантирует сброс даже при ошибке в onSubmit/afterSubmit.
390
+ // Always clear submitting, even when onSubmit/afterSubmit throws.
403
391
  bindingState.submitting = false;
404
392
  this.notifyChanged(new Set([entityNodeObj]));
405
393
  }
406
394
  }
407
- // ─── Приватные helpers для entity ────────────────────────────────────────
395
+ // ─── Private entity helpers ──────────────────────────────────────────────
408
396
  /**
409
397
  * Upsert entities in EntityRegistry and register/update their leaf nodes.
410
398
  * Returns Set of changed leaf nodes. Does NOT call recompute or notifyChanged.
@@ -412,7 +400,7 @@ export class Palistor {
412
400
  * Used internally by `set()` and by `executeListResolve` via the
413
401
  * `setEntitiesRaw` callback in ResolveManagerDeps.
414
402
  *
415
- * Phase 4: when `listNode` is provided, triggers entity field resolves for
403
+ * When `listNode` is provided, triggers entity field resolves for
416
404
  * each template field entry belonging to that list.
417
405
  *
418
406
  * @internal
@@ -420,22 +408,22 @@ export class Palistor {
420
408
  _setEntitiesRaw(items, listNode) {
421
409
  const changed = new Set();
422
410
  for (const item of items) {
423
- // Создать новый EntityNode или обновить существующий (рекурсивный merge).
424
- // Если item.id отсутствует EntityRegistry сгенерирует временный _tmp_* id.
411
+ // Create a new EntityNode or update the existing one (recursive merge).
412
+ // When item.id is missing, EntityRegistry generates a temporary _tmp_* id.
425
413
  const entityNode = this.entityRegistry.upsert(item);
426
414
  const entityId = entityNode.id.value;
427
- // Все entity-ноды живут в namespace "_entity_." для изоляции от config-нод формы.
415
+ // All entity nodes live in the "_entity_." namespace, isolated from form config nodes.
428
416
  const entityPrefix = `_entity_.${entityId}`;
429
- // projectionObj — «зеркало» entity в виде plain POJO { id, field1, field2 }.
430
- // Используется в valuesCache.values как элемент массива списка.
431
- // Ссылочная идентичность POJO сохраняется между upsert-ами, меняются только значения.
417
+ // projectionObj — the entity's plain-POJO mirror { id, field1, field2 }.
418
+ // Used in valuesCache.values as a list array element.
419
+ // The POJO's referential identity survives upserts; only values change.
432
420
  let projectionObj = this.entityProjectionObjs.get(entityId);
433
421
  if (!projectionObj) {
434
422
  projectionObj = {};
435
423
  this.entityProjectionObjs.set(entityId, projectionObj);
436
424
  }
437
- // DFS-обход entity-дерева: зарегистрировать новые leaf-ноды
438
- // или обнаружить изменения в существующих.
425
+ // DFS over the entity tree: register new leaf nodes
426
+ // or detect changes in existing ones.
439
427
  this.walkAndSyncEntityNode(entityNode, entityPrefix, entityNode, changed, projectionObj);
440
428
  // Entity field resolves are lazy-only: triggered when a component first reads
441
429
  // field.value or field.loading (via queueMicrotask in buildEntityProjectionProxy).
@@ -444,19 +432,19 @@ export class Palistor {
444
432
  return changed;
445
433
  }
446
434
  /**
447
- * Синхронизировать valuesCache с составом спискаЕДИНЫЙ метод (root + per-entity).
435
+ * Sync valuesCache with list membershipa SINGLE method (root + per-entity).
448
436
  *
449
- * Ветвится по `listState.ownerEntity`:
450
- * - `null` → root: пишем массив POJO-зеркал в `nodeSlot` config-узла
451
- * (`valuesCache.values.users = [{id:"u1",…}, …]` — тот же массив, что читает
452
- * `proxy.users.value`, поэтому React видит обновление);
453
- * - entity → per-entity (вариант C, C3/C4): материализуем состав в projectionObj
454
- * владельца по пути списка (`["contacts"]` или `["profile","contacts"]`). Это
455
- * включает вложенный список в `store.getValues()` — projectionObj владельца
456
- * входит в массив корневого списка по ссылке, а child-projectionObj-ы
457
- * рекурсивно материализуют свои списки.
437
+ * Branches on `listState.ownerEntity`:
438
+ * - `null` → root: write the array of POJO mirrors into the config node's
439
+ * `nodeSlot` (`valuesCache.values.users = [{id:"u1",…}, …]` — the same
440
+ * array that `proxy.users.value` reads, so React sees the update);
441
+ * - entity → per-entity: materialize the membership into the owner's
442
+ * projectionObj at the list path (`["contacts"]` or `["profile","contacts"]`).
443
+ * This includes the nested list in `store.getValues()` — the owner's
444
+ * projectionObj is referenced by the root list array, and child
445
+ * projectionObjs materialize their lists recursively.
458
446
  *
459
- * No-op, если слот/путь/projectionObj владельца отсутствуют.
447
+ * No-op when the slot/path/owner projectionObj is missing.
460
448
  *
461
449
  * @internal
462
450
  */
@@ -465,14 +453,14 @@ export class Palistor {
465
453
  .map((id) => this.entityProjectionObjs.get(id))
466
454
  .filter((obj) => obj !== undefined);
467
455
  if (listState.ownerEntity === null) {
468
- // Root: слот в valuesCache (пара { parent, key }) по config-узлу.
456
+ // Root: the valuesCache slot ({ parent, key } pair) keyed by config node.
469
457
  const slot = this.values.nodeSlot.get(listState.listConfigNode);
470
458
  if (!slot)
471
459
  return;
472
460
  slot.parent[slot.key] = materialized;
473
461
  return;
474
462
  }
475
- // Per-entity: спускаемся к родительскому POJO по пути и пишем состав.
463
+ // Per-entity: descend to the parent POJO along the path and write the membership.
476
464
  const listConfigNode = listState.listConfigNode;
477
465
  const path = this.nodes.listFieldKeys.get(listConfigNode);
478
466
  if (!path || path.length === 0)
@@ -493,22 +481,22 @@ export class Palistor {
493
481
  }
494
482
  target[path[path.length - 1]] = materialized;
495
483
  }
496
- /** @internal Текущий id entity (учитывает rekey через nodeState). */
484
+ /** @internal Current entity id (accounts for rekey via nodeState). */
497
485
  _entityId(entity) {
498
486
  const idLeaf = entity.id;
499
487
  return (this.nodes.nodeState.get(idLeaf)?.value ??
500
488
  entity.id.value);
501
489
  }
502
490
  /**
503
- * Восстановить состав корневых И per-entity списков из снимка значений
504
- * (вариант C, C3 — persist hydrate).
491
+ * Restore the membership of root AND per-entity lists from a values
492
+ * snapshot (persist hydrate).
505
493
  *
506
- * `applyPatch` пропускает list-узлы, поэтому состав списков восстанавливается
507
- * отдельным проходом: для каждого list-поля создаём child-entity, проставляем
508
- * owner-ссылку (для вложенных), заполняем `itemIds`/`initialItemIds` и
509
- * синхронизируем valuesCache. Рекурсивно обрабатывает nested-of-nested.
494
+ * `applyPatch` skips list nodes, so list membership is restored in a
495
+ * separate pass: for every list field we create child entities, set the
496
+ * owner reference (for nested ones), fill `itemIds`/`initialItemIds` and
497
+ * sync valuesCache. Handles nested-of-nested recursively.
510
498
  *
511
- * Возвращает множество изменённых узлов для последующего notify.
499
+ * Returns the set of changed nodes for a subsequent notify.
512
500
  *
513
501
  * @internal
514
502
  */
@@ -522,7 +510,7 @@ export class Palistor {
522
510
  const child = configNode[key];
523
511
  if (!child || typeof child !== "object")
524
512
  continue;
525
- // Вложенная группа (не список): рекурсия, чтобы достать списки внутри неё.
513
+ // Nested group (not a list): recurse to reach the lists inside it.
526
514
  if (!Array.isArray(child)) {
527
515
  if (isGroupNode(child)) {
528
516
  const nested = valueObj?.[key];
@@ -555,7 +543,7 @@ export class Palistor {
555
543
  if (ownerEntity) {
556
544
  this.entityRegistry.setEntityOwner(childEntity, this._entityId(ownerEntity), listConfigNode);
557
545
  }
558
- // Рекурсия во вложенные списки этого item.
546
+ // Recurse into this item's nested lists.
559
547
  this._restoreListsRec(template, itemObj, childEntity, changed);
560
548
  }
561
549
  }
@@ -572,7 +560,7 @@ export class Palistor {
572
560
  listState.itemIds = ids;
573
561
  listState.initialItemIds = [...ids];
574
562
  this.syncListValuesCache(listState);
575
- // U2: ListState ключ трекинга; listConfigNode мост обратной совместимости.
563
+ // ListState is the tracking key; listConfigNode is the backward-compat bridge.
576
564
  changed.add(listState);
577
565
  changed.add(listConfigNode);
578
566
  }
@@ -580,57 +568,57 @@ export class Palistor {
580
568
  }
581
569
  }
582
570
  /**
583
- * Вернуть поверхностную копию объекта данных entity без list-полей
584
- * (их состав восстанавливается отдельно через EntityListState). Иначе
585
- * `createEntityNode` затянул бы массив как обычный leaf-value.
571
+ * Return a shallow copy of an entity data object without list fields
572
+ * (their membership is restored separately via EntityListState). Otherwise
573
+ * `createEntityNode` would ingest the array as a regular leaf value.
586
574
  */
587
575
  _stripListFields(itemObj, template) {
588
576
  const result = {};
589
577
  for (const key of Object.keys(itemObj)) {
590
578
  const tField = template?.[key];
591
579
  if (Array.isArray(tField))
592
- continue; // list-поле — пропускаем
580
+ continue; // skip list fields
593
581
  result[key] = itemObj[key];
594
582
  }
595
583
  return result;
596
584
  }
597
585
  /**
598
- * Рекурсивный обход entity node: регистрирует новые leaf-ноды через
599
- * `registerDynamicLeaf` и собирает изменённые в `changed`.
586
+ * Recursive walk over an entity node: registers new leaf nodes via
587
+ * `registerDynamicLeaf` and accumulates changed ones in `changed`.
600
588
  *
601
- * Также поддерживает entity projection POJO (projectionObj):
602
- * - Новые leaf-ноды: регистрируют nodeSlot → projectionObj
603
- * - Существующие leaf-ноды: обновляют через updateValuesCacheEntry
589
+ * Also maintains the entity projection POJO (projectionObj):
590
+ * - New leaf nodes: register nodeSlot → projectionObj
591
+ * - Existing leaf nodes: updated via updateValuesCacheEntry
604
592
  *
605
- * @param node Текущий узел entity для обхода
606
- * @param prefix Dot-путь текущего узла (e.g. "_entity_.u1")
607
- * @param parent Родительский объект (для регистрации leaf-нод)
608
- * @param changed Множество изменённых узлов (накапливается)
593
+ * @param node Current entity node being walked
594
+ * @param prefix Dot-path of the current node (e.g. "_entity_.u1")
595
+ * @param parent Parent object (for leaf registration)
596
+ * @param changed Accumulated set of changed nodes
609
597
  * @param projectionObj Plain POJO at the current nesting level for valuesCache
610
598
  */
611
599
  walkAndSyncEntityNode(node, prefix, parent, changed, projectionObj) {
612
- // Зарегистрировать dot-path группового узла (e.g. "_entity_.u1", "_entity_.u1.address").
613
- // nodePaths используется в compute-подсистеме для определения «группы» листа
614
- // (getNodeGroupPath) и при таргетированном recompute.
600
+ // Register the group node's dot-path (e.g. "_entity_.u1", "_entity_.u1.address").
601
+ // nodePaths is used by the compute subsystem to determine a leaf's "group"
602
+ // (getNodeGroupPath) and during targeted recompute.
615
603
  if (!this.nodes.nodePaths.has(parent)) {
616
604
  this.nodes.nodePaths.set(parent, prefix);
617
605
  }
618
606
  for (const key of Object.keys(node)) {
619
607
  const child = node[key];
620
- // Пропускаем примитивы (не часть entity-дерева)
608
+ // Skip primitives (not part of the entity tree)
621
609
  if (!child || typeof child !== "object")
622
610
  continue;
623
611
  const childObj = child;
624
612
  const childPath = `${prefix}.${key}`;
625
613
  if (isLeafNode(childObj)) {
626
- // ── Листовой узел (EntityLeafNode): { value: <текущее значение> } ──
614
+ // ── Leaf node (EntityLeafNode): { value: <current value> } ──
627
615
  const leaf = childObj;
628
616
  if (!this.nodes.nodeState.has(childObj)) {
629
- // Первая встреча с этим leaf — зарегистрировать в NodeRegistry:
630
- // - nodeState: хранит FieldState (value, isVisible, dirty, etc.)
631
- // - nodePaths: маппинг node → dot-path
632
- // - nodeParents: маппинг node → parent group
633
- // Все entity leaf-ы всегда visible, не обязательны, не disabled.
617
+ // First encounter of this leaf — register in NodeRegistry:
618
+ // - nodeState: holds FieldState (value, isVisible, dirty, etc.)
619
+ // - nodePaths: node → dot-path mapping
620
+ // - nodeParents: node → parent group mapping
621
+ // Entity leaves are always visible, never required/disabled.
634
622
  this.nodes.registerDynamicLeaf(childObj, childPath, parent, {
635
623
  value: leaf.value,
636
624
  isVisible: true,
@@ -643,9 +631,9 @@ export class Palistor {
643
631
  // Seed initialValueMap so recomputeDirtyTargeted compares against the entity's
644
632
  // loaded value instead of `undefined` — prevents spurious dirty=true on first load.
645
633
  this.dirty.initialValueMap.set(childObj, leaf.value);
646
- // Привязать leaf к POJO-зеркалу через nodeSlot.
647
- // При следующих updateValuesCacheEntry(node, newValue)
648
- // значение в projectionObj обновится автоматически за O(1).
634
+ // Bind the leaf to the POJO mirror via nodeSlot. Subsequent
635
+ // updateValuesCacheEntry(node, newValue) calls update the
636
+ // projectionObj value automatically in O(1).
649
637
  if (projectionObj !== undefined) {
650
638
  this.values.nodeSlot.set(childObj, { parent: projectionObj, key });
651
639
  projectionObj[key] = leaf.value;
@@ -653,24 +641,22 @@ export class Palistor {
653
641
  changed.add(childObj);
654
642
  }
655
643
  else {
656
- // Leaf уже зарегистрированпроверяем, изменилось ли значение.
657
- // Это происходит при повторном upsert (обновление entity с сервера).
644
+ // Leaf already registeredcheck whether the value changed.
645
+ // Happens on repeated upserts (entity refresh from the server).
658
646
  const state = this.nodes.nodeState.get(childObj);
659
647
  if (state.value !== leaf.value) {
660
- // Обновляем nodeState напрямую (без writePipeline это raw-запись)
648
+ // Raw write to nodeState (bypasses writePipeline on purpose)
661
649
  state.value = leaf.value;
662
- // Обновить projectionObj через nodeSlot: O(1), не нужно искать POJO.
663
650
  updateValuesCacheEntry(this.values, childObj, leaf.value);
664
651
  changed.add(childObj);
665
652
  }
666
- // Если значение не изменилосьleaf не попадёт в changed,
667
- // подписчики не будут уведомлены (оптимизация).
653
+ // Unchanged values stay out of `changed` subscribers are not notified.
668
654
  }
669
655
  }
670
656
  else {
671
- // ── Групповой узел (EntityGroupNode): вложенный объект без "value" ──
672
- // Например: address: { city: { value: "Moscow" } }
673
- // Создаём вложенный POJO в projectionObj (если нужно) и рекурсируем.
657
+ // ── Group node (EntityGroupNode): nested object without "value" ──
658
+ // e.g. address: { city: { value: "Moscow" } }
659
+ // Create a nested POJO in projectionObj (if needed) and recurse.
674
660
  let nestedProjectionObj;
675
661
  if (projectionObj !== undefined) {
676
662
  if (!projectionObj[key] || typeof projectionObj[key] !== "object") {
@@ -683,8 +669,8 @@ export class Palistor {
683
669
  }
684
670
  }
685
671
  /**
686
- * Рекурсивно собрать все leaf-ноды из entity node tree.
687
- * Используется в `delete()` для очистки NodeRegistry.
672
+ * Recursively collect all leaf nodes from an entity node tree.
673
+ * Used by `delete()` to clean up NodeRegistry.
688
674
  */
689
675
  collectEntityLeaves(node, result) {
690
676
  for (const key of Object.keys(node)) {
@@ -692,11 +678,9 @@ export class Palistor {
692
678
  if (!child || typeof child !== "object")
693
679
  continue;
694
680
  if (isLeafNode(child)) {
695
- // Leaf-нода: объект с полем "value" → добавить в результат
696
681
  result.add(child);
697
682
  }
698
683
  else {
699
- // Группа — рекурсивно обойти вложенные узлы
700
684
  this.collectEntityLeaves(child, result);
701
685
  }
702
686
  }
@@ -710,37 +694,34 @@ export class Palistor {
710
694
  */
711
695
  collectEntityTemplateErrors(templateNode, entityNode, errors, parentPath) {
712
696
  const translate = this.services.translate;
713
- // Построить plain-объект значений entity передаётся вторым аргументом
714
- // в каждый validate(), чтобы валидатор мог проверять зависимости между полями.
715
- // Пример: validate: (v, vals) => vals.password !== vals.confirmPassword ? "Mismatch" : undefined
697
+ // Plain values object passed as the second argument to every validate(),
698
+ // so validators can check cross-field dependencies, e.g.:
699
+ // validate: (v, vals) => vals.password !== vals.confirmPassword ? "Mismatch" : undefined
716
700
  const entityValues = this.buildEntityValuesForTemplate(entityNode);
717
701
  for (const key of configKeys(templateNode)) {
718
702
  const templateField = templateNode[key];
719
703
  if (!templateField || typeof templateField !== "object")
720
704
  continue;
721
- // Формируем dot-path для сообщения об ошибке (e.g. "address.city")
705
+ // Dot-path for the error message (e.g. "address.city")
722
706
  const path = parentPath ? `${parentPath}.${key}` : key;
723
707
  if (isLeafNode(templateField)) {
724
- // Leaf-поле template — проверяем, есть ли validate() функция
725
708
  if (typeof templateField.validate === "function") {
726
- // Извлечь текущее значение из entity. Приоритет:
727
- // 1. nodeState (актуальное значение после write pipeline)
728
- // 2. fallback на entityField.value (если node ещё не зарегистрирован)
709
+ // Current value: nodeState first (post write-pipeline), falling back
710
+ // to entityField.value when the node is not registered yet.
729
711
  const entityField = entityNode[key];
730
712
  const currentValue = entityField && typeof entityField === "object"
731
713
  ? this.nodes.nodeState.get(entityField)?.value ?? entityField.value
732
714
  : undefined;
733
- // Вызвать validate(currentValue, allEntityValues, translateFn).
734
- // Если возвращает строку это сообщение об ошибке.
735
- // undefined / false = поле валидно.
715
+ // validate(currentValue, allEntityValues, translateFn):
716
+ // a string is an error message; undefined/false means valid.
736
717
  const result = templateField.validate(currentValue, entityValues, translate);
737
718
  if (result)
738
719
  errors.push({ path, message: result });
739
720
  }
740
721
  }
741
722
  else {
742
- // Группа templateрекурсия в соответствующий вложенный узел entity.
743
- // Template и entity должны иметь одинаковую структуру вложенности.
723
+ // Template grouprecurse into the matching nested entity node.
724
+ // Template and entity share the same nesting structure.
744
725
  const entityField = entityNode[key];
745
726
  if (entityField && typeof entityField === "object") {
746
727
  this.collectEntityTemplateErrors(templateField, entityField, errors, path);
@@ -755,19 +736,18 @@ export class Palistor {
755
736
  * @internal
756
737
  */
757
738
  buildEntityValuesForTemplate(entityNode) {
758
- // Результат: plain-объект вида { id: "u1", name: "Alice", address: { city: "Moscow" } }
739
+ // Result shape: { id: "u1", name: "Alice", address: { city: "Moscow" } }
759
740
  const values = {};
760
741
  for (const key of Object.keys(entityNode)) {
761
742
  const field = entityNode[key];
762
743
  if (field && typeof field === "object") {
763
744
  if (isLeafNode(field)) {
764
- // Leaf: читаем value из nodeState (актуальное), fallback на field.value
745
+ // Leaf: read value from nodeState (current), fall back to field.value
765
746
  values[key] =
766
747
  this.nodes.nodeState.get(field)?.value ??
767
748
  field.value;
768
749
  }
769
750
  else {
770
- // Группа: рекурсивно собрать вложенный объект
771
751
  values[key] = this.buildEntityValuesForTemplate(field);
772
752
  }
773
753
  }