@sectile/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion.d.ts +0 -1
- package/dist/adapter-runtime.d.ts +53 -2
- package/dist/adapter-runtime.js +224 -25
- package/dist/adapter-runtime.js.map +1 -1
- package/dist/alert-dialog.d.ts +0 -1
- package/dist/carousel.d.ts +0 -1
- package/dist/cascade-list.d.ts +16 -0
- package/dist/cascade-list.js +33 -0
- package/dist/cascade-list.js.map +1 -0
- package/dist/cascade-select.d.ts +0 -1
- package/dist/cascade-select.js +47 -119
- package/dist/cascade-select.js.map +1 -1
- package/dist/checkbox-group.d.ts +0 -1
- package/dist/checkbox.d.ts +0 -1
- package/dist/collection-window.d.ts +0 -1
- package/dist/color-picker.d.ts +3 -8
- package/dist/color-picker.js +60 -305
- package/dist/color-picker.js.map +1 -1
- package/dist/combobox.d.ts +0 -1
- package/dist/dialog.d.ts +0 -1
- package/dist/disclosure.d.ts +0 -1
- package/dist/drawer.d.ts +0 -1
- package/dist/editable.d.ts +0 -1
- package/dist/editing/color-text.d.ts +7 -0
- package/dist/editing/color-text.js +199 -0
- package/dist/editing/color-text.js.map +1 -0
- package/dist/error-code.d.ts +1 -2
- package/dist/feed.d.ts +0 -1
- package/dist/grid-control.d.ts +3 -0
- package/dist/grid-control.js +2 -0
- package/dist/grid-control.js.map +1 -0
- package/dist/identity.d.ts +10 -0
- package/dist/identity.js +16 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/interaction.d.ts +0 -1
- package/dist/internal/composites/cascade-choice.d.ts +54 -0
- package/dist/internal/composites/cascade-choice.js +154 -0
- package/dist/internal/composites/cascade-choice.js.map +1 -0
- package/dist/internal/composites/combobox.d.ts +0 -1
- package/dist/internal/composites/combobox.js +28 -11
- package/dist/internal/composites/combobox.js.map +1 -1
- package/dist/internal/composites/grid-control.d.ts +0 -1
- package/dist/internal/composites/grid-control.js +18 -23
- package/dist/internal/composites/grid-control.js.map +1 -1
- package/dist/internal/composites/linear-action.d.ts +0 -1
- package/dist/internal/composites/linear-action.js +20 -14
- package/dist/internal/composites/linear-action.js.map +1 -1
- package/dist/internal/composites/linear-choice.d.ts +0 -1
- package/dist/internal/composites/linear-choice.js +15 -8
- package/dist/internal/composites/linear-choice.js.map +1 -1
- package/dist/internal/composites/listbox.d.ts +0 -1
- package/dist/internal/composites/listbox.js +15 -13
- package/dist/internal/composites/listbox.js.map +1 -1
- package/dist/internal/composites/menu.d.ts +0 -1
- package/dist/internal/composites/menu.js +38 -17
- package/dist/internal/composites/menu.js.map +1 -1
- package/dist/internal/composites/popup.d.ts +0 -1
- package/dist/internal/composites/slider.d.ts +0 -1
- package/dist/internal/composites/tree-grid.d.ts +0 -1
- package/dist/internal/composites/tree-grid.js +70 -48
- package/dist/internal/composites/tree-grid.js.map +1 -1
- package/dist/internal/composites/tree-view.d.ts +0 -1
- package/dist/internal/composites/tree-view.js +32 -20
- package/dist/internal/composites/tree-view.js.map +1 -1
- package/dist/internal/editing/text.d.ts +0 -1
- package/dist/internal/editing/text.js +40 -27
- package/dist/internal/editing/text.js.map +1 -1
- package/dist/internal/kernel/bounded-scalar.d.ts +31 -0
- package/dist/internal/kernel/bounded-scalar.js +78 -0
- package/dist/internal/kernel/bounded-scalar.js.map +1 -0
- package/dist/internal/kernel/decimal.d.ts +0 -1
- package/dist/internal/kernel/exact-ratio.d.ts +5 -0
- package/dist/internal/kernel/exact-ratio.js +2 -0
- package/dist/internal/kernel/exact-ratio.js.map +1 -0
- package/dist/internal/kernel/foundation.d.ts +8 -6
- package/dist/internal/kernel/foundation.js +35 -16
- package/dist/internal/kernel/foundation.js.map +1 -1
- package/dist/internal/kernel/indexed-sequence.d.ts +28 -3
- package/dist/internal/kernel/indexed-sequence.js +162 -12
- package/dist/internal/kernel/indexed-sequence.js.map +1 -1
- package/dist/internal/kernel/machine.d.ts +1 -2
- package/dist/internal/kernel/machine.js +5 -2
- package/dist/internal/kernel/machine.js.map +1 -1
- package/dist/internal/runtime/revision.d.ts +0 -1
- package/dist/internal/state/checked.d.ts +0 -1
- package/dist/internal/state/cursor.d.ts +0 -1
- package/dist/internal/state/expansion.d.ts +0 -1
- package/dist/internal/state/open-state.d.ts +0 -1
- package/dist/internal/state/selection.d.ts +0 -1
- package/dist/internal/state/selection.js +99 -27
- package/dist/internal/state/selection.js.map +1 -1
- package/dist/layer-stack.d.ts +0 -1
- package/dist/listbox.d.ts +0 -1
- package/dist/menu-button.d.ts +0 -1
- package/dist/menu.d.ts +0 -1
- package/dist/menubar.d.ts +0 -1
- package/dist/meter-group.d.ts +38 -0
- package/dist/meter-group.js +103 -0
- package/dist/meter-group.js.map +1 -0
- package/dist/meter.d.ts +25 -0
- package/dist/meter.js +89 -0
- package/dist/meter.js.map +1 -0
- package/dist/multi-thumb-slider.d.ts +0 -1
- package/dist/navigation-menu.d.ts +0 -1
- package/dist/number-field.d.ts +0 -1
- package/dist/pagination.d.ts +0 -1
- package/dist/pin-input.d.ts +0 -1
- package/dist/popover.d.ts +0 -1
- package/dist/progress.d.ts +17 -0
- package/dist/progress.js +49 -0
- package/dist/progress.js.map +1 -0
- package/dist/quantity-field.d.ts +0 -1
- package/dist/radio-group.d.ts +0 -1
- package/dist/rating.d.ts +0 -1
- package/dist/reorder.d.ts +0 -1
- package/dist/reorder.js +92 -43
- package/dist/reorder.js.map +1 -1
- package/dist/result.d.ts +2 -1
- package/dist/result.js +14 -0
- package/dist/result.js.map +1 -1
- package/dist/revision.d.ts +1 -1
- package/dist/revision.js +1 -0
- package/dist/revision.js.map +1 -1
- package/dist/select.d.ts +0 -1
- package/dist/selection.d.ts +24 -0
- package/dist/selection.js +20 -0
- package/dist/selection.js.map +1 -0
- package/dist/shared.d.ts +0 -1
- package/dist/slider.d.ts +0 -1
- package/dist/spin-button.d.ts +0 -1
- package/dist/stepper.d.ts +0 -1
- package/dist/structures/anchored-layout.d.ts +33 -0
- package/dist/structures/anchored-layout.js +158 -0
- package/dist/structures/anchored-layout.js.map +1 -0
- package/dist/structures/color.d.ts +80 -0
- package/dist/structures/color.js +206 -0
- package/dist/structures/color.js.map +1 -0
- package/dist/structures/geometry.d.ts +53 -0
- package/dist/structures/geometry.js +165 -0
- package/dist/structures/geometry.js.map +1 -0
- package/dist/structures/grid.d.ts +20 -4
- package/dist/structures/grid.js +91 -50
- package/dist/structures/grid.js.map +1 -1
- package/dist/structures/index-span.d.ts +31 -0
- package/dist/structures/index-span.js +267 -0
- package/dist/structures/index-span.js.map +1 -0
- package/dist/structures/metric-index.d.ts +36 -0
- package/dist/structures/metric-index.js +390 -0
- package/dist/structures/metric-index.js.map +1 -0
- package/dist/structures/range.d.ts +21 -4
- package/dist/structures/range.js +156 -11
- package/dist/structures/range.js.map +1 -1
- package/dist/structures/selection-expression.d.ts +26 -0
- package/dist/structures/selection-expression.js +157 -0
- package/dist/structures/selection-expression.js.map +1 -0
- package/dist/structures/sequence.d.ts +0 -1
- package/dist/structures/sequence.js +69 -7
- package/dist/structures/sequence.js.map +1 -1
- package/dist/structures/tree.d.ts +5 -1
- package/dist/structures/tree.js +33 -5
- package/dist/structures/tree.js.map +1 -1
- package/dist/switch.d.ts +0 -1
- package/dist/tabs.d.ts +0 -1
- package/dist/tags-input.d.ts +0 -1
- package/dist/text.d.ts +0 -1
- package/dist/timer.d.ts +0 -1
- package/dist/toast.d.ts +2 -2
- package/dist/toast.js +3 -1
- package/dist/toast.js.map +1 -1
- package/dist/toggle-button.d.ts +0 -1
- package/dist/toggle-group.d.ts +0 -1
- package/dist/toolbar.d.ts +0 -1
- package/dist/tooltip.d.ts +0 -1
- package/dist/tree-grid.d.ts +0 -1
- package/dist/tree-view.d.ts +0 -1
- package/dist/units.d.ts +0 -1
- package/dist/window-splitter.d.ts +0 -1
- package/package.json +75 -12
- package/dist/accordion.d.ts.map +0 -1
- package/dist/adapter-runtime.d.ts.map +0 -1
- package/dist/alert-dialog.d.ts.map +0 -1
- package/dist/carousel.d.ts.map +0 -1
- package/dist/cascade-select.d.ts.map +0 -1
- package/dist/checkbox-group.d.ts.map +0 -1
- package/dist/checkbox.d.ts.map +0 -1
- package/dist/collection-window.d.ts.map +0 -1
- package/dist/color-picker.d.ts.map +0 -1
- package/dist/combobox.d.ts.map +0 -1
- package/dist/dialog.d.ts.map +0 -1
- package/dist/disclosure.d.ts.map +0 -1
- package/dist/drawer.d.ts.map +0 -1
- package/dist/editable.d.ts.map +0 -1
- package/dist/error-code.d.ts.map +0 -1
- package/dist/feed.d.ts.map +0 -1
- package/dist/form.d.ts +0 -135
- package/dist/form.d.ts.map +0 -1
- package/dist/form.js +0 -627
- package/dist/form.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/interaction.d.ts.map +0 -1
- package/dist/internal/composites/combobox.d.ts.map +0 -1
- package/dist/internal/composites/grid-control.d.ts.map +0 -1
- package/dist/internal/composites/linear-action.d.ts.map +0 -1
- package/dist/internal/composites/linear-choice.d.ts.map +0 -1
- package/dist/internal/composites/listbox.d.ts.map +0 -1
- package/dist/internal/composites/menu.d.ts.map +0 -1
- package/dist/internal/composites/popup.d.ts.map +0 -1
- package/dist/internal/composites/slider.d.ts.map +0 -1
- package/dist/internal/composites/tree-grid.d.ts.map +0 -1
- package/dist/internal/composites/tree-view.d.ts.map +0 -1
- package/dist/internal/editing/text.d.ts.map +0 -1
- package/dist/internal/kernel/decimal.d.ts.map +0 -1
- package/dist/internal/kernel/foundation.d.ts.map +0 -1
- package/dist/internal/kernel/indexed-sequence.d.ts.map +0 -1
- package/dist/internal/kernel/machine.d.ts.map +0 -1
- package/dist/internal/runtime/revision.d.ts.map +0 -1
- package/dist/internal/state/checked.d.ts.map +0 -1
- package/dist/internal/state/cursor.d.ts.map +0 -1
- package/dist/internal/state/expansion.d.ts.map +0 -1
- package/dist/internal/state/open-state.d.ts.map +0 -1
- package/dist/internal/state/selection.d.ts.map +0 -1
- package/dist/layer-stack.d.ts.map +0 -1
- package/dist/listbox.d.ts.map +0 -1
- package/dist/menu-button.d.ts.map +0 -1
- package/dist/menu.d.ts.map +0 -1
- package/dist/menubar.d.ts.map +0 -1
- package/dist/multi-thumb-slider.d.ts.map +0 -1
- package/dist/navigation-menu.d.ts.map +0 -1
- package/dist/number-field.d.ts.map +0 -1
- package/dist/pagination.d.ts.map +0 -1
- package/dist/pin-input.d.ts.map +0 -1
- package/dist/popover.d.ts.map +0 -1
- package/dist/quantity-field.d.ts.map +0 -1
- package/dist/radio-group.d.ts.map +0 -1
- package/dist/rating.d.ts.map +0 -1
- package/dist/reorder.d.ts.map +0 -1
- package/dist/result.d.ts.map +0 -1
- package/dist/revision.d.ts.map +0 -1
- package/dist/select.d.ts.map +0 -1
- package/dist/shared.d.ts.map +0 -1
- package/dist/slider.d.ts.map +0 -1
- package/dist/spin-button.d.ts.map +0 -1
- package/dist/stepper.d.ts.map +0 -1
- package/dist/structures/grid.d.ts.map +0 -1
- package/dist/structures/range.d.ts.map +0 -1
- package/dist/structures/sequence.d.ts.map +0 -1
- package/dist/structures/tree.d.ts.map +0 -1
- package/dist/switch.d.ts.map +0 -1
- package/dist/tabs.d.ts.map +0 -1
- package/dist/tags-input.d.ts.map +0 -1
- package/dist/text.d.ts.map +0 -1
- package/dist/timer.d.ts.map +0 -1
- package/dist/toast.d.ts.map +0 -1
- package/dist/toggle-button.d.ts.map +0 -1
- package/dist/toggle-group.d.ts.map +0 -1
- package/dist/toolbar.d.ts.map +0 -1
- package/dist/tooltip.d.ts.map +0 -1
- package/dist/tree-grid.d.ts.map +0 -1
- package/dist/tree-view.d.ts.map +0 -1
- package/dist/units.d.ts.map +0 -1
- package/dist/window-splitter.d.ts.map +0 -1
package/dist/form.js
DELETED
|
@@ -1,627 +0,0 @@
|
|
|
1
|
-
import { fail, ok } from './internal/kernel/foundation.js';
|
|
2
|
-
import { unwrap } from './result.js';
|
|
3
|
-
export function createFormFieldPath(path) {
|
|
4
|
-
return unwrap(tryCreateFormFieldPath(path));
|
|
5
|
-
}
|
|
6
|
-
export function tryCreateFormFieldPath(path) {
|
|
7
|
-
const segments = typeof path === 'string' ? parsePath(path) : ok([...path]);
|
|
8
|
-
if (!segments.ok)
|
|
9
|
-
return segments;
|
|
10
|
-
return validatePathSegments(segments.value, true);
|
|
11
|
-
}
|
|
12
|
-
export function createFormRelativePath(path) {
|
|
13
|
-
return unwrap(tryCreateFormRelativePath(path));
|
|
14
|
-
}
|
|
15
|
-
export function tryCreateFormRelativePath(path) {
|
|
16
|
-
const segments = typeof path === 'string'
|
|
17
|
-
? parsePath(path)
|
|
18
|
-
: ok(typeof path === 'number' ? [path] : [...path]);
|
|
19
|
-
if (!segments.ok)
|
|
20
|
-
return segments;
|
|
21
|
-
return validatePathSegments(segments.value, false);
|
|
22
|
-
}
|
|
23
|
-
export function appendFormFieldPath(base, relative) {
|
|
24
|
-
return Object.freeze([
|
|
25
|
-
...createFormFieldPath(base),
|
|
26
|
-
...createFormRelativePath(relative),
|
|
27
|
-
]);
|
|
28
|
-
}
|
|
29
|
-
function validatePathSegments(segments, requireStringRoot) {
|
|
30
|
-
if (segments.length === 0 || (requireStringRoot && typeof segments[0] !== 'string')) {
|
|
31
|
-
return fail('construction', requireStringRoot ? 'form-field-path-root-invalid' : 'form-relative-path-empty', requireStringRoot
|
|
32
|
-
? 'A Form field path must start with a string segment.'
|
|
33
|
-
: 'A relative Form field path must not be empty.');
|
|
34
|
-
}
|
|
35
|
-
for (const segment of segments) {
|
|
36
|
-
if (typeof segment === 'number') {
|
|
37
|
-
if (!Number.isSafeInteger(segment) || segment < 0) {
|
|
38
|
-
return fail('construction', 'form-field-path-index-invalid', 'Form field path indices must be non-negative safe integers.');
|
|
39
|
-
}
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (segment.length === 0 || /[.\[\]]/u.test(segment)) {
|
|
43
|
-
return fail('construction', 'form-field-path-segment-invalid', 'Form field path string segments must be non-empty and must not contain dots or brackets.');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return ok(Object.freeze([...segments]));
|
|
47
|
-
}
|
|
48
|
-
export function encodeFormFieldPath(path) {
|
|
49
|
-
const segments = createFormFieldPath(path);
|
|
50
|
-
return segments.map((segment, index) => (typeof segment === 'number'
|
|
51
|
-
? `[${segment}]`
|
|
52
|
-
: index === 0 ? segment : `.${segment}`)).join('');
|
|
53
|
-
}
|
|
54
|
-
export function createFormValues(entries) {
|
|
55
|
-
return unwrap(tryCreateFormValues(entries));
|
|
56
|
-
}
|
|
57
|
-
export function tryCreateFormValues(entries) {
|
|
58
|
-
const root = Object.create(null);
|
|
59
|
-
const branches = new WeakSet();
|
|
60
|
-
const repeated = new Map();
|
|
61
|
-
branches.add(root);
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
const path = tryCreateFormFieldPath(entry.path);
|
|
64
|
-
if (!path.ok)
|
|
65
|
-
return path;
|
|
66
|
-
const canonical = encodeSegments(path.value);
|
|
67
|
-
let container = root;
|
|
68
|
-
for (let index = 0; index < path.value.length - 1; index += 1) {
|
|
69
|
-
const segment = path.value[index];
|
|
70
|
-
const nextSegment = path.value[index + 1];
|
|
71
|
-
const existing = readContainer(container, segment);
|
|
72
|
-
const needsArray = typeof nextSegment === 'number';
|
|
73
|
-
if (!hasContainer(container, segment)) {
|
|
74
|
-
const branch = needsArray
|
|
75
|
-
? []
|
|
76
|
-
: Object.create(null);
|
|
77
|
-
branches.add(branch);
|
|
78
|
-
writeContainer(container, segment, branch);
|
|
79
|
-
container = branch;
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (typeof existing !== 'object'
|
|
83
|
-
|| existing === null
|
|
84
|
-
|| !branches.has(existing)
|
|
85
|
-
|| Array.isArray(existing) !== needsArray) {
|
|
86
|
-
return valueCollision(canonical);
|
|
87
|
-
}
|
|
88
|
-
container = existing;
|
|
89
|
-
}
|
|
90
|
-
const leaf = path.value[path.value.length - 1];
|
|
91
|
-
const existing = readContainer(container, leaf);
|
|
92
|
-
if (!hasContainer(container, leaf)) {
|
|
93
|
-
writeContainer(container, leaf, entry.value);
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (typeof existing === 'object' && existing !== null && branches.has(existing)) {
|
|
97
|
-
return valueCollision(canonical);
|
|
98
|
-
}
|
|
99
|
-
const values = repeated.get(canonical);
|
|
100
|
-
if (values === undefined) {
|
|
101
|
-
const next = [existing, entry.value];
|
|
102
|
-
repeated.set(canonical, next);
|
|
103
|
-
writeContainer(container, leaf, next);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
values.push(entry.value);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
freezeBranches(root, branches);
|
|
110
|
-
return ok(root);
|
|
111
|
-
}
|
|
112
|
-
export function createFormState(input = {}) {
|
|
113
|
-
return unwrap(tryCreateFormState(input));
|
|
114
|
-
}
|
|
115
|
-
export function tryCreateFormState(input = {}) {
|
|
116
|
-
const validationGeneration = input.validationGeneration ?? 0;
|
|
117
|
-
const validationStatus = input.validationStatus ?? 'idle';
|
|
118
|
-
const validationTrigger = input.validationTrigger ?? null;
|
|
119
|
-
const validationIntent = input.validationIntent ?? null;
|
|
120
|
-
const submissionGeneration = input.submissionGeneration ?? 0;
|
|
121
|
-
const submissionStatus = input.submissionStatus ?? 'idle';
|
|
122
|
-
const submitCount = input.submitCount ?? 0;
|
|
123
|
-
if (!Number.isSafeInteger(validationGeneration) || validationGeneration < 0) {
|
|
124
|
-
return fail('construction', 'form-validation-generation-invalid', 'Form validation generation must be a non-negative safe integer.');
|
|
125
|
-
}
|
|
126
|
-
if (!Number.isSafeInteger(submissionGeneration) || submissionGeneration < 0) {
|
|
127
|
-
return fail('construction', 'form-submission-generation-invalid', 'Form submission generation must be a non-negative safe integer.');
|
|
128
|
-
}
|
|
129
|
-
if (!isValidationStatus(validationStatus)) {
|
|
130
|
-
return fail('construction', 'form-validation-status-invalid', 'Form validation status is invalid.');
|
|
131
|
-
}
|
|
132
|
-
if (validationTrigger !== null && !isValidationTrigger(validationTrigger)) {
|
|
133
|
-
return fail('construction', 'form-validation-trigger-invalid', 'Form validation trigger is invalid.');
|
|
134
|
-
}
|
|
135
|
-
if (validationIntent !== null && !isValidationIntent(validationIntent)) {
|
|
136
|
-
return fail('construction', 'form-validation-intent-invalid', 'Form validation intent is invalid.');
|
|
137
|
-
}
|
|
138
|
-
if (!isSubmissionStatus(submissionStatus)) {
|
|
139
|
-
return fail('construction', 'form-submission-status-invalid', 'Form submission status is invalid.');
|
|
140
|
-
}
|
|
141
|
-
if ((validationStatus === 'idle' && (validationTrigger !== null || validationIntent !== null))
|
|
142
|
-
|| (validationStatus !== 'idle' && (validationTrigger === null || validationIntent === null))) {
|
|
143
|
-
return fail('construction', 'form-validation-context-invalid', 'Form validation context must be present exactly while validation has run.');
|
|
144
|
-
}
|
|
145
|
-
if (!Number.isSafeInteger(submitCount) || submitCount < 0) {
|
|
146
|
-
return fail('construction', 'form-submit-count-invalid', 'Form submitCount must be a non-negative safe integer.');
|
|
147
|
-
}
|
|
148
|
-
const fields = [];
|
|
149
|
-
for (const inputField of input.fields ?? []) {
|
|
150
|
-
if (fields.some((field) => field.id === inputField.id)) {
|
|
151
|
-
return fail('construction', 'form-field-id-duplicate', 'Form field identifiers must be unique.');
|
|
152
|
-
}
|
|
153
|
-
const field = normalizeField(inputField);
|
|
154
|
-
if (!field.ok)
|
|
155
|
-
return field;
|
|
156
|
-
fields.push(field.value);
|
|
157
|
-
}
|
|
158
|
-
const issues = normalizeIssues(input.issues ?? [], undefined);
|
|
159
|
-
if (!issues.ok)
|
|
160
|
-
return issues;
|
|
161
|
-
const issueIds = [
|
|
162
|
-
...issues.value.map((issue) => issue.id),
|
|
163
|
-
...fields.flatMap((field) => field.issues.map((issue) => issue.id)),
|
|
164
|
-
];
|
|
165
|
-
if (new Set(issueIds).size !== issueIds.length) {
|
|
166
|
-
return fail('construction', 'form-issue-id-duplicate', 'Form issue identifiers must be unique.');
|
|
167
|
-
}
|
|
168
|
-
return ok(buildState({
|
|
169
|
-
validationGeneration,
|
|
170
|
-
validationStatus,
|
|
171
|
-
validationTrigger,
|
|
172
|
-
validationIntent,
|
|
173
|
-
submissionGeneration,
|
|
174
|
-
submissionStatus,
|
|
175
|
-
submitCount,
|
|
176
|
-
submitted: input.submitted ?? submitCount > 0,
|
|
177
|
-
fields,
|
|
178
|
-
issues: issues.value,
|
|
179
|
-
}));
|
|
180
|
-
}
|
|
181
|
-
export function applyFormEvent(state, event) {
|
|
182
|
-
const current = tryCreateFormState(state);
|
|
183
|
-
if (!current.ok) {
|
|
184
|
-
return fail('transition-rejection', current.error.code, current.error.message);
|
|
185
|
-
}
|
|
186
|
-
if (typeof event !== 'string') {
|
|
187
|
-
if (event.type === 'register-field')
|
|
188
|
-
return registerField(state, event.field);
|
|
189
|
-
if (event.type === 'unregister-field')
|
|
190
|
-
return unregisterField(state, event.id);
|
|
191
|
-
if (event.type === 'update-field')
|
|
192
|
-
return updateField(state, event);
|
|
193
|
-
if (event.type === 'reorder-fields')
|
|
194
|
-
return reorderFields(state, event.ids);
|
|
195
|
-
if (event.type === 'replace-issues') {
|
|
196
|
-
if (event.generation !== undefined) {
|
|
197
|
-
const currentGeneration = requireValidationGeneration(state, event.generation);
|
|
198
|
-
if (!currentGeneration.ok)
|
|
199
|
-
return currentGeneration;
|
|
200
|
-
}
|
|
201
|
-
return replaceIssues(state, event.source, event.issues);
|
|
202
|
-
}
|
|
203
|
-
if (event.type === 'validation-invalidated')
|
|
204
|
-
return invalidateValidation(state);
|
|
205
|
-
if (event.type === 'validation-started') {
|
|
206
|
-
return startValidation(state, event.trigger, event.intent);
|
|
207
|
-
}
|
|
208
|
-
if (event.type === 'validation-completed') {
|
|
209
|
-
return completeValidation(state, event.trigger, event.intent, event.generation);
|
|
210
|
-
}
|
|
211
|
-
if (event.type === 'submit-failed') {
|
|
212
|
-
return submitFailed(state, event.generation, event.issues ?? []);
|
|
213
|
-
}
|
|
214
|
-
if (event.type === 'submit-started') {
|
|
215
|
-
const currentGeneration = requireSubmissionGeneration(state, event.generation);
|
|
216
|
-
if (!currentGeneration.ok)
|
|
217
|
-
return currentGeneration;
|
|
218
|
-
if (state.submissionStatus !== 'idle'
|
|
219
|
-
|| state.validationStatus !== 'valid'
|
|
220
|
-
|| state.validationIntent !== 'submission') {
|
|
221
|
-
return fail('transition-rejection', 'form-submit-not-requested', 'Form submission can start only after a valid submit request.');
|
|
222
|
-
}
|
|
223
|
-
return update(buildState({ ...state, submissionStatus: 'submitting' }));
|
|
224
|
-
}
|
|
225
|
-
if (event.type === 'submit-succeeded') {
|
|
226
|
-
const currentGeneration = requireSubmissionGeneration(state, event.generation);
|
|
227
|
-
if (!currentGeneration.ok)
|
|
228
|
-
return currentGeneration;
|
|
229
|
-
if (state.submissionStatus !== 'submitting') {
|
|
230
|
-
return fail('transition-rejection', 'form-submit-not-pending', 'Form submission can succeed only while pending.');
|
|
231
|
-
}
|
|
232
|
-
return update(buildState({
|
|
233
|
-
...withoutIssueSource(state, 'server'),
|
|
234
|
-
submissionStatus: 'succeeded',
|
|
235
|
-
}));
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return reset(state);
|
|
239
|
-
}
|
|
240
|
-
function registerField(state, input) {
|
|
241
|
-
const normalized = normalizeField(input);
|
|
242
|
-
if (!normalized.ok)
|
|
243
|
-
return transitionError(normalized);
|
|
244
|
-
const index = state.fields.findIndex((field) => field.id === input.id);
|
|
245
|
-
const fields = [...state.fields];
|
|
246
|
-
if (index < 0)
|
|
247
|
-
fields.push(normalized.value);
|
|
248
|
-
else
|
|
249
|
-
fields[index] = normalized.value;
|
|
250
|
-
return rebuild(state, fields, state.issues);
|
|
251
|
-
}
|
|
252
|
-
function unregisterField(state, id) {
|
|
253
|
-
if (!state.fields.some((field) => field.id === id)) {
|
|
254
|
-
return fail('transition-rejection', 'form-field-id-missing', 'The Form field to unregister does not exist.');
|
|
255
|
-
}
|
|
256
|
-
return rebuild(state, state.fields.filter((field) => field.id !== id), state.issues);
|
|
257
|
-
}
|
|
258
|
-
function updateField(state, event) {
|
|
259
|
-
const index = state.fields.findIndex((field) => field.id === event.id);
|
|
260
|
-
if (index < 0) {
|
|
261
|
-
return fail('transition-rejection', 'form-field-id-missing', 'The Form field to update does not exist.');
|
|
262
|
-
}
|
|
263
|
-
const current = state.fields[index];
|
|
264
|
-
const normalized = normalizeField({
|
|
265
|
-
id: current.id,
|
|
266
|
-
...(current.name === null ? {} : { name: current.name }),
|
|
267
|
-
touched: event.touched ?? current.touched,
|
|
268
|
-
dirty: event.dirty ?? current.dirty,
|
|
269
|
-
valid: event.valid ?? current.valid,
|
|
270
|
-
issues: event.issues ?? current.issues,
|
|
271
|
-
});
|
|
272
|
-
if (!normalized.ok)
|
|
273
|
-
return transitionError(normalized);
|
|
274
|
-
const fields = [...state.fields];
|
|
275
|
-
fields[index] = normalized.value;
|
|
276
|
-
return rebuild(state, fields, state.issues);
|
|
277
|
-
}
|
|
278
|
-
function reorderFields(state, ids) {
|
|
279
|
-
if (ids.length !== state.fields.length
|
|
280
|
-
|| new Set(ids).size !== ids.length
|
|
281
|
-
|| ids.some((id) => !state.fields.some((field) => field.id === id))) {
|
|
282
|
-
return fail('transition-rejection', 'form-field-order-invalid', 'Form field order must contain every registered field exactly once.');
|
|
283
|
-
}
|
|
284
|
-
const byId = new Map(state.fields.map((field) => [field.id, field]));
|
|
285
|
-
return rebuild(state, ids.map((id) => byId.get(id)), state.issues);
|
|
286
|
-
}
|
|
287
|
-
function replaceIssues(state, source, inputIssues) {
|
|
288
|
-
if (inputIssues.some((issue) => issue.source !== source)) {
|
|
289
|
-
return fail('transition-rejection', 'form-issue-source-mismatch', 'Every replacement issue must match the requested source.');
|
|
290
|
-
}
|
|
291
|
-
const normalized = normalizeIssues(inputIssues, undefined);
|
|
292
|
-
if (!normalized.ok)
|
|
293
|
-
return transitionError(normalized);
|
|
294
|
-
const registered = new Set(state.fields.map((field) => field.id));
|
|
295
|
-
const fields = state.fields.map((field) => {
|
|
296
|
-
const fieldIssues = Object.freeze([
|
|
297
|
-
...field.issues.filter((issue) => issue.source !== source),
|
|
298
|
-
...normalized.value.filter((issue) => issue.fieldId === field.id),
|
|
299
|
-
]);
|
|
300
|
-
return Object.freeze({
|
|
301
|
-
...field,
|
|
302
|
-
valid: fieldIssues.length === 0,
|
|
303
|
-
issues: fieldIssues,
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
const issues = [
|
|
307
|
-
...state.issues.filter((issue) => issue.source !== source),
|
|
308
|
-
...normalized.value.filter((issue) => issue.fieldId === undefined || !registered.has(issue.fieldId)),
|
|
309
|
-
];
|
|
310
|
-
return rebuild(state, fields, issues);
|
|
311
|
-
}
|
|
312
|
-
function invalidateValidation(state) {
|
|
313
|
-
if (state.validationStatus === 'idle' && state.submissionStatus === 'idle') {
|
|
314
|
-
return update(state);
|
|
315
|
-
}
|
|
316
|
-
return update(buildState({
|
|
317
|
-
...state,
|
|
318
|
-
validationStatus: 'idle',
|
|
319
|
-
validationTrigger: null,
|
|
320
|
-
validationIntent: null,
|
|
321
|
-
submissionStatus: state.submissionStatus === 'submitting'
|
|
322
|
-
? 'submitting'
|
|
323
|
-
: 'idle',
|
|
324
|
-
}));
|
|
325
|
-
}
|
|
326
|
-
function startValidation(state, trigger, intent) {
|
|
327
|
-
if (state.submissionStatus === 'submitting') {
|
|
328
|
-
return fail('transition-rejection', 'form-validation-during-submit', 'Form validation cannot start while submission is pending.');
|
|
329
|
-
}
|
|
330
|
-
if (state.validationGeneration === Number.MAX_SAFE_INTEGER) {
|
|
331
|
-
return fail('resource-rejection', 'form-validation-generation-exhausted', 'Form validation generation cannot advance beyond the safe-integer ceiling.');
|
|
332
|
-
}
|
|
333
|
-
const submission = intent === 'submission';
|
|
334
|
-
const current = submission ? withoutIssueSource(state, 'server') : state;
|
|
335
|
-
return update(buildState({
|
|
336
|
-
...current,
|
|
337
|
-
validationGeneration: current.validationGeneration + 1,
|
|
338
|
-
validationStatus: 'validating',
|
|
339
|
-
validationTrigger: trigger,
|
|
340
|
-
validationIntent: intent,
|
|
341
|
-
submissionStatus: 'idle',
|
|
342
|
-
submitCount: current.submitCount + (submission ? 1 : 0),
|
|
343
|
-
submitted: current.submitted || submission,
|
|
344
|
-
}));
|
|
345
|
-
}
|
|
346
|
-
function completeValidation(state, trigger, intent, generation) {
|
|
347
|
-
const currentGeneration = requireValidationGeneration(state, generation);
|
|
348
|
-
if (!currentGeneration.ok)
|
|
349
|
-
return currentGeneration;
|
|
350
|
-
if (state.validationStatus !== 'validating'
|
|
351
|
-
|| state.validationTrigger !== trigger
|
|
352
|
-
|| state.validationIntent !== intent) {
|
|
353
|
-
return fail('transition-rejection', 'form-validation-not-pending', 'Form validation can complete only for the active validation run.');
|
|
354
|
-
}
|
|
355
|
-
const completed = buildState({
|
|
356
|
-
...state,
|
|
357
|
-
validationStatus: state.valid ? 'valid' : 'invalid',
|
|
358
|
-
});
|
|
359
|
-
if (intent !== 'submission')
|
|
360
|
-
return update(completed);
|
|
361
|
-
if (completed.valid) {
|
|
362
|
-
if (completed.submissionGeneration === Number.MAX_SAFE_INTEGER) {
|
|
363
|
-
return fail('resource-rejection', 'form-submission-generation-exhausted', 'Form submission generation cannot advance beyond the safe-integer ceiling.');
|
|
364
|
-
}
|
|
365
|
-
const submissionGeneration = completed.submissionGeneration + 1;
|
|
366
|
-
return update(buildState({ ...completed, submissionGeneration }), [{ type: 'submit-requested', generation: submissionGeneration }]);
|
|
367
|
-
}
|
|
368
|
-
const allIssues = orderedIssues(completed);
|
|
369
|
-
const firstInvalid = completed.fields.find((field) => !field.valid || field.issues.length > 0);
|
|
370
|
-
const commands = [];
|
|
371
|
-
if (firstInvalid !== undefined)
|
|
372
|
-
commands.push({ type: 'focus-field', id: firstInvalid.id });
|
|
373
|
-
if (allIssues.length > 0) {
|
|
374
|
-
commands.push({
|
|
375
|
-
type: 'announce-summary',
|
|
376
|
-
issueIds: Object.freeze(allIssues.map((issue) => issue.id)),
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
return update(completed, commands);
|
|
380
|
-
}
|
|
381
|
-
function submitFailed(state, generation, issues) {
|
|
382
|
-
const currentGeneration = requireSubmissionGeneration(state, generation);
|
|
383
|
-
if (!currentGeneration.ok)
|
|
384
|
-
return currentGeneration;
|
|
385
|
-
if (state.submissionStatus !== 'submitting') {
|
|
386
|
-
return fail('transition-rejection', 'form-submit-not-pending', 'Form submission can fail only while pending.');
|
|
387
|
-
}
|
|
388
|
-
if (issues.some((issue) => issue.source !== 'server')) {
|
|
389
|
-
return fail('transition-rejection', 'form-submit-issue-source-invalid', 'Submit failure issues must use the server source.');
|
|
390
|
-
}
|
|
391
|
-
const replaced = replaceIssues(state, 'server', issues);
|
|
392
|
-
if (!replaced.ok)
|
|
393
|
-
return replaced;
|
|
394
|
-
const failed = buildState({ ...replaced.value.state, submissionStatus: 'failed' });
|
|
395
|
-
const ordered = orderedIssues(failed);
|
|
396
|
-
const firstInvalid = failed.fields.find((field) => !field.valid || field.issues.length > 0);
|
|
397
|
-
const commands = [];
|
|
398
|
-
if (firstInvalid !== undefined)
|
|
399
|
-
commands.push({ type: 'focus-field', id: firstInvalid.id });
|
|
400
|
-
if (ordered.length > 0) {
|
|
401
|
-
commands.push({
|
|
402
|
-
type: 'announce-summary',
|
|
403
|
-
issueIds: Object.freeze(ordered.map((issue) => issue.id)),
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
return update(failed, commands);
|
|
407
|
-
}
|
|
408
|
-
function reset(state) {
|
|
409
|
-
const fields = state.fields.map((field) => Object.freeze({
|
|
410
|
-
...field,
|
|
411
|
-
touched: false,
|
|
412
|
-
dirty: false,
|
|
413
|
-
valid: true,
|
|
414
|
-
issues: Object.freeze([]),
|
|
415
|
-
}));
|
|
416
|
-
return update(buildState({
|
|
417
|
-
validationGeneration: state.validationGeneration,
|
|
418
|
-
validationStatus: 'idle',
|
|
419
|
-
validationTrigger: null,
|
|
420
|
-
validationIntent: null,
|
|
421
|
-
submissionGeneration: state.submissionGeneration,
|
|
422
|
-
submissionStatus: 'idle',
|
|
423
|
-
submitCount: 0,
|
|
424
|
-
submitted: false,
|
|
425
|
-
fields,
|
|
426
|
-
issues: [],
|
|
427
|
-
}), fields.map((field) => ({ type: 'reset-field', id: field.id })));
|
|
428
|
-
}
|
|
429
|
-
function normalizeField(input) {
|
|
430
|
-
if (input.id.trim().length === 0) {
|
|
431
|
-
return fail('construction', 'form-field-id-empty', 'Form field identifiers must not be empty.');
|
|
432
|
-
}
|
|
433
|
-
const name = input.name?.trim() || null;
|
|
434
|
-
const issues = normalizeIssues(input.issues ?? [], input.id);
|
|
435
|
-
if (!issues.ok)
|
|
436
|
-
return issues;
|
|
437
|
-
return ok(Object.freeze({
|
|
438
|
-
id: input.id,
|
|
439
|
-
name,
|
|
440
|
-
touched: input.touched ?? false,
|
|
441
|
-
dirty: input.dirty ?? false,
|
|
442
|
-
valid: input.valid ?? issues.value.length === 0,
|
|
443
|
-
issues: issues.value,
|
|
444
|
-
}));
|
|
445
|
-
}
|
|
446
|
-
function normalizeIssues(input, fieldId) {
|
|
447
|
-
const issues = [];
|
|
448
|
-
for (const issue of input) {
|
|
449
|
-
if (issue.id.trim().length === 0 || issue.message.trim().length === 0) {
|
|
450
|
-
return fail('construction', 'form-issue-invalid', 'Form issue identifiers and messages must not be empty.');
|
|
451
|
-
}
|
|
452
|
-
if (issues.some((candidate) => candidate.id === issue.id)) {
|
|
453
|
-
return fail('construction', 'form-issue-id-duplicate', 'Form issue identifiers must be unique.');
|
|
454
|
-
}
|
|
455
|
-
if (fieldId !== undefined && issue.fieldId !== undefined && issue.fieldId !== fieldId) {
|
|
456
|
-
return fail('construction', 'form-issue-field-mismatch', 'A field issue must reference its owning field.');
|
|
457
|
-
}
|
|
458
|
-
issues.push(Object.freeze({
|
|
459
|
-
id: issue.id,
|
|
460
|
-
message: issue.message.trim(),
|
|
461
|
-
source: issue.source,
|
|
462
|
-
...((fieldId ?? issue.fieldId) === undefined
|
|
463
|
-
? {}
|
|
464
|
-
: { fieldId: fieldId ?? issue.fieldId }),
|
|
465
|
-
}));
|
|
466
|
-
}
|
|
467
|
-
return ok(Object.freeze(issues));
|
|
468
|
-
}
|
|
469
|
-
function buildState(input) {
|
|
470
|
-
const fields = Object.freeze([...input.fields]);
|
|
471
|
-
const issues = Object.freeze([...input.issues]);
|
|
472
|
-
return Object.freeze({
|
|
473
|
-
...input,
|
|
474
|
-
touched: fields.some((field) => field.touched),
|
|
475
|
-
dirty: fields.some((field) => field.dirty),
|
|
476
|
-
valid: issues.length === 0 && fields.every((field) => field.valid && field.issues.length === 0),
|
|
477
|
-
fields,
|
|
478
|
-
issues,
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
function requireValidationGeneration(state, generation) {
|
|
482
|
-
if (!Number.isSafeInteger(generation) || generation < 1) {
|
|
483
|
-
return fail('transition-rejection', 'form-validation-generation-invalid', 'Validation generation must be a positive safe integer.');
|
|
484
|
-
}
|
|
485
|
-
if (state.validationStatus !== 'validating' || generation !== state.validationGeneration) {
|
|
486
|
-
return fail('transition-rejection', 'form-validation-generation-stale', 'Validation result does not belong to the active generation.', { generation, currentGeneration: state.validationGeneration });
|
|
487
|
-
}
|
|
488
|
-
return ok(true);
|
|
489
|
-
}
|
|
490
|
-
function requireSubmissionGeneration(state, generation) {
|
|
491
|
-
if (!Number.isSafeInteger(generation) || generation < 1) {
|
|
492
|
-
return fail('transition-rejection', 'form-submission-generation-invalid', 'Submission generation must be a positive safe integer.');
|
|
493
|
-
}
|
|
494
|
-
if (generation !== state.submissionGeneration) {
|
|
495
|
-
return fail('transition-rejection', 'form-submission-generation-stale', 'Submission result does not belong to the active generation.', { generation, currentGeneration: state.submissionGeneration });
|
|
496
|
-
}
|
|
497
|
-
return ok(true);
|
|
498
|
-
}
|
|
499
|
-
function rebuild(state, fields, issues) {
|
|
500
|
-
const created = tryCreateFormState({ ...state, fields, issues });
|
|
501
|
-
return created.ok ? update(created.value) : transitionError(created);
|
|
502
|
-
}
|
|
503
|
-
function orderedIssues(state) {
|
|
504
|
-
return [
|
|
505
|
-
...state.fields.flatMap((field) => field.issues),
|
|
506
|
-
...state.issues,
|
|
507
|
-
];
|
|
508
|
-
}
|
|
509
|
-
function withoutIssueSource(state, source) {
|
|
510
|
-
return buildState({
|
|
511
|
-
...state,
|
|
512
|
-
fields: state.fields.map((field) => Object.freeze({
|
|
513
|
-
...field,
|
|
514
|
-
issues: Object.freeze(field.issues.filter((issue) => issue.source !== source)),
|
|
515
|
-
})),
|
|
516
|
-
issues: state.issues.filter((issue) => issue.source !== source),
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
function update(state, commands = []) {
|
|
520
|
-
return ok(Object.freeze({ state, commands: Object.freeze([...commands]) }));
|
|
521
|
-
}
|
|
522
|
-
function transitionError(result) {
|
|
523
|
-
if (result.ok) {
|
|
524
|
-
return fail('internal-invariant', 'form-result-unexpected', 'Expected a failed result.');
|
|
525
|
-
}
|
|
526
|
-
return fail('transition-rejection', result.error.code, result.error.message);
|
|
527
|
-
}
|
|
528
|
-
function isValidationStatus(value) {
|
|
529
|
-
return value === 'idle'
|
|
530
|
-
|| value === 'validating'
|
|
531
|
-
|| value === 'valid'
|
|
532
|
-
|| value === 'invalid';
|
|
533
|
-
}
|
|
534
|
-
function isSubmissionStatus(value) {
|
|
535
|
-
return value === 'idle'
|
|
536
|
-
|| value === 'submitting'
|
|
537
|
-
|| value === 'succeeded'
|
|
538
|
-
|| value === 'failed';
|
|
539
|
-
}
|
|
540
|
-
function isValidationTrigger(value) {
|
|
541
|
-
return value === 'input' || value === 'blur' || value === 'submit';
|
|
542
|
-
}
|
|
543
|
-
function isValidationIntent(value) {
|
|
544
|
-
return value === 'interaction' || value === 'submission';
|
|
545
|
-
}
|
|
546
|
-
function parsePath(path) {
|
|
547
|
-
if (path.length === 0) {
|
|
548
|
-
return fail('construction', 'form-field-path-empty', 'A Form field path must not be empty.');
|
|
549
|
-
}
|
|
550
|
-
const segments = [];
|
|
551
|
-
let index = 0;
|
|
552
|
-
let expectSegment = true;
|
|
553
|
-
while (index < path.length) {
|
|
554
|
-
if (path[index] === '.') {
|
|
555
|
-
if (expectSegment)
|
|
556
|
-
return pathSyntaxError(path, index);
|
|
557
|
-
expectSegment = true;
|
|
558
|
-
index += 1;
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
if (path[index] === '[') {
|
|
562
|
-
if (expectSegment && segments.length > 0)
|
|
563
|
-
return pathSyntaxError(path, index);
|
|
564
|
-
const close = path.indexOf(']', index + 1);
|
|
565
|
-
if (close < 0)
|
|
566
|
-
return pathSyntaxError(path, index);
|
|
567
|
-
const value = path.slice(index + 1, close);
|
|
568
|
-
if (value.length === 0 || value.includes('['))
|
|
569
|
-
return pathSyntaxError(path, index);
|
|
570
|
-
segments.push(/^\d+$/u.test(value) ? Number(value) : value);
|
|
571
|
-
index = close + 1;
|
|
572
|
-
expectSegment = false;
|
|
573
|
-
if (index < path.length && path[index] !== '.' && path[index] !== '[') {
|
|
574
|
-
return pathSyntaxError(path, index);
|
|
575
|
-
}
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
const start = index;
|
|
579
|
-
while (index < path.length && path[index] !== '.' && path[index] !== '[') {
|
|
580
|
-
if (path[index] === ']')
|
|
581
|
-
return pathSyntaxError(path, index);
|
|
582
|
-
index += 1;
|
|
583
|
-
}
|
|
584
|
-
if (!expectSegment || start === index)
|
|
585
|
-
return pathSyntaxError(path, start);
|
|
586
|
-
segments.push(path.slice(start, index));
|
|
587
|
-
expectSegment = false;
|
|
588
|
-
}
|
|
589
|
-
return expectSegment ? pathSyntaxError(path, path.length) : ok(segments);
|
|
590
|
-
}
|
|
591
|
-
function pathSyntaxError(path, index) {
|
|
592
|
-
return fail('construction', 'form-field-path-syntax-invalid', 'Form field paths must use dot properties and bracket indices without empty segments.', { path, index });
|
|
593
|
-
}
|
|
594
|
-
function encodeSegments(segments) {
|
|
595
|
-
return segments.map((segment, index) => (typeof segment === 'number'
|
|
596
|
-
? `[${segment}]`
|
|
597
|
-
: index === 0 ? segment : `.${segment}`)).join('');
|
|
598
|
-
}
|
|
599
|
-
function valueCollision(path) {
|
|
600
|
-
return fail('construction', 'form-value-path-collision', 'A Form value path cannot be both a leaf and a container.', { path });
|
|
601
|
-
}
|
|
602
|
-
function readContainer(container, segment) {
|
|
603
|
-
return Array.isArray(container)
|
|
604
|
-
? container[segment]
|
|
605
|
-
: container[String(segment)];
|
|
606
|
-
}
|
|
607
|
-
function hasContainer(container, segment) {
|
|
608
|
-
return Object.hasOwn(container, segment);
|
|
609
|
-
}
|
|
610
|
-
function writeContainer(container, segment, value) {
|
|
611
|
-
if (Array.isArray(container))
|
|
612
|
-
container[segment] = value;
|
|
613
|
-
else
|
|
614
|
-
container[String(segment)] = value;
|
|
615
|
-
}
|
|
616
|
-
function freezeBranches(value, branches) {
|
|
617
|
-
for (const child of Object.values(value)) {
|
|
618
|
-
if (typeof child === 'object' && child !== null && branches.has(child)) {
|
|
619
|
-
freezeBranches(child, branches);
|
|
620
|
-
}
|
|
621
|
-
else if (Array.isArray(child)) {
|
|
622
|
-
Object.freeze(child);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
Object.freeze(value);
|
|
626
|
-
}
|
|
627
|
-
//# sourceMappingURL=form.js.map
|