@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.
- package/README.md +91 -73
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +370 -242
- package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +28 -7
- package/dist/form/behaviors/context.d.ts +19 -2
- package/dist/form/behaviors/index.d.ts +3 -3
- package/dist/form/behaviors/internals.d.ts +9 -2
- package/dist/form/behaviors/node.d.ts +25 -2
- package/dist/form/behaviors/operators.d.ts +17 -9
- package/dist/form/behaviors/schema-operators.d.ts +6 -4
- package/dist/form/behaviors/types.d.ts +12 -4
- package/dist/form/create-form.d.ts +47 -35
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +45 -28
- package/dist/form/form-config-base.d.ts +46 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +16 -21
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +30 -14
- package/dist/form/signal-node-registry.d.ts +6 -7
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +19 -92
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +139 -63
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +42 -7
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +22 -30
- package/dist/form/validation/run.d.ts +93 -18
- package/dist/form/validation/strategy.d.ts +41 -13
- package/dist/form/validation/types.d.ts +74 -20
- package/dist/form/validators/date-utils.d.ts +13 -1
- package/dist/form/validators/email.d.ts +8 -7
- package/dist/form/validators/file-type.d.ts +8 -7
- package/dist/form/validators/file-utils.d.ts +2 -1
- package/dist/form/validators/future-date.d.ts +8 -7
- package/dist/form/validators/index.d.ts +1 -1
- package/dist/form/validators/integer.d.ts +8 -7
- package/dist/form/validators/is-date.d.ts +11 -9
- package/dist/form/validators/is-number.d.ts +11 -9
- package/dist/form/validators/max-age.d.ts +13 -9
- package/dist/form/validators/max-date.d.ts +8 -7
- package/dist/form/validators/max-file-size.d.ts +8 -7
- package/dist/form/validators/max-files.d.ts +8 -7
- package/dist/form/validators/max-length.d.ts +14 -7
- package/dist/form/validators/max-total-file-size.d.ts +8 -7
- package/dist/form/validators/max.d.ts +10 -8
- package/dist/form/validators/min-age.d.ts +12 -9
- package/dist/form/validators/min-date.d.ts +8 -7
- package/dist/form/validators/min-file-size.d.ts +8 -7
- package/dist/form/validators/min-files.d.ts +11 -9
- package/dist/form/validators/min-length.d.ts +14 -7
- package/dist/form/validators/min.d.ts +10 -8
- package/dist/form/validators/multiple-of.d.ts +10 -8
- package/dist/form/validators/non-negative.d.ts +8 -7
- package/dist/form/validators/non-zero.d.ts +8 -7
- package/dist/form/validators/past-date.d.ts +8 -7
- package/dist/form/validators/pattern.d.ts +9 -8
- package/dist/form/validators/phone.d.ts +8 -7
- package/dist/form/validators/required.d.ts +11 -9
- package/dist/form/validators/url.d.ts +20 -13
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +1 -1
- package/dist/i18n/en.json.d.ts +5 -1
- package/dist/i18n/locale-store.d.ts +7 -0
- package/dist/i18n/message-format.d.ts +24 -4
- package/dist/i18n/ru.json.d.ts +5 -1
- package/dist/i18n/translator.d.ts +11 -0
- package/dist/i18n/validation-message.d.ts +42 -7
- package/dist/i18n.d.ts +7 -12
- package/dist/i18n.js +66 -283
- package/dist/index.d.ts +16 -7
- package/dist/index.js +924 -2855
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.js +1 -1
- package/dist/locale/en.json +4 -1
- package/dist/locale/ru.js +4 -1
- package/dist/locale/ru.json +4 -1
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.js +1 -1
- package/dist/model/behaviors-value.d.ts +24 -6
- package/dist/model/create-model.d.ts +2 -2
- package/dist/model/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +11 -5
- package/dist/model/model-nodes.d.ts +79 -12
- package/dist/model/model-signals-proxy.d.ts +5 -6
- package/dist/model/model-value-proxy.d.ts +20 -15
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +63 -13
- package/dist/model.js +20 -18
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
- package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
- package/dist/platforms/react/form-bundle-context.d.ts +2 -2
- package/dist/platforms/react/hooks/types.d.ts +57 -38
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
- package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
- package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
- package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +6 -1
- package/dist/platforms/react/i18n/hooks.d.ts +16 -10
- package/dist/platforms/react/i18n/provider.d.ts +9 -2
- package/dist/platforms/react/index.d.ts +6 -4
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BbtX3KuN.js +643 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-CoFSemuP.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.js +9 -9
- package/dist/validators/email.js +5 -5
- package/dist/validators/future-date.js +8 -8
- package/dist/validators/integer.js +3 -3
- package/dist/validators/is-date.js +4 -4
- package/dist/validators/is-number.js +4 -4
- package/dist/validators/max-age.js +8 -9
- package/dist/validators/max-date.js +8 -8
- package/dist/validators/max-length.js +7 -7
- package/dist/validators/max.js +4 -4
- package/dist/validators/min-age.js +10 -8
- package/dist/validators/min-date.js +3 -3
- package/dist/validators/min-length.js +5 -5
- package/dist/validators/min.js +4 -4
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/non-negative.js +3 -3
- package/dist/validators/non-zero.js +3 -3
- package/dist/validators/past-date.js +8 -8
- package/dist/validators/pattern.js +5 -5
- package/dist/validators/phone.js +11 -9
- package/dist/validators/required.js +2 -2
- package/dist/validators/url.js +48 -12
- package/dist/validators.js +17 -17
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +111 -80
- package/docs/llms/03-api-signatures.md +368 -153
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +129 -81
- package/docs/llms/10-arrays.md +106 -75
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +221 -74
- package/docs/llms/16-ui-components.md +55 -42
- package/docs/llms/17-nonexistent-api.md +199 -32
- package/docs/llms/18-conditional-fields.md +66 -32
- package/docs/llms/19-reading-values.md +15 -3
- package/docs/llms/20-compute-vs-watch.md +16 -10
- package/docs/llms/21-array-operations.md +51 -27
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +276 -128
- package/docs/llms/29-async-preload.md +39 -29
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +177 -65
- package/docs/llms/34-i18n.md +69 -43
- package/docs/llms/35-unified-contract.md +100 -39
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6084 -3420
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -72
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -189
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -297
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -126
- package/dist/form/nodes/subscription-manager.d.ts +0 -180
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/config.d.ts +0 -73
- package/dist/form/validation/error-handler.d.ts +0 -166
- package/dist/form/validators/validation-error.d.ts +0 -8
- package/dist/index-BrNkQj1y.js +0 -105
- package/dist/loader-Dv_Hl7JC.js +0 -216
- package/dist/model-value-proxy-caTmAiUd.js +0 -419
- package/dist/node-BeV_lV5i.js +0 -100
- package/dist/strategy-DgzELWno.js +0 -204
- 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
package/dist/locale/en.json
CHANGED
|
@@ -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
|
package/dist/locale/ru.json
CHANGED
|
@@ -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
|
@@ -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
|
|
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
|
|
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
|
-
* (`{
|
|
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
|
-
* Создать реактивную модель данных
|
|
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`, `
|
|
6
|
+
* bulk-сеттеры (`model.set`/`model.patch`, `setValue` группы формы) пропускают это поле, чтобы
|
|
7
7
|
* значение из payload не затирало вычисляемое. Прикладной код напрямую обычно не вызывает.
|
|
8
8
|
*
|
|
9
9
|
* Идемпотентна на уровне флага: повторные вызовы наращивают счётчик ссылок, чтобы каждый оператор,
|
package/dist/model/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@reformer/core/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 =
|
|
49
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
188
|
+
/** Начальное значение элемента как шаблон: фабрика — как есть, значение — копией. */
|
|
122
189
|
export declare const blankFrom: (initialValue: unknown) => Blank;
|
|
123
190
|
export {};
|