@petrarca/sonnet-core 0.6.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/README.md +63 -24
- package/dist/config/index.d.ts +52 -1
- package/dist/config/index.js +17 -1
- package/dist/config/index.js.map +1 -1
- package/dist/hooks/index.d.ts +17 -1
- package/dist/hooks/index.js +30 -11
- package/dist/hooks/index.js.map +1 -1
- package/dist/schema/index.d.ts +139 -5
- package/dist/schema/index.js +139 -2
- package/dist/schema/index.js.map +1 -1
- package/package.json +1 -1
package/dist/schema/index.d.ts
CHANGED
|
@@ -39,6 +39,31 @@ interface UIRule {
|
|
|
39
39
|
effect: UIRuleEffect;
|
|
40
40
|
condition: UIRuleCondition;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A named validator reference carried by `x-ui-validate`. The bare-string form
|
|
44
|
+
* is the no-config common case (`"ibanValid"`); the object form supplies
|
|
45
|
+
* per-reference config and, for async form validators, the inputs that trigger
|
|
46
|
+
* a re-run (`dependsOn`). See the custom-validation design in sonnet-forms.
|
|
47
|
+
*/
|
|
48
|
+
interface UIValidateRef {
|
|
49
|
+
/** Registered validator name, resolved against the ValidatorRegistry. */
|
|
50
|
+
name: string;
|
|
51
|
+
/** Per-reference config, passed to the validator as `ctx.args`. */
|
|
52
|
+
args?: Record<string, unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* For an async FORM validator: the field names whose values trigger a re-run.
|
|
55
|
+
* The engine keys/debounces the async result on these values. Ignored for
|
|
56
|
+
* sync validators and for field validators.
|
|
57
|
+
*/
|
|
58
|
+
dependsOn?: string[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The value of `x-ui-validate`: one validator or a list. On a property it
|
|
62
|
+
* selects field validators (`(value, ctx) => ...`); on the root object it
|
|
63
|
+
* selects form validators (`(data, ctx) => FieldError[]`). A bare string is
|
|
64
|
+
* shorthand for `{ name }`.
|
|
65
|
+
*/
|
|
66
|
+
type UIValidate = string | UIValidateRef | Array<string | UIValidateRef>;
|
|
42
67
|
/**
|
|
43
68
|
* JSON Schema property definition following Draft 2020-12 specification.
|
|
44
69
|
* Supports basic types, validation constraints, conditionals, and custom extensions.
|
|
@@ -88,11 +113,14 @@ type JsonSchemaProperty = {
|
|
|
88
113
|
"x-ui-options"?: Record<string, any>;
|
|
89
114
|
"x-ui-layout"?: UILayoutConfig;
|
|
90
115
|
"x-ui-title"?: string;
|
|
91
|
-
"x-ui-help"?: string;
|
|
92
116
|
"x-ui-placeholder"?: string;
|
|
93
117
|
"x-ui-description"?: string;
|
|
118
|
+
"x-ui-validate"?: UIValidate;
|
|
94
119
|
"x-ui-column"?: {
|
|
95
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Display order (ascending). Properties without an order are appended
|
|
122
|
+
* after the ordered ones, keeping their schema declaration order.
|
|
123
|
+
*/
|
|
96
124
|
order?: number;
|
|
97
125
|
/** Exclude this column from the table entirely. */
|
|
98
126
|
hidden?: boolean;
|
|
@@ -129,6 +157,7 @@ type JsonSchema = {
|
|
|
129
157
|
$defs?: Record<string, JsonSchemaProperty>;
|
|
130
158
|
definitions?: Record<string, JsonSchemaProperty>;
|
|
131
159
|
"x-ui-order"?: string[];
|
|
160
|
+
"x-ui-validate"?: UIValidate;
|
|
132
161
|
[key: `x-${string}`]: unknown;
|
|
133
162
|
};
|
|
134
163
|
/**
|
|
@@ -161,10 +190,11 @@ interface UISchema {
|
|
|
161
190
|
"x-ui-order"?: string[];
|
|
162
191
|
/** Declarative show/hide or enable/disable rule (see UIRule). */
|
|
163
192
|
"x-ui-rule"?: UIRule;
|
|
193
|
+
/** Custom validators by name (override of the schema-level x-ui-validate). */
|
|
194
|
+
"x-ui-validate"?: UIValidate;
|
|
164
195
|
"x-ui-title"?: string | false;
|
|
165
196
|
"x-ui-description"?: string;
|
|
166
197
|
"x-ui-placeholder"?: string;
|
|
167
|
-
"x-ui-help"?: string;
|
|
168
198
|
/** Semantic field-slot width (default "full"). */
|
|
169
199
|
"x-ui-width"?: WidthToken;
|
|
170
200
|
/** Column override -- merged over schema-level x-ui-column, UI schema wins. */
|
|
@@ -386,7 +416,14 @@ declare function isNullableBoolean(property: JsonSchemaProperty): boolean;
|
|
|
386
416
|
declare function extractEnumValues(property: JsonSchemaProperty): (string | number | boolean)[];
|
|
387
417
|
/** Priority: x-ui-title > title > field name. */
|
|
388
418
|
declare function getFieldTitle(fieldName: string, property: JsonSchemaProperty): string;
|
|
389
|
-
/**
|
|
419
|
+
/**
|
|
420
|
+
* Help text for a field, rendered under the control.
|
|
421
|
+
*
|
|
422
|
+
* `x-ui-description` overrides the standard JSON Schema `description`. There is
|
|
423
|
+
* one help-text slot per field, so a schema that only carries `description` --
|
|
424
|
+
* anything generated from a typed model, or authored in the designer's Data tab
|
|
425
|
+
* -- shows it without needing a UI hint.
|
|
426
|
+
*/
|
|
390
427
|
declare function getFieldHelpText(property: JsonSchemaProperty): string | undefined;
|
|
391
428
|
declare function getFieldPlaceholder(property: JsonSchemaProperty): string | undefined;
|
|
392
429
|
declare function getFieldWidget(property: JsonSchemaProperty): string | undefined;
|
|
@@ -531,6 +568,18 @@ declare function hasConditionals(schema: JsonSchema): boolean;
|
|
|
531
568
|
* validates that value against a schema. Evaluation yields presentation flags
|
|
532
569
|
* (visible / enabled) the renderer applies -- the field itself stays declared at
|
|
533
570
|
* its own position, so layout is stable (unlike if/then/else).
|
|
571
|
+
*
|
|
572
|
+
* Scopes are root-anchored at every depth: a rule on a nested field reads its
|
|
573
|
+
* pointer from the top of the form, so `/country` means the form's `country`
|
|
574
|
+
* even from inside `address`, and a sibling is reached by its full path
|
|
575
|
+
* (`/address/city`). This matches every other JSON Pointer in the vocabulary
|
|
576
|
+
* (`editablePaths`, validation error pointers). The renderer is responsible for
|
|
577
|
+
* passing the root form data in -- see `useRootFormData` in sonnet-forms.
|
|
578
|
+
*
|
|
579
|
+
* A consequence worth knowing when authoring inside `$defs`: a definition is
|
|
580
|
+
* reusable, but a root-anchored scope is not. A rule inside a definition that
|
|
581
|
+
* references `/country` only holds for forms that have a top-level `country`.
|
|
582
|
+
* This is inherent to root-anchoring and is the same in comparable libraries.
|
|
534
583
|
*/
|
|
535
584
|
|
|
536
585
|
/** The presentation state a rule resolves to for its target field. */
|
|
@@ -721,9 +770,69 @@ interface ValidationResult {
|
|
|
721
770
|
/**
|
|
722
771
|
* Validate form data against a JSON Schema, recursing into nested objects
|
|
723
772
|
* and array items.
|
|
773
|
+
*
|
|
774
|
+
* Two layers: this function owns required-ness and array cardinality with
|
|
775
|
+
* human-authored messages; `validateContentConstraints` adds the JSON Schema
|
|
776
|
+
* content constraints (format, pattern, type, length, range, enum, const) via
|
|
777
|
+
* AJV. Both contribute to the same `FieldError[]`, so downstream attribution
|
|
778
|
+
* (per-field `rawErrors`, per-tab error badges) is unchanged.
|
|
724
779
|
*/
|
|
725
780
|
declare function validateFormData(schema: JsonSchema, data: Record<string, unknown>): ValidationResult;
|
|
726
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Content-constraint validation, layered on top of validateFormData.
|
|
784
|
+
*
|
|
785
|
+
* validateFormData covers required-ness and array cardinality with
|
|
786
|
+
* human-authored messages. This module adds the JSON Schema *content*
|
|
787
|
+
* constraints it does not: `format`, `pattern`, `type`, `minLength` /
|
|
788
|
+
* `maxLength`, `minimum` / `maximum` (and their exclusive variants),
|
|
789
|
+
* `enum`, `const`, and `multipleOf`. It reuses the shared AJV instance
|
|
790
|
+
* (already configured with `ajv-formats`) and maps AJV's errors into the
|
|
791
|
+
* same `FieldError` shape the renderer consumes -- AJV's `instancePath` is
|
|
792
|
+
* already an RFC 6901 JSON Pointer, so per-field and per-tab attribution
|
|
793
|
+
* keep working unchanged.
|
|
794
|
+
*
|
|
795
|
+
* Required and array-cardinality keywords are intentionally excluded here so
|
|
796
|
+
* validateFormData stays the single source of those messages -- no field ever
|
|
797
|
+
* shows two errors for the same cause.
|
|
798
|
+
*/
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Validate content constraints via AJV and return them as FieldErrors.
|
|
802
|
+
*
|
|
803
|
+
* Only content keywords are returned; required and array-cardinality errors
|
|
804
|
+
* are filtered out (validateFormData owns those). Returns an empty array when
|
|
805
|
+
* the schema compiles cleanly and the data satisfies every content rule, or
|
|
806
|
+
* when AJV cannot compile the schema (in which case the caller's structural
|
|
807
|
+
* validation still stands).
|
|
808
|
+
*/
|
|
809
|
+
declare function validateContentConstraints(schema: JsonSchema, data: Record<string, unknown>): FieldError[];
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Merge FieldError lists from several validation sources into one, de-duplicated
|
|
813
|
+
* by field so a field never shows two errors for the same cause.
|
|
814
|
+
*
|
|
815
|
+
* Sources are passed in precedence order (strongest first): structural/content
|
|
816
|
+
* from validateFormData, then custom validators, then external/extra errors.
|
|
817
|
+
* The first source to flag a given field (by its pointer's root segment) wins;
|
|
818
|
+
* later sources add errors only for fields not already flagged. This extends the
|
|
819
|
+
* existing rule inside validateFormData (a content error is skipped on a field
|
|
820
|
+
* that already has a structural error) across every source.
|
|
821
|
+
*
|
|
822
|
+
* Form-level errors (pointer "") are never de-duplicated against fields -- they
|
|
823
|
+
* belong to the form, not a field -- but identical form-level messages collapse.
|
|
824
|
+
*
|
|
825
|
+
* Pure function -- no React, no side effects.
|
|
826
|
+
*/
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Merge ordered FieldError sources. Earlier sources take precedence per field.
|
|
830
|
+
*
|
|
831
|
+
* @param sources FieldError lists in precedence order (strongest first).
|
|
832
|
+
* @returns one de-duplicated list preserving each kept error's order.
|
|
833
|
+
*/
|
|
834
|
+
declare function mergeFieldErrors(...sources: FieldError[][]): FieldError[];
|
|
835
|
+
|
|
727
836
|
/**
|
|
728
837
|
* diffFormData -- structural diff between two form data snapshots.
|
|
729
838
|
*
|
|
@@ -741,4 +850,29 @@ declare function validateFormData(schema: JsonSchema, data: Record<string, unkno
|
|
|
741
850
|
*/
|
|
742
851
|
declare function diffFormData(original: Record<string, unknown>, updated: Record<string, unknown>): FormDiff;
|
|
743
852
|
|
|
744
|
-
|
|
853
|
+
/**
|
|
854
|
+
* ensureFormItemIds -- stamp a stable `_fid` onto every array-of-object item
|
|
855
|
+
* in a form data snapshot, guided by the schema.
|
|
856
|
+
*
|
|
857
|
+
* The form system matches array items across a diff by their `_fid` (see
|
|
858
|
+
* diffFormData). ArrayWidget assigns one when it renders, but that only reaches
|
|
859
|
+
* the working copy. The dirty-tracking baseline is snapshotted from the
|
|
860
|
+
* incoming `data`, which has none -- so a loaded array diffs as a wholesale
|
|
861
|
+
* replace after a single-item edit.
|
|
862
|
+
*
|
|
863
|
+
* Stamping the incoming data once, before both the working copy and the
|
|
864
|
+
* baseline are derived from it, gives the two sides the *same* ids, so
|
|
865
|
+
* identity survives the diff. Items that already carry a `_fid` keep it.
|
|
866
|
+
*
|
|
867
|
+
* Only array-of-object fields declared in the schema are touched; scalar
|
|
868
|
+
* arrays and everything else pass through unchanged. Recurses into nested
|
|
869
|
+
* objects and into the items of nested arrays.
|
|
870
|
+
*/
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Return `data` with a `_fid` on every array-of-object item the schema
|
|
874
|
+
* declares. Returns the input unchanged when nothing needed stamping.
|
|
875
|
+
*/
|
|
876
|
+
declare function ensureFormItemIds(data: Record<string, unknown>, schema: JsonSchema): Record<string, unknown>;
|
|
877
|
+
|
|
878
|
+
export { type ArrayItemChange, type FieldDiff, type FieldError, type FormDiff, type JsonSchema, type JsonSchemaProperty, type RefResolver, type RuleState, type UILayoutConfig, type UILayoutGroupConfig, type UILayoutRowConfig, type UIRule, type UIRuleCondition, type UIRuleEffect, type UISchema, type UISchemaOptions, type UIValidate, type UIValidateRef, type ValidationResult, type ValueChange, WIDTH_TOKEN_REMS, type WidthToken, ajv, applySchemaDefaults, diffFormData, ensureFormItemIds, evaluateUiRule, extractEnumValues, extractSchemaDefaults, findActiveBranch, generateJsonSchemaFromVariables, getAtPointer, getBranchLabel, getBranchValue, getDiscriminator, getFieldConstraints, getFieldDefault, getFieldHelpText, getFieldPlaceholder, getFieldTitle, getFieldType, getFieldWidget, getOptionalFields, getRequiredFields, getSchemaProperties, hasConditionals, hasProperties, isFieldOptional, isFieldRequired, isNullableBoolean, isOneOf, matchesConst, mergeFieldErrors, resolveRefs, resolveSchema, toJsonSchema, validateContentConstraints, validateFormData, validateSchema };
|
package/dist/schema/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-XAEHYRXC.js";
|
|
4
4
|
import {
|
|
5
5
|
FORM_ITEM_ID_FIELD,
|
|
6
|
+
generateId,
|
|
6
7
|
isEqual
|
|
7
8
|
} from "../chunk-XCCOEUVC.js";
|
|
8
9
|
|
|
@@ -57,7 +58,7 @@ function getFieldTitle(fieldName, property) {
|
|
|
57
58
|
return property["x-ui-title"] || property.title || fieldName;
|
|
58
59
|
}
|
|
59
60
|
function getFieldHelpText(property) {
|
|
60
|
-
return property["x-ui-description"] ?? property
|
|
61
|
+
return property["x-ui-description"] ?? property.description;
|
|
61
62
|
}
|
|
62
63
|
function getFieldPlaceholder(property) {
|
|
63
64
|
return property["x-ui-placeholder"];
|
|
@@ -146,7 +147,12 @@ var ajv = new Ajv({
|
|
|
146
147
|
strict: false,
|
|
147
148
|
validateFormats: true,
|
|
148
149
|
// Allow unknown keywords (for x-ui-widget, x-ui-title, etc.)
|
|
149
|
-
strictSchema: false
|
|
150
|
+
strictSchema: false,
|
|
151
|
+
// Enable $data references so a keyword can reference another field's value,
|
|
152
|
+
// e.g. { "const": { "$data": "1/password" } } for "must equal password".
|
|
153
|
+
// This is the declarative path for simple cross-field rules (see the
|
|
154
|
+
// custom-validation design); complex cross-field logic uses form validators.
|
|
155
|
+
$data: true
|
|
150
156
|
});
|
|
151
157
|
addFormats(ajv);
|
|
152
158
|
function resolveSchema(schema, formData) {
|
|
@@ -466,6 +472,63 @@ function resolveRefs(schema, resolve) {
|
|
|
466
472
|
return resolved;
|
|
467
473
|
}
|
|
468
474
|
|
|
475
|
+
// src/schema/contentValidation.ts
|
|
476
|
+
var OWNED_BY_VALIDATE_FORM_DATA = /* @__PURE__ */ new Set([
|
|
477
|
+
"required",
|
|
478
|
+
"minItems",
|
|
479
|
+
"maxItems"
|
|
480
|
+
]);
|
|
481
|
+
function unescapePointerToken(token) {
|
|
482
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
483
|
+
}
|
|
484
|
+
function labelForPointer(schema, pointer) {
|
|
485
|
+
if (pointer === "") return "";
|
|
486
|
+
const tokens = pointer.split("/").slice(1).map(unescapePointerToken);
|
|
487
|
+
const parts = [];
|
|
488
|
+
let node = schema;
|
|
489
|
+
for (const token of tokens) {
|
|
490
|
+
if (/^\d+$/.test(token)) {
|
|
491
|
+
if (parts.length > 0) {
|
|
492
|
+
parts[parts.length - 1] = `${parts[parts.length - 1]}[${Number(token) + 1}]`;
|
|
493
|
+
}
|
|
494
|
+
node = node?.items;
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const props = node?.properties;
|
|
498
|
+
const child = props?.[token];
|
|
499
|
+
parts.push(child ? getFieldTitle(token, child) : token);
|
|
500
|
+
node = child;
|
|
501
|
+
}
|
|
502
|
+
return parts.join(" > ");
|
|
503
|
+
}
|
|
504
|
+
function messageFor(label, keyword, message) {
|
|
505
|
+
const what = label || "Value";
|
|
506
|
+
const detail = message ?? "is invalid";
|
|
507
|
+
return `${what} ${detail}`;
|
|
508
|
+
}
|
|
509
|
+
function validateContentConstraints(schema, data) {
|
|
510
|
+
let validate;
|
|
511
|
+
try {
|
|
512
|
+
validate = ajv.compile(schema);
|
|
513
|
+
} catch {
|
|
514
|
+
return [];
|
|
515
|
+
}
|
|
516
|
+
if (validate(data)) return [];
|
|
517
|
+
const out = [];
|
|
518
|
+
const seen = /* @__PURE__ */ new Set();
|
|
519
|
+
for (const err of validate.errors ?? []) {
|
|
520
|
+
if (OWNED_BY_VALIDATE_FORM_DATA.has(err.keyword)) continue;
|
|
521
|
+
const pointer = err.instancePath;
|
|
522
|
+
const label = labelForPointer(schema, pointer);
|
|
523
|
+
const message = messageFor(label, err.keyword, err.message);
|
|
524
|
+
const key = `${pointer}\0${message}`;
|
|
525
|
+
if (seen.has(key)) continue;
|
|
526
|
+
seen.add(key);
|
|
527
|
+
out.push({ pointer, message });
|
|
528
|
+
}
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
|
|
469
532
|
// src/schema/validateFormData.ts
|
|
470
533
|
function escapePointerToken(token) {
|
|
471
534
|
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
@@ -556,6 +619,11 @@ function validateFormData(schema, data) {
|
|
|
556
619
|
{ label: "", pointer: "" },
|
|
557
620
|
fieldErrors
|
|
558
621
|
);
|
|
622
|
+
const structurallyFlagged = new Set(fieldErrors.map((e) => e.pointer));
|
|
623
|
+
for (const err of validateContentConstraints(schema, data)) {
|
|
624
|
+
if (structurallyFlagged.has(err.pointer)) continue;
|
|
625
|
+
fieldErrors.push(err);
|
|
626
|
+
}
|
|
559
627
|
return {
|
|
560
628
|
errors: fieldErrors.map((e) => e.message),
|
|
561
629
|
fieldErrors,
|
|
@@ -563,6 +631,34 @@ function validateFormData(schema, data) {
|
|
|
563
631
|
};
|
|
564
632
|
}
|
|
565
633
|
|
|
634
|
+
// src/schema/mergeFieldErrors.ts
|
|
635
|
+
function pointerRoot(pointer) {
|
|
636
|
+
const token = pointer.split("/")[1] ?? "";
|
|
637
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
638
|
+
}
|
|
639
|
+
function mergeFieldErrors(...sources) {
|
|
640
|
+
const out = [];
|
|
641
|
+
const claimedFields = /* @__PURE__ */ new Set();
|
|
642
|
+
const seenFormMessages = /* @__PURE__ */ new Set();
|
|
643
|
+
for (const source of sources) {
|
|
644
|
+
for (const err of source) {
|
|
645
|
+
if (err.pointer === "") {
|
|
646
|
+
if (seenFormMessages.has(err.message)) continue;
|
|
647
|
+
seenFormMessages.add(err.message);
|
|
648
|
+
out.push(err);
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const root = pointerRoot(err.pointer);
|
|
652
|
+
if (claimedFields.has(root)) continue;
|
|
653
|
+
out.push(err);
|
|
654
|
+
}
|
|
655
|
+
for (const err of source) {
|
|
656
|
+
if (err.pointer !== "") claimedFields.add(pointerRoot(err.pointer));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return out;
|
|
660
|
+
}
|
|
661
|
+
|
|
566
662
|
// src/schema/diffFormData.ts
|
|
567
663
|
function areBothPlainObjects(a, b) {
|
|
568
664
|
return a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b);
|
|
@@ -705,11 +801,50 @@ function diffFormData(original, updated) {
|
|
|
705
801
|
}
|
|
706
802
|
return diff;
|
|
707
803
|
}
|
|
804
|
+
|
|
805
|
+
// src/schema/ensureFormItemIds.ts
|
|
806
|
+
function isPlainObject2(v) {
|
|
807
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
808
|
+
}
|
|
809
|
+
function isObjectArray2(property) {
|
|
810
|
+
return property.type === "array" && !!property.items?.properties;
|
|
811
|
+
}
|
|
812
|
+
function stampItem(item, itemSchema) {
|
|
813
|
+
if (!isPlainObject2(item)) return item;
|
|
814
|
+
const withChildren = stampObject(item, itemSchema.properties ?? {});
|
|
815
|
+
return FORM_ITEM_ID_FIELD in withChildren ? withChildren : { ...withChildren, [FORM_ITEM_ID_FIELD]: generateId() };
|
|
816
|
+
}
|
|
817
|
+
function stampValue(value, property) {
|
|
818
|
+
if (isObjectArray2(property) && Array.isArray(value)) {
|
|
819
|
+
return value.map((item) => stampItem(item, property.items));
|
|
820
|
+
}
|
|
821
|
+
if (property.type === "object" && property.properties && isPlainObject2(value)) {
|
|
822
|
+
return stampObject(value, property.properties);
|
|
823
|
+
}
|
|
824
|
+
return value;
|
|
825
|
+
}
|
|
826
|
+
function stampObject(data, properties) {
|
|
827
|
+
let changed = false;
|
|
828
|
+
const out = { ...data };
|
|
829
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
830
|
+
if (!(name in data)) continue;
|
|
831
|
+
const next = stampValue(data[name], property);
|
|
832
|
+
if (next !== data[name]) {
|
|
833
|
+
out[name] = next;
|
|
834
|
+
changed = true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return changed ? out : data;
|
|
838
|
+
}
|
|
839
|
+
function ensureFormItemIds(data, schema) {
|
|
840
|
+
return stampObject(data, getSchemaProperties(schema));
|
|
841
|
+
}
|
|
708
842
|
export {
|
|
709
843
|
WIDTH_TOKEN_REMS,
|
|
710
844
|
ajv,
|
|
711
845
|
applySchemaDefaults,
|
|
712
846
|
diffFormData,
|
|
847
|
+
ensureFormItemIds,
|
|
713
848
|
evaluateUiRule,
|
|
714
849
|
extractEnumValues,
|
|
715
850
|
extractSchemaDefaults,
|
|
@@ -736,9 +871,11 @@ export {
|
|
|
736
871
|
isNullableBoolean,
|
|
737
872
|
isOneOf,
|
|
738
873
|
matchesConst,
|
|
874
|
+
mergeFieldErrors,
|
|
739
875
|
resolveRefs,
|
|
740
876
|
resolveSchema,
|
|
741
877
|
toJsonSchema,
|
|
878
|
+
validateContentConstraints,
|
|
742
879
|
validateFormData,
|
|
743
880
|
validateSchema
|
|
744
881
|
};
|