@reformer/core 11.0.0-beta.8 → 11.0.0-beta.9

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 (224) hide show
  1. package/README.md +91 -73
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +370 -242
  4. package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
  5. package/dist/date-utils-90-qZbse.js +43 -0
  6. package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
  7. package/dist/form/aggregate-signals.d.ts +21 -63
  8. package/dist/form/behaviors/collections.d.ts +28 -7
  9. package/dist/form/behaviors/context.d.ts +19 -2
  10. package/dist/form/behaviors/index.d.ts +3 -3
  11. package/dist/form/behaviors/internals.d.ts +9 -2
  12. package/dist/form/behaviors/node.d.ts +25 -2
  13. package/dist/form/behaviors/operators.d.ts +17 -9
  14. package/dist/form/behaviors/schema-operators.d.ts +6 -4
  15. package/dist/form/behaviors/types.d.ts +12 -4
  16. package/dist/form/create-form.d.ts +47 -35
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +45 -28
  19. package/dist/form/form-config-base.d.ts +46 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +16 -21
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +30 -14
  37. package/dist/form/signal-node-registry.d.ts +6 -7
  38. package/dist/form/submission.d.ts +93 -0
  39. package/dist/form/type-guards.d.ts +14 -73
  40. package/dist/form/types/contracts.d.ts +19 -92
  41. package/dist/form/types/field-config.d.ts +27 -0
  42. package/dist/form/types/index.d.ts +5 -57
  43. package/dist/form/types/nodes.d.ts +159 -0
  44. package/dist/form/types/schema-node.d.ts +139 -63
  45. package/dist/form/types/validation-schema.d.ts +23 -27
  46. package/dist/form/validation/context.d.ts +42 -7
  47. package/dist/form/validation/index.d.ts +9 -8
  48. package/dist/form/validation/operators.d.ts +22 -30
  49. package/dist/form/validation/run.d.ts +93 -18
  50. package/dist/form/validation/strategy.d.ts +41 -13
  51. package/dist/form/validation/types.d.ts +74 -20
  52. package/dist/form/validators/date-utils.d.ts +13 -1
  53. package/dist/form/validators/email.d.ts +8 -7
  54. package/dist/form/validators/file-type.d.ts +8 -7
  55. package/dist/form/validators/file-utils.d.ts +2 -1
  56. package/dist/form/validators/future-date.d.ts +8 -7
  57. package/dist/form/validators/index.d.ts +1 -1
  58. package/dist/form/validators/integer.d.ts +8 -7
  59. package/dist/form/validators/is-date.d.ts +11 -9
  60. package/dist/form/validators/is-number.d.ts +11 -9
  61. package/dist/form/validators/max-age.d.ts +13 -9
  62. package/dist/form/validators/max-date.d.ts +8 -7
  63. package/dist/form/validators/max-file-size.d.ts +8 -7
  64. package/dist/form/validators/max-files.d.ts +8 -7
  65. package/dist/form/validators/max-length.d.ts +14 -7
  66. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  67. package/dist/form/validators/max.d.ts +10 -8
  68. package/dist/form/validators/min-age.d.ts +12 -9
  69. package/dist/form/validators/min-date.d.ts +8 -7
  70. package/dist/form/validators/min-file-size.d.ts +8 -7
  71. package/dist/form/validators/min-files.d.ts +11 -9
  72. package/dist/form/validators/min-length.d.ts +14 -7
  73. package/dist/form/validators/min.d.ts +10 -8
  74. package/dist/form/validators/multiple-of.d.ts +10 -8
  75. package/dist/form/validators/non-negative.d.ts +8 -7
  76. package/dist/form/validators/non-zero.d.ts +8 -7
  77. package/dist/form/validators/past-date.d.ts +8 -7
  78. package/dist/form/validators/pattern.d.ts +9 -8
  79. package/dist/form/validators/phone.d.ts +8 -7
  80. package/dist/form/validators/required.d.ts +11 -9
  81. package/dist/form/validators/url.d.ts +20 -13
  82. package/dist/form/validators/validation-issue.d.ts +8 -0
  83. package/dist/i18n/descriptor.d.ts +1 -1
  84. package/dist/i18n/en.json.d.ts +5 -1
  85. package/dist/i18n/locale-store.d.ts +7 -0
  86. package/dist/i18n/message-format.d.ts +24 -4
  87. package/dist/i18n/ru.json.d.ts +5 -1
  88. package/dist/i18n/translator.d.ts +11 -0
  89. package/dist/i18n/validation-message.d.ts +42 -7
  90. package/dist/i18n.d.ts +7 -12
  91. package/dist/i18n.js +66 -283
  92. package/dist/index.d.ts +16 -7
  93. package/dist/index.js +924 -2855
  94. package/dist/loader-BWkigpVe.js +52 -0
  95. package/dist/locale/en.js +1 -1
  96. package/dist/locale/en.json +4 -1
  97. package/dist/locale/ru.js +4 -1
  98. package/dist/locale/ru.json +4 -1
  99. package/dist/locale-CG54AU2Q.js +219 -0
  100. package/dist/locale.js +1 -1
  101. package/dist/model/behaviors-value.d.ts +24 -6
  102. package/dist/model/create-model.d.ts +2 -2
  103. package/dist/model/derived-registry.d.ts +1 -1
  104. package/dist/model/index.d.ts +11 -5
  105. package/dist/model/model-nodes.d.ts +79 -12
  106. package/dist/model/model-signals-proxy.d.ts +5 -6
  107. package/dist/model/model-value-proxy.d.ts +20 -15
  108. package/dist/model/safe-effect.d.ts +33 -1
  109. package/dist/model/types.d.ts +63 -13
  110. package/dist/model.js +20 -18
  111. package/dist/node-kind-C-r6ZMdo.js +15 -0
  112. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  113. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  114. package/dist/platforms/react/form-bundle-context.d.ts +2 -2
  115. package/dist/platforms/react/hooks/types.d.ts +57 -38
  116. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  117. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  118. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  119. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  120. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  121. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  122. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  123. package/dist/platforms/react/i18n/context.d.ts +6 -1
  124. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  125. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  126. package/dist/platforms/react/index.d.ts +6 -4
  127. package/dist/react.d.ts +22 -0
  128. package/dist/react.js +505 -0
  129. package/dist/runtime-token.d.ts +1 -2
  130. package/dist/safe-effect-BbtX3KuN.js +643 -0
  131. package/dist/schema-controller-CpiO2JoK.js +143 -0
  132. package/dist/signals.d.ts +15 -9
  133. package/dist/signals.js +11 -6
  134. package/dist/strategy-CoFSemuP.js +375 -0
  135. package/dist/type-guards-D-N_5Ddh.js +19 -0
  136. package/dist/validation-issue-B9mjI8jz.js +7 -0
  137. package/dist/validation-message-Ced9MGxs.js +194 -0
  138. package/dist/validation.js +9 -9
  139. package/dist/validators/email.js +5 -5
  140. package/dist/validators/future-date.js +8 -8
  141. package/dist/validators/integer.js +3 -3
  142. package/dist/validators/is-date.js +4 -4
  143. package/dist/validators/is-number.js +4 -4
  144. package/dist/validators/max-age.js +8 -9
  145. package/dist/validators/max-date.js +8 -8
  146. package/dist/validators/max-length.js +7 -7
  147. package/dist/validators/max.js +4 -4
  148. package/dist/validators/min-age.js +10 -8
  149. package/dist/validators/min-date.js +3 -3
  150. package/dist/validators/min-length.js +5 -5
  151. package/dist/validators/min.js +4 -4
  152. package/dist/validators/multiple-of.js +11 -7
  153. package/dist/validators/non-negative.js +3 -3
  154. package/dist/validators/non-zero.js +3 -3
  155. package/dist/validators/past-date.js +8 -8
  156. package/dist/validators/pattern.js +5 -5
  157. package/dist/validators/phone.js +11 -9
  158. package/dist/validators/required.js +2 -2
  159. package/dist/validators/url.js +48 -12
  160. package/dist/validators.js +17 -17
  161. package/docs/llms/01-api-reference.md +60 -34
  162. package/docs/llms/02-quick-start.md +111 -80
  163. package/docs/llms/03-api-signatures.md +368 -153
  164. package/docs/llms/04-common-patterns.md +58 -56
  165. package/docs/llms/05-common-mistakes.md +69 -44
  166. package/docs/llms/06-troubleshooting.md +14 -9
  167. package/docs/llms/07-complete-import.md +74 -43
  168. package/docs/llms/08-form-types.md +11 -4
  169. package/docs/llms/09-formschema.md +129 -81
  170. package/docs/llms/10-arrays.md +106 -75
  171. package/docs/llms/11-async-watchfield.md +2 -2
  172. package/docs/llms/13-multi-step.md +134 -63
  173. package/docs/llms/14-extended-mistakes.md +20 -17
  174. package/docs/llms/15-project-structure.md +221 -74
  175. package/docs/llms/16-ui-components.md +55 -42
  176. package/docs/llms/17-nonexistent-api.md +199 -32
  177. package/docs/llms/18-conditional-fields.md +66 -32
  178. package/docs/llms/19-reading-values.md +15 -3
  179. package/docs/llms/20-compute-vs-watch.md +16 -10
  180. package/docs/llms/21-array-operations.md +51 -27
  181. package/docs/llms/22-cycle-detection.md +1 -1
  182. package/docs/llms/23-copy-from.md +2 -2
  183. package/docs/llms/24-sync-fields.md +6 -5
  184. package/docs/llms/25-reset-when.md +14 -7
  185. package/docs/llms/26-transform-value.md +4 -4
  186. package/docs/llms/27-revalidate-when.md +16 -15
  187. package/docs/llms/28-submit-and-reset.md +276 -128
  188. package/docs/llms/29-async-preload.md +39 -29
  189. package/docs/llms/30-type-safety-recipes.md +105 -51
  190. package/docs/llms/31-async-validator-debounce.md +96 -53
  191. package/docs/llms/33-validation-strategy.md +177 -65
  192. package/docs/llms/34-i18n.md +69 -43
  193. package/docs/llms/35-unified-contract.md +100 -39
  194. package/docs/llms/36-issue-messages.md +284 -0
  195. package/docs/llms/37-compiled-schema.md +332 -0
  196. package/docs/llms/38-form-flow.md +397 -0
  197. package/llms-index.json +1 -1
  198. package/llms.txt +6084 -3420
  199. package/package.json +23 -4
  200. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  201. package/dist/date-utils-dTT_x_Be.js +0 -31
  202. package/dist/form/create-core-form.d.ts +0 -72
  203. package/dist/form/factories/index.d.ts +0 -6
  204. package/dist/form/factories/node-factory.d.ts +0 -189
  205. package/dist/form/form-submitter.d.ts +0 -121
  206. package/dist/form/nodes/array-node.d.ts +0 -276
  207. package/dist/form/nodes/field-node.d.ts +0 -297
  208. package/dist/form/nodes/form-node.d.ts +0 -326
  209. package/dist/form/nodes/group-node.d.ts +0 -284
  210. package/dist/form/nodes/model-array-node.d.ts +0 -126
  211. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  212. package/dist/form/status-machine.d.ts +0 -115
  213. package/dist/form/types/deep-schema.d.ts +0 -107
  214. package/dist/form/types/form-proxy.d.ts +0 -141
  215. package/dist/form/unique-id.d.ts +0 -45
  216. package/dist/form/validation/config.d.ts +0 -73
  217. package/dist/form/validation/error-handler.d.ts +0 -166
  218. package/dist/form/validators/validation-error.d.ts +0 -8
  219. package/dist/index-BrNkQj1y.js +0 -105
  220. package/dist/loader-Dv_Hl7JC.js +0 -216
  221. package/dist/model-value-proxy-caTmAiUd.js +0 -419
  222. package/dist/node-BeV_lV5i.js +0 -100
  223. package/dist/strategy-DgzELWno.js +0 -204
  224. package/dist/validation-error-B-U3RsV0.js +0 -11
@@ -0,0 +1,52 @@
1
+ import { e as p } from "./locale-CG54AU2Q.js";
2
+ function u(t) {
3
+ const n = t.messages;
4
+ return n !== null && typeof n == "object";
5
+ }
6
+ function y(t) {
7
+ if (t != null) {
8
+ if (typeof t != "object" || Array.isArray(t))
9
+ throw new TypeError("источник локали вернул не объект");
10
+ return u(t) ? t : { messages: t };
11
+ }
12
+ }
13
+ function g(t) {
14
+ const n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), r = (e) => {
15
+ const i = n.get(e);
16
+ if (i !== void 0) return Promise.resolve(i);
17
+ const c = o.get(e);
18
+ if (c !== void 0) return c;
19
+ const l = Promise.all(t.map(async (s) => y(await s(e)))).then((s) => {
20
+ let a = { code: e, messages: {} };
21
+ for (const f of s)
22
+ f !== void 0 && (a = p(a, f));
23
+ return a = { ...a, code: e }, n.set(e, a), a;
24
+ }).finally(() => {
25
+ o.delete(e);
26
+ });
27
+ return o.set(e, l), l;
28
+ };
29
+ return Object.assign(r, {
30
+ peek: (e) => n.get(e),
31
+ preload: r
32
+ });
33
+ }
34
+ function h(t, n) {
35
+ return async (o) => {
36
+ const r = t(o), e = await fetch(r, n);
37
+ if (!e.ok)
38
+ throw new Error(`локаль «${o}»: ${r} ответил ${e.status}`);
39
+ return await e.json();
40
+ };
41
+ }
42
+ function d(t) {
43
+ return (n) => {
44
+ const o = n.split("-")[0].toLowerCase(), e = (Object.prototype.hasOwnProperty.call(t, n) ? t[n] : void 0) ?? (Object.prototype.hasOwnProperty.call(t, o) ? t[o] : void 0);
45
+ return e === void 0 ? null : e();
46
+ };
47
+ }
48
+ export {
49
+ d as b,
50
+ g as c,
51
+ h as f
52
+ };
package/dist/locale/en.js CHANGED
@@ -1,4 +1,4 @@
1
- import { m as e } from "../en-CjAqn7h0.js";
1
+ import { m as e } from "../en-HP8XEych.js";
2
2
  const o = { code: "en", weekStartsOn: 0, messages: e };
3
3
  export {
4
4
  o as en
@@ -17,6 +17,8 @@
17
17
  "validation.max": "Must be at most {max}",
18
18
  "validation.minLength": "Enter at least {minLength, plural, one{# character} other{# characters}}",
19
19
  "validation.maxLength": "Enter no more than {maxLength, plural, one{# character} other{# characters}}",
20
+ "validation.minItems": "At least {minLength, plural, one{# item} other{# items}}",
21
+ "validation.maxItems": "No more than {maxLength, plural, one{# item} other{# items}}",
20
22
  "validation.isNumber": "Enter a number",
21
23
  "validation.integer": "Enter a whole number",
22
24
  "validation.multipleOf": "Must be a multiple of {multipleOf}",
@@ -37,6 +39,7 @@
37
39
  "validation.maxTotalFileSize": "The total size of the files exceeds {maxTotalFileSize}",
38
40
  "validation.fileExists": "File {fileName} has already been added",
39
41
  "validation.uploadAborted": "Upload cancelled",
40
- "validation.uploadFailed": "Failed to upload {fileName}"
42
+ "validation.uploadFailed": "Failed to upload {fileName}",
43
+ "validation.ruleFailed": "Could not check this field. Try again"
41
44
  }
42
45
  }
package/dist/locale/ru.js CHANGED
@@ -14,6 +14,8 @@ const a = {
14
14
  "validation.max": "Не больше {max}",
15
15
  "validation.minLength": "Не меньше {minLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
16
16
  "validation.maxLength": "Не больше {maxLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
17
+ "validation.minItems": "Не меньше {minLength, plural, one{# элемента} few{# элементов} many{# элементов} other{# элемента}}",
18
+ "validation.maxItems": "Не больше {maxLength, plural, one{# элемента} few{# элементов} many{# элементов} other{# элемента}}",
17
19
  "validation.isNumber": "Введите число",
18
20
  "validation.integer": "Введите целое число",
19
21
  "validation.multipleOf": "Число должно быть кратно {multipleOf}",
@@ -34,7 +36,8 @@ const a = {
34
36
  "validation.maxTotalFileSize": "Суммарный размер файлов больше {maxTotalFileSize}",
35
37
  "validation.fileExists": "Файл {fileName} уже добавлен",
36
38
  "validation.uploadAborted": "Загрузка прервана",
37
- "validation.uploadFailed": "Не удалось загрузить файл {fileName}"
39
+ "validation.uploadFailed": "Не удалось загрузить файл {fileName}",
40
+ "validation.ruleFailed": "Не удалось проверить поле. Повторите попытку"
38
41
  }, i = a, e = { code: "ru", weekStartsOn: 1, messages: i };
39
42
  export {
40
43
  e as ru
@@ -17,6 +17,8 @@
17
17
  "validation.max": "Не больше {max}",
18
18
  "validation.minLength": "Не меньше {minLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
19
19
  "validation.maxLength": "Не больше {maxLength, plural, one{# символа} few{# символов} many{# символов} other{# символа}}",
20
+ "validation.minItems": "Не меньше {minLength, plural, one{# элемента} few{# элементов} many{# элементов} other{# элемента}}",
21
+ "validation.maxItems": "Не больше {maxLength, plural, one{# элемента} few{# элементов} many{# элементов} other{# элемента}}",
20
22
  "validation.isNumber": "Введите число",
21
23
  "validation.integer": "Введите целое число",
22
24
  "validation.multipleOf": "Число должно быть кратно {multipleOf}",
@@ -37,6 +39,7 @@
37
39
  "validation.maxTotalFileSize": "Суммарный размер файлов больше {maxTotalFileSize}",
38
40
  "validation.fileExists": "Файл {fileName} уже добавлен",
39
41
  "validation.uploadAborted": "Загрузка прервана",
40
- "validation.uploadFailed": "Не удалось загрузить файл {fileName}"
42
+ "validation.uploadFailed": "Не удалось загрузить файл {fileName}",
43
+ "validation.ruleFailed": "Не удалось проверить поле. Повторите попытку"
41
44
  }
42
45
  }
@@ -0,0 +1,219 @@
1
+ class v extends Error {
2
+ name = "MessageSyntaxError";
3
+ }
4
+ const O = (t) => `⟦${t}⟧`, w = 100, L = new RegExp("\\p{Pattern_White_Space}", "u"), _ = /[\p{White_Space}\p{Pattern_Syntax}]/u, $ = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map();
5
+ function E(t) {
6
+ let e = x.get(t);
7
+ if (e === void 0) {
8
+ try {
9
+ Intl.getCanonicalLocales(t), e = t;
10
+ } catch {
11
+ e = "en", process.env.NODE_ENV !== "production" && console.warn(
12
+ `[@reformer/core/i18n] код языка «${t}» не BCP 47 — числа, даты и формы слов форматируются по en.`
13
+ );
14
+ }
15
+ x.set(t, e);
16
+ }
17
+ return e;
18
+ }
19
+ function M(t) {
20
+ try {
21
+ return String(t);
22
+ } catch {
23
+ return "";
24
+ }
25
+ }
26
+ function b(t) {
27
+ let e = 0, n = 0;
28
+ const r = (s) => {
29
+ throw new v(`${s} (позиция ${e}) в сообщении: ${t}`);
30
+ }, i = (s, o) => s === "{" || s === "}" || s === "<" || s === ">" || o && s === "#", l = () => {
31
+ for (; e < t.length && L.test(t[e]); ) e++;
32
+ }, m = () => {
33
+ const s = e;
34
+ for (; e < t.length && !_.test(t[e]); ) e++;
35
+ return e === s && r("ожидалось имя"), t.slice(s, e);
36
+ }, p = (s) => {
37
+ if (s !== "plural" || t[e] !== "=") return m();
38
+ const o = e++;
39
+ (t[e] === "+" || t[e] === "-") && e++;
40
+ const f = e;
41
+ for (; e < t.length && t[e] >= "0" && t[e] <= "9"; ) e++;
42
+ return (e === f || !Number.isSafeInteger(Number(t.slice(f, e)))) && r("после «=» ожидалось целое число"), t.slice(o, e);
43
+ }, c = (s) => {
44
+ if (e++, t[e] === "'")
45
+ return e++, "'";
46
+ if (!i(t[e], s)) return "'";
47
+ let o = "";
48
+ for (; e < t.length; )
49
+ if (t[e] !== "'") o += t[e++];
50
+ else if (t[e + 1] === "'")
51
+ o += "'", e += 2;
52
+ else
53
+ return e++, o;
54
+ return o;
55
+ }, a = (s, o) => {
56
+ const f = [];
57
+ let h = "";
58
+ const g = () => {
59
+ h !== "" && (f.push({ kind: "text", value: h }), h = "");
60
+ };
61
+ for (; e < t.length; ) {
62
+ const d = t[e];
63
+ if (d === "}") {
64
+ s || r("непарная «}» — литерал пишется как '}'");
65
+ break;
66
+ }
67
+ d === "'" ? h += c(o !== null) : d === "#" && o !== null ? (e++, g(), f.push({ kind: "number", name: o })) : d === "{" ? (g(), f.push(N())) : (h += d, e++);
68
+ }
69
+ return g(), f;
70
+ }, u = (s, o, f) => {
71
+ const h = /* @__PURE__ */ new Map();
72
+ for (l(); e < t.length && t[e] !== "}"; ) {
73
+ const g = p(o);
74
+ h.has(g) && r(`ветка «${g}» в «${s}, ${o}» повторяется`), l(), t[e] !== "{" && r(`ожидалась «{» после ветки «${g}»`), e++, h.set(g, a(!0, f)), t[e] !== "}" && r(`ветка «${g}» не закрыта`), e++, l();
75
+ }
76
+ return t[e] !== "}" && r(`«${s}, ${o}» не закрыт «}»`), e++, h.has("other") || r(`в «${s}, ${o}» нет обязательной ветки other`), h;
77
+ }, N = () => {
78
+ n === w && r(`вложенность аргументов глубже ${w} уровней`), n++, e++, l();
79
+ const s = m();
80
+ l();
81
+ let o;
82
+ if (t[e] === "}")
83
+ e++, o = { kind: "arg", name: s };
84
+ else {
85
+ t[e] !== "," && r(`ожидались «,» или «}» после аргумента «${s}»`), e++, l();
86
+ const f = m();
87
+ f !== "plural" && f !== "select" && r(`неизвестный тип «${f}» у «${s}» — поддерживаются plural и select`), l(), t[e] !== "," && r(`ожидалась «,» после «${f}»`), e++, o = {
88
+ kind: f,
89
+ name: s,
90
+ branches: u(s, f, f === "plural" ? s : null)
91
+ };
92
+ }
93
+ return n--, o;
94
+ };
95
+ return a(!1, null);
96
+ }
97
+ function j(t, e, n, r = {}) {
98
+ const i = r.onMissingArgument ?? O, l = (p) => {
99
+ const c = e[p];
100
+ return typeof c == "number" && Number.isFinite(c) ? c : null;
101
+ }, m = (p) => {
102
+ let c = "";
103
+ for (const a of p)
104
+ if (a.kind === "text") c += a.value;
105
+ else if (a.kind === "arg")
106
+ c += e[a.name] === void 0 ? i(a.name) : M(e[a.name]);
107
+ else if (a.kind === "number") {
108
+ const u = l(a.name);
109
+ c += u === null ? i(a.name) : A(n).format(u);
110
+ } else if (a.kind === "plural") {
111
+ const u = l(a.name);
112
+ c += u === null ? i(a.name) : m(
113
+ a.branches.get(`=${u}`) ?? a.branches.get(P(n).select(u)) ?? a.branches.get("other")
114
+ );
115
+ } else {
116
+ const u = e[a.name];
117
+ c += u === void 0 ? i(a.name) : m(a.branches.get(M(u)) ?? a.branches.get("other"));
118
+ }
119
+ return c;
120
+ };
121
+ return m(t);
122
+ }
123
+ function P(t) {
124
+ let e = $.get(t);
125
+ return e === void 0 && $.set(t, e = new Intl.PluralRules(E(t))), e;
126
+ }
127
+ function A(t) {
128
+ let e = y.get(t);
129
+ return e === void 0 && y.set(t, e = new Intl.NumberFormat(E(t))), e;
130
+ }
131
+ function F(t, e = {}) {
132
+ const n = /* @__PURE__ */ new Map();
133
+ return (r, i = {}) => {
134
+ let l = n.get(r);
135
+ return l === void 0 && n.set(r, l = b(r)), j(l, i, t, e);
136
+ };
137
+ }
138
+ const I = Object.freeze({
139
+ code: "en",
140
+ messages: Object.freeze({})
141
+ });
142
+ function C(t, e) {
143
+ const { messages: n, ...r } = e;
144
+ return {
145
+ ...t,
146
+ ...r,
147
+ messages: n === void 0 ? t.messages : { ...t.messages, ...n }
148
+ };
149
+ }
150
+ function S(t, e) {
151
+ return Object.prototype.hasOwnProperty.call(t, e);
152
+ }
153
+ function k(t, e = /* @__PURE__ */ new Set()) {
154
+ for (const n of t)
155
+ if (n.kind !== "text" && (e.add(n.name), n.kind === "plural" || n.kind === "select"))
156
+ for (const r of n.branches.values()) k(r, e);
157
+ return e;
158
+ }
159
+ function D(t, e) {
160
+ const n = [], r = /* @__PURE__ */ new Map();
161
+ for (const [i, l] of Object.entries(t)) {
162
+ if (typeof l != "string") {
163
+ n.push({ key: i, kind: "syntax", message: "сообщение не является строкой" });
164
+ continue;
165
+ }
166
+ try {
167
+ r.set(i, b(l));
168
+ } catch (m) {
169
+ n.push({
170
+ key: i,
171
+ kind: "syntax",
172
+ message: m instanceof Error ? m.message : String(m)
173
+ });
174
+ }
175
+ }
176
+ if (e === void 0) return n;
177
+ for (const i of Object.keys(t))
178
+ S(e, i) || n.push({ key: i, kind: "extra", message: "ключа нет в образце" });
179
+ for (const [i, l] of Object.entries(e)) {
180
+ if (!S(t, i)) {
181
+ n.push({ key: i, kind: "missing", message: "ключ есть в образце, но не переведён" });
182
+ continue;
183
+ }
184
+ const m = r.get(i);
185
+ if (m === void 0) continue;
186
+ let p;
187
+ try {
188
+ if (typeof l != "string") throw new TypeError("сообщение не является строкой");
189
+ p = b(l);
190
+ } catch (u) {
191
+ n.push({
192
+ key: i,
193
+ kind: "syntax",
194
+ message: `в образце: ${u instanceof Error ? u.message : String(u)}`
195
+ });
196
+ continue;
197
+ }
198
+ const c = [...k(p)].sort().join(", "), a = [...k(m)].sort().join(", ");
199
+ c !== a && n.push({
200
+ key: i,
201
+ kind: "arguments",
202
+ message: `образец ждёт {${c}}, словарь — {${a}}`
203
+ });
204
+ }
205
+ return n;
206
+ }
207
+ export {
208
+ I as D,
209
+ v as M,
210
+ F as c,
211
+ C as e,
212
+ j as f,
213
+ S as h,
214
+ E as i,
215
+ k as m,
216
+ b as p,
217
+ M as t,
218
+ D as v
219
+ };
package/dist/locale.js CHANGED
@@ -1,4 +1,4 @@
1
- import { b as e } from "./loader-Dv_Hl7JC.js";
1
+ import { b as e } from "./loader-BWkigpVe.js";
2
2
  const n = ["en", "ru"], t = e({
3
3
  en: () => import("./locale/en.js").then((o) => o.en),
4
4
  ru: () => import("./locale/ru.js").then((o) => o.ru)
@@ -4,6 +4,12 @@ export type BehaviorCleanup = () => void;
4
4
  /**
5
5
  * Вычисляемое поле: `target = fn(...sourceValues)` при изменении источников.
6
6
  *
7
+ * Примитив модели: цель вычисляемой НЕ помечает. Поэтому массовая запись (`model.set`,
8
+ * `model.patch`, `model.reset`) её перезаписывает, а `isDirty()` её учитывает — до следующей смены
9
+ * источников. Оператор поведения `computeFrom` из `@reformer/core/behaviors` цель помечает
10
+ * (`markDerived`) и защищён от циклов: массовая запись такую цель не трогает. В поведении формы
11
+ * нужен оператор; примитив — для модели без формы.
12
+ *
7
13
  * @group Model
8
14
  * @param sources Сигналы-источники (`model.$.a`, `model.$.b`).
9
15
  * @param target Сигнал-цель (`model.$.total`).
@@ -16,7 +22,7 @@ export type BehaviorCleanup = () => void;
16
22
  * computeFrom([model.$.price, model.$.qty], model.$.total, (price, qty) => price * qty);
17
23
  * ```
18
24
  */
19
- export declare function computeFrom<R>(sources: ReadonlySignal<any>[], target: Signal<R>, fn: (...values: any[]) => R, options?: {
25
+ export declare function computeFrom<R>(sources: ReadonlySignal<any>[], target: Signal<R>, fn: (...values: any[]) => NoInfer<R>, options?: {
20
26
  when?: (...values: any[]) => boolean;
21
27
  }): BehaviorCleanup;
22
28
  /**
@@ -59,6 +65,18 @@ export declare function watchField<T>(source: ReadonlySignal<T>, cb: (value: T)
59
65
  * ```
60
66
  */
61
67
  export declare function transformValue<T>(target: Signal<T>, transformer: (value: T) => T): BehaviorCleanup;
68
+ /**
69
+ * Опции {@link resetWhen}. Без `resetValue` поле сбрасывается в `null`, поэтому `resetValue`
70
+ * обязателен, если `null` не входит в тип поля: `resetWhen(model.$.promoCode, cond)` для поля
71
+ * `string` не компилируется — нужно `{ resetValue: '' }`.
72
+ *
73
+ * @group Behaviors
74
+ */
75
+ export type ResetWhenOptionsArg<T> = null extends T ? [options?: {
76
+ resetValue?: NoInfer<T>;
77
+ }] : [options: {
78
+ resetValue: NoInfer<T>;
79
+ }];
62
80
  /**
63
81
  * Сброс значения поля к `resetValue` (по умолчанию `null`), когда `condition` истинно.
64
82
  *
@@ -68,12 +86,12 @@ export declare function transformValue<T>(target: Signal<T>, transformer: (value
68
86
  * resetWhen(model.$.cardNumber, () => model.paymentType !== 'card', { resetValue: '' });
69
87
  * ```
70
88
  */
71
- export declare function resetWhen<T>(target: Signal<T>, condition: () => boolean, options?: {
72
- resetValue?: T;
73
- }): BehaviorCleanup;
89
+ export declare function resetWhen<T>(target: Signal<T>, condition: () => boolean, ...[options]: ResetWhenOptionsArg<T>): BehaviorCleanup;
74
90
  /**
75
91
  * Двусторонняя синхронизация двух полей (опционально с трансформом `a → b`).
76
- * Сходимость обеспечивается peek-guard'ом; флаг предотвращает лишние круги.
92
+ * Сходимость обеспечивается peek-guard'ом; флаг предотвращает лишние круги. При создании источник —
93
+ * `a`: `b` получает его значение (через `transform`), обратной записи при создании нет. Записи в
94
+ * оба поля в одном тике — побеждает последняя.
77
95
  *
78
96
  * @group Model
79
97
  * @example
@@ -81,7 +99,7 @@ export declare function resetWhen<T>(target: Signal<T>, condition: () => boolean
81
99
  * syncFields(model.$.field1, model.$.field2);
82
100
  * ```
83
101
  */
84
- export declare function syncFields<T>(a: Signal<T>, b: Signal<T>, options?: {
102
+ export declare function syncFields<T>(a: Signal<T>, b: Signal<NoInfer<T>>, options?: {
85
103
  transform?: (value: T) => T;
86
104
  }): BehaviorCleanup;
87
105
  /**
@@ -16,7 +16,7 @@ export declare function eachLeafSignal<T>(model: FormModel<T>, visit: (signal: P
16
16
  * Обойти ВСЕ ручки значений модели: листья (включая элементы массивов) И сами массивы.
17
17
  *
18
18
  * Отличие от {@link eachLeafSignal} — узлы-массивы: массив бывает одним значением поля
19
- * (`{ value: model.$.tags, component }`), и нода такого поля привязана к сигналу массива, а не
19
+ * (`{ model: model.$.tags, component }`), и нода такого поля привязана к сигналу массива, а не
20
20
  * к сигналам элементов. Обходчику, которому нужна именно НОДА поля (стратегия `blur` подписывается
21
21
  * на её `touched`), одних листьев мало — он не увидел бы мультивыбор. Так строятся триггеры
22
22
  * `change`/`blur` стратегий валидации (см. `createFormValidation`).
@@ -25,7 +25,7 @@ export declare function eachLeafSignal<T>(model: FormModel<T>, visit: (signal: P
25
25
  */
26
26
  export declare function eachValueSignal<T>(model: FormModel<T>, visit: (signal: PathAwareSignal<unknown>) => void): void;
27
27
  /**
28
- * Создать реактивную модель данных формы (слой M1).
28
+ * Создать реактивную модель данных формы.
29
29
  *
30
30
  * @group Model
31
31
  * @param initial Начальные значения (объект). Определяют форму данных и initial-снимок.
@@ -3,7 +3,7 @@ import { Signal } from '@preact/signals-core';
3
3
  * Пометить сигнал производным (вычисляемым).
4
4
  *
5
5
  * Вызывается операторами {@link computeFrom}/`compute` для их сигнала-цели. После этого
6
- * bulk-сеттеры (`model.set`/`model.patch`, `patchValue`/`setValue`) пропускают это поле, чтобы
6
+ * bulk-сеттеры (`model.set`/`model.patch`, `setValue` группы формы) пропускают это поле, чтобы
7
7
  * значение из payload не затирало вычисляемое. Прикладной код напрямую обычно не вызывает.
8
8
  *
9
9
  * Идемпотентна на уровне флага: повторные вызовы наращивают счётчик ссылок, чтобы каждый оператор,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `@reformer/core/model` — низкоуровневый реактивный субстрат (M1) и barrel модуля `model`.
2
+ * `@reformer/core/model` — низкоуровневый реактивный субстрат и barrel модуля `model`.
3
3
  *
4
4
  * Это «прокси-объект с сигналами» и инструментарий для построения доменных модулей поверх него
5
5
  * (form, table, …). Модуль СТРОГО реактивный: НИЧЕГО связанного с формами/валидацией/схемами здесь
@@ -19,18 +19,24 @@
19
19
  * - **value-операции** (реактивные правила) — `computeFrom`/`copyFrom`/`watchField`/`transformValue`/
20
20
  * `resetWhen`/`syncFields`/`revalidateWhen`;
21
21
  * - **producer-owned флаг** — `markDerived`/`isDerived`/`unmarkDerived`;
22
- * - **утилиты субстрата** — `runOutsideEffect`/`safeCallback`/`safeDebouncedCallback`.
22
+ * - **утилиты субстрата** — `runOutsideEffect`/`safeCallback`/`safeDebouncedCallback` и
23
+ * `deferredWritesSettled` (дождаться отложенных записей текущего тика);
24
+ * - **сравнение значений** — `deepEqual`, на нём стоят `isDirty` и сверка «модель изменилась».
25
+ *
26
+ * Запасной шаблон элемента массива модель принимает от того, кто строит её извне (сборка формы
27
+ * передаёт `initialValue` узла-массива); что это узел схемы, модель не знает.
23
28
  *
24
29
  * @group Model
25
30
  * @module model
26
31
  */
27
32
  export { createModel, eachLeafSignal, eachValueSignal } from './create-model';
28
33
  export { arrayOf } from './model-nodes';
29
- export type { FormModel, ModelArray, ModelArraySignals, ModelGroupSignals, ModelObject, ModelValue, ModelSignals, ModelApi, PathAwareSignal, } from './types';
34
+ export type { DeepPartial, FormModel, ModelArray, ModelArraySignals, ModelGroupSignals, ModelObject, ModelValue, ModelSignals, ModelApi, PathAwareSignal, } from './types';
30
35
  export { isModelContainerSignal, isValueSignal } from './model-signals-proxy';
31
36
  export { modelOf } from './model-value-proxy';
32
37
  export type { ModelOf } from './model-value-proxy';
33
38
  export { computeFrom, copyFrom, watchField, transformValue, resetWhen, syncFields, revalidateWhen, } from './behaviors-value';
34
- export type { BehaviorCleanup } from './behaviors-value';
39
+ export type { BehaviorCleanup, ResetWhenOptionsArg } from './behaviors-value';
35
40
  export { markDerived, isDerived, unmarkDerived } from './derived-registry';
36
- export { runOutsideEffect, safeCallback, safeDebouncedCallback } from './safe-effect';
41
+ export { runOutsideEffect, safeCallback, safeDebouncedCallback, deferredWritesSettled, } from './safe-effect';
42
+ export { deepEqual } from './model-nodes';
@@ -1,6 +1,19 @@
1
1
  import { Signal } from '@preact/signals-core';
2
2
  import { PathAwareSignal } from './types';
3
3
  export declare const isIndexKey: (key: string) => boolean;
4
+ /**
5
+ * Глубокое сравнение значений модели: объекты и массивы — по содержимому, даты — по времени,
6
+ * `NaN` равен себе, остальное (включая файлы) — по ссылке.
7
+ *
8
+ * То же сравнение стоит за `isDirty` модели и за сверкой «модель изменилась, пока шла проверка»
9
+ * при отправке и переходе шага: значение, изменённое и возвращённое, изменением не считается.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * deepEqual({ date: new Date(0), tags: ['a'] }, { date: new Date(0), tags: ['a'] }); // true
14
+ * ```
15
+ */
16
+ export declare const deepEqual: (a: unknown, b: unknown) => boolean;
4
17
  /** Фабрика нового элемента массива. */
5
18
  type Blank = () => unknown;
6
19
  /**
@@ -12,7 +25,8 @@ type Shape = () => unknown;
12
25
  * Массив модели с шаблоном нового элемента.
13
26
  *
14
27
  * Шаблон — фабрика значения, которое получает `push()` / `insertAt(i)` без аргумента: кнопка
15
- * «Добавить» секции массива, `model.items.push()` в поведении. Объявляется там же, где остальные
28
+ * «Добавить» секции массива, `model.items.push()` в поведении. Частичное значение
29
+ * (`push({ type: 'house' })`) дополняется из шаблона. Объявляется там же, где остальные
16
30
  * начальные значения, — в модели; схеме и JSX знать его не нужно.
17
31
  *
18
32
  * Возвращает обычный массив: тип поля остаётся `U[]`. Шаблон переживает `set` / `patch` / `reset`
@@ -39,14 +53,18 @@ type Shape = () => unknown;
39
53
  *
40
54
  * model.coBorrowers.push(); // { name: '', phones: [] }
41
55
  * model.coBorrowers.at(0).phones.push(); // { number: '' }
42
- * model.coBorrowers.push({ name: 'Анна', phones: [] }); // значение целиком — как раньше
56
+ * model.coBorrowers.push({ name: 'Анна' }); // { name: 'Анна', phones: [] } — остальное из шаблона
43
57
  * ```
44
58
  *
45
59
  * @group Model
46
60
  */
47
61
  export declare function arrayOf<U>(blank: () => U, items?: readonly U[]): U[];
48
- export type ModelNode = LeafNode | GroupNode | ArrayNode;
49
- declare class LeafNode {
62
+ export type ModelNode = ModelLeafNode | ModelGroupNode | ModelArrayNode;
63
+ /** Лист модели, чей это сигнал; `undefined` — сигнал не из модели. @internal */
64
+ export declare function leafOfSignal(handle: object): {
65
+ reference(): unknown;
66
+ } | undefined;
67
+ declare class ModelLeafNode {
50
68
  path: string;
51
69
  readonly kind: "leaf";
52
70
  readonly signal: PathAwareSignal<unknown>;
@@ -58,29 +76,63 @@ declare class LeafNode {
58
76
  /** Нереактивный снимок. */
59
77
  peek(): unknown;
60
78
  set(value: unknown): void;
79
+ /**
80
+ * Вернуть значение к снимку. Производное поле (цель `compute`, поле сервера с `markDerived`)
81
+ * сброс не пишет — как `set` и `patch`: значением владеет поведение, а снимок такого поля снят
82
+ * до первого расчёта, и `compute` пересчитал бы его только при смене источников.
83
+ */
61
84
  resetToInitial(): void;
62
85
  captureInitial(): void;
86
+ /** Значение точки отсчёта — копия: правка полученного снимок не трогает. */
87
+ reference(): unknown;
63
88
  dirty(): boolean;
89
+ /**
90
+ * Отличается ли значение от снимка. Производное поле отличием не считается: его пишет поведение,
91
+ * а не пользователь — иначе форма без единой правки была бы «изменённой».
92
+ */
93
+ differsFrom(snapshot: unknown): boolean;
64
94
  }
65
- export declare class GroupNode {
95
+ export declare class ModelGroupNode {
66
96
  path: string;
67
97
  readonly kind: "group";
68
98
  readonly children: Map<string, ModelNode>;
69
99
  /**
70
100
  * @param shape Образец той же группы из шаблона родительского массива: из него вложенные
71
- * массивы берут шаблоны, если значение пришло обычными данными (см. {@link ArrayNode}).
101
+ * массивы берут шаблоны, если значение пришло обычными данными (см. {@link ModelArrayNode}).
72
102
  */
73
103
  constructor(initial: Record<string, unknown>, path: string, shape?: Shape);
74
104
  rebase(path: string): void;
75
105
  /** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
76
106
  read(): Record<string, unknown>;
77
107
  peek(): Record<string, unknown>;
108
+ /**
109
+ * Полная установка: значение ожидается целиком. Ключи, которых в нём нет, остаются прежними —
110
+ * как у {@link patch}, — но в dev о них сообщает предупреждение: неполный объект в `set` — почти
111
+ * всегда ошибка вызывающего кода, а не намерение.
112
+ */
78
113
  set(value: unknown): void;
114
+ /**
115
+ * Частичное обновление: записываются только переданные ключи. Вложенная группа сливается так
116
+ * же, массив заменяется целиком.
117
+ */
118
+ patch(value: unknown): void;
119
+ /**
120
+ * Запись значения по ключам группы. Лишние ключи значения игнорируются, производные поля не
121
+ * затираются.
122
+ *
123
+ * @param missing - Куда складывать пути ключей, для которых значение не пришло. Передаётся при
124
+ * полной установке ({@link set}); при частичной ({@link patch}) — `undefined`.
125
+ * @param partial - Частичное обновление: ключ со значением `undefined` — «не трогать», как в
126
+ * частичном значении элемента массива (`DeepPartial` допускает `{ ключ: undefined }`).
127
+ */
128
+ private write;
79
129
  resetToInitial(): void;
80
130
  captureInitial(): void;
81
131
  dirty(): boolean;
132
+ /** Отличается ли группа от снимка её значения (строка массива сверяется со снимком массива). */
133
+ differsFrom(snapshot: unknown): boolean;
82
134
  }
83
- export declare class ArrayNode {
135
+ export declare class ModelArrayNode {
84
136
  path: string;
85
137
  private readonly shape?;
86
138
  readonly kind: "array";
@@ -89,18 +141,26 @@ export declare class ArrayNode {
89
141
  private blank;
90
142
  private fallback;
91
143
  private sample;
92
- /** @param shape Образец того же массива из шаблона родителя (см. {@link GroupNode}). */
144
+ /** @param shape Образец того же массива из шаблона родителя (см. {@link ModelGroupNode}). */
93
145
  constructor(initial: unknown[], path: string, shape?: Shape | undefined);
94
- /** Шаблон по старшинству: свой (`arrayOf`) → из шаблона родителя → из схемы. */
146
+ /** Шаблон по старшинству: свой (`arrayOf`) → из шаблона родителя → запасной извне. */
95
147
  private template;
96
148
  private readonly itemShape;
97
149
  private build;
98
- /** Значение нового элемента: переданное либо из шаблона. */
150
+ /**
151
+ * Значение нового элемента: из шаблона; переданный объект накладывается поверх шаблона —
152
+ * недостающие ключи берутся из шаблона, поэтому `push({ type: 'house' })` даёт полный элемент.
153
+ */
99
154
  private fresh;
100
- /** Шаблон из схемы (`initialValue` узла-массива) — запасной: шаблон модели главнее. */
155
+ /** Запасной шаблон извне (сборка формы передаёт `initialValue`): шаблон модели главнее. */
101
156
  provideBlank(blank: Blank): void;
102
157
  rebase(path: string): void;
103
158
  private reindex;
159
+ /**
160
+ * Индекс за концом массива — ошибка, а не «в конец»: `splice` молча сдвигал бы элемент на
161
+ * другой индекс, и вызывающий код получал бы путь, которого не просил.
162
+ */
163
+ private beyondEnd;
104
164
  push(value?: unknown): void;
105
165
  insertAt(index: number, value?: unknown): void;
106
166
  removeAt(index: number): void;
@@ -116,8 +176,15 @@ export declare class ArrayNode {
116
176
  set(value: unknown): void;
117
177
  resetToInitial(): void;
118
178
  captureInitial(): void;
179
+ /** Элементы точки отсчёта — копия: правка полученного снимок не трогает. */
180
+ reference(): unknown[];
119
181
  dirty(): boolean;
182
+ /**
183
+ * Отличается ли массив от снимка: другая длина либо отличается элемент. Сверка идёт по узлам, а
184
+ * не по значению целиком — производные поля строк (цели `compute` поведения строки) не в счёт.
185
+ */
186
+ differsFrom(snapshot: unknown): boolean;
120
187
  }
121
- /** Значение `initialValue` узла-массива схемы как шаблон: фабрика — как есть, значение — копией. */
188
+ /** Начальное значение элемента как шаблон: фабрика — как есть, значение — копией. */
122
189
  export declare const blankFrom: (initialValue: unknown) => Blank;
123
190
  export {};