@form-engine-ts/core 2.0.0 → 2.1.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 +9 -0
- package/dist/index.cjs +218 -7
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +217 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,11 +38,20 @@ Forms, pages, fields, options, and submissions accept JSON-only `metadata` and p
|
|
|
38
38
|
`translationMetadata`. These extension values survive sanitization, localization, submission creation, and storage
|
|
39
39
|
round-trips. `completionMessage` is localized with the rest of the form text.
|
|
40
40
|
|
|
41
|
+
`transformFieldType` changes a question's type without discarding source text, translations, conditions, or extension
|
|
42
|
+
metadata. `validateFormSchema(schema, { policy })` applies the framework-independent `FormPolicy`, including field,
|
|
43
|
+
option, text, serialized-byte, allowed-type, and required-locale constraints. Required locales cover every source text
|
|
44
|
+
that exists on the form, its fields, options, and pages.
|
|
45
|
+
|
|
46
|
+
Translation callbacks receive `nodeMetadata` and `existingTranslationMetadata` separately. The deprecated `metadata`
|
|
47
|
+
slot property remains an alias for `nodeMetadata` during migration.
|
|
48
|
+
|
|
41
49
|
`calculateCrossTabulation` builds a two-question frequency matrix from submissions. `dispatchWebhook` posts typed
|
|
42
50
|
`response.submitted` or `schema.updated` events with timeout handling, custom headers, and optional HMAC-SHA256 signing.
|
|
43
51
|
|
|
44
52
|
CSV export neutralizes string cells whose first non-whitespace character is `=`, `+`, `-`, or `@`. This is enabled by
|
|
45
53
|
default; trusted callers can pass `{ neutralizeFormulas: false }`. RFC 4180 quoting and the UTF-8 BOM remain unchanged.
|
|
54
|
+
The columns are exactly `submissionId`, `submittedAt`, `locale`, followed by one column per field in schema order.
|
|
46
55
|
|
|
47
56
|
Storage adapters share inclusive ISO 8601 submission-range filtering:
|
|
48
57
|
|
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
resolveLocalizedSchema: () => resolveLocalizedSchema,
|
|
39
39
|
sanitizeSchema: () => sanitizeSchema,
|
|
40
40
|
selectVisibleAnswers: () => selectVisibleAnswers,
|
|
41
|
+
transformFieldType: () => transformFieldType,
|
|
41
42
|
validateAnswers: () => validateAnswers,
|
|
42
43
|
validateFormSchema: () => validateFormSchema,
|
|
43
44
|
validatePageAnswers: () => validatePageAnswers,
|
|
@@ -420,7 +421,151 @@ function validateField(value, path, issues) {
|
|
|
420
421
|
}
|
|
421
422
|
return true;
|
|
422
423
|
}
|
|
423
|
-
function
|
|
424
|
+
function collectSchemaText(schema) {
|
|
425
|
+
const entries = [{ path: "title", value: schema.title }];
|
|
426
|
+
if (schema.description !== void 0) entries.push({ path: "description", value: schema.description });
|
|
427
|
+
if (schema.completionMessage !== void 0)
|
|
428
|
+
entries.push({ path: "completionMessage", value: schema.completionMessage });
|
|
429
|
+
for (const [locale, translation] of Object.entries(schema.translations ?? {})) {
|
|
430
|
+
for (const property of ["title", "description", "completionMessage"]) {
|
|
431
|
+
const value = translation[property];
|
|
432
|
+
if (value !== void 0) entries.push({ path: `translations.${locale}.${property}`, value });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
436
|
+
entries.push({ path: `fields[${fieldIndex}].title`, value: field.title });
|
|
437
|
+
if (field.description !== void 0)
|
|
438
|
+
entries.push({ path: `fields[${fieldIndex}].description`, value: field.description });
|
|
439
|
+
for (const [locale, translation] of Object.entries(field.translations ?? {})) {
|
|
440
|
+
for (const property of ["title", "description"]) {
|
|
441
|
+
const value = translation[property];
|
|
442
|
+
if (value !== void 0)
|
|
443
|
+
entries.push({ path: `fields[${fieldIndex}].translations.${locale}.${property}`, value });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!("options" in field)) return;
|
|
447
|
+
field.options.forEach((option, optionIndex) => {
|
|
448
|
+
entries.push({ path: `fields[${fieldIndex}].options[${optionIndex}].label`, value: option.label });
|
|
449
|
+
for (const [locale, value] of Object.entries(option.translations ?? {})) {
|
|
450
|
+
entries.push({ path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`, value });
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
schema.pages?.forEach((page, pageIndex) => {
|
|
455
|
+
if (page.title !== void 0) entries.push({ path: `pages[${pageIndex}].title`, value: page.title });
|
|
456
|
+
if (page.description !== void 0)
|
|
457
|
+
entries.push({ path: `pages[${pageIndex}].description`, value: page.description });
|
|
458
|
+
for (const [locale, translation] of Object.entries(page.translations ?? {})) {
|
|
459
|
+
for (const property of ["title", "description"]) {
|
|
460
|
+
const value = translation[property];
|
|
461
|
+
if (value !== void 0)
|
|
462
|
+
entries.push({ path: `pages[${pageIndex}].translations.${locale}.${property}`, value });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
return entries;
|
|
467
|
+
}
|
|
468
|
+
function addRequiredTranslationIssues(schema, locale, issues) {
|
|
469
|
+
if (!(schema.supportedLocales ?? []).includes(locale)) {
|
|
470
|
+
issue(issues, "supportedLocales", "required_locale_missing", `Required locale ${locale} is missing.`);
|
|
471
|
+
}
|
|
472
|
+
if (locale === schema.defaultLocale) return;
|
|
473
|
+
const required = [
|
|
474
|
+
{ path: `translations.${locale}.title`, value: schema.translations?.[locale]?.title }
|
|
475
|
+
];
|
|
476
|
+
if (schema.description !== void 0)
|
|
477
|
+
required.push({ path: `translations.${locale}.description`, value: schema.translations?.[locale]?.description });
|
|
478
|
+
if (schema.completionMessage !== void 0) {
|
|
479
|
+
required.push({
|
|
480
|
+
path: `translations.${locale}.completionMessage`,
|
|
481
|
+
value: schema.translations?.[locale]?.completionMessage
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
485
|
+
required.push({
|
|
486
|
+
path: `fields[${fieldIndex}].translations.${locale}.title`,
|
|
487
|
+
value: field.translations?.[locale]?.title
|
|
488
|
+
});
|
|
489
|
+
if (field.description !== void 0) {
|
|
490
|
+
required.push({
|
|
491
|
+
path: `fields[${fieldIndex}].translations.${locale}.description`,
|
|
492
|
+
value: field.translations?.[locale]?.description
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (!("options" in field)) return;
|
|
496
|
+
field.options.forEach((option, optionIndex) => {
|
|
497
|
+
required.push({
|
|
498
|
+
path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`,
|
|
499
|
+
value: option.translations?.[locale]
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
schema.pages?.forEach((page, pageIndex) => {
|
|
504
|
+
if (page.title !== void 0) {
|
|
505
|
+
required.push({
|
|
506
|
+
path: `pages[${pageIndex}].translations.${locale}.title`,
|
|
507
|
+
value: page.translations?.[locale]?.title
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
if (page.description !== void 0) {
|
|
511
|
+
required.push({
|
|
512
|
+
path: `pages[${pageIndex}].translations.${locale}.description`,
|
|
513
|
+
value: page.translations?.[locale]?.description
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
for (const translation of required) {
|
|
518
|
+
if (translation.value === void 0 || translation.value.trim().length === 0) {
|
|
519
|
+
issue(
|
|
520
|
+
issues,
|
|
521
|
+
translation.path,
|
|
522
|
+
"required_translation_missing",
|
|
523
|
+
`A translation for required locale ${locale} is missing.`
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function validatePolicy(schema, policy, issues) {
|
|
529
|
+
if (policy.maxFields !== void 0 && schema.fields.length > policy.maxFields) {
|
|
530
|
+
issue(issues, "fields", "max_fields_exceeded", `At most ${policy.maxFields} fields are allowed.`);
|
|
531
|
+
}
|
|
532
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
533
|
+
if (policy.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(field.type)) {
|
|
534
|
+
issue(issues, `fields[${fieldIndex}].type`, "disallowed_field_type", `Field type ${field.type} is not allowed.`);
|
|
535
|
+
}
|
|
536
|
+
if (policy.maxOptionsPerField !== void 0 && "options" in field && field.options.length > policy.maxOptionsPerField) {
|
|
537
|
+
issue(
|
|
538
|
+
issues,
|
|
539
|
+
`fields[${fieldIndex}].options`,
|
|
540
|
+
"max_options_exceeded",
|
|
541
|
+
`At most ${policy.maxOptionsPerField} options are allowed.`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
if (policy.maxTextLength !== void 0) {
|
|
546
|
+
for (const entry of collectSchemaText(schema)) {
|
|
547
|
+
if (entry.value.length > policy.maxTextLength) {
|
|
548
|
+
issue(
|
|
549
|
+
issues,
|
|
550
|
+
entry.path,
|
|
551
|
+
"max_text_length_exceeded",
|
|
552
|
+
`Text must be at most ${policy.maxTextLength} characters.`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
for (const locale of policy.requiredLocales ?? []) addRequiredTranslationIssues(schema, locale, issues);
|
|
558
|
+
if (policy.maxSchemaBytes !== void 0) {
|
|
559
|
+
try {
|
|
560
|
+
const byteLength = new TextEncoder().encode(JSON.stringify(schema)).byteLength;
|
|
561
|
+
if (byteLength > policy.maxSchemaBytes) {
|
|
562
|
+
issue(issues, "$", "max_schema_bytes_exceeded", `Schema must be at most ${policy.maxSchemaBytes} bytes.`);
|
|
563
|
+
}
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function validateFormSchema(input, options = {}) {
|
|
424
569
|
const issues = [];
|
|
425
570
|
if (!isRecord(input)) {
|
|
426
571
|
return { valid: false, issues: [{ path: "$", code: "invalid_schema", message: "Expected a schema object." }] };
|
|
@@ -566,6 +711,14 @@ function validateFormSchema(input) {
|
|
|
566
711
|
}
|
|
567
712
|
}
|
|
568
713
|
}
|
|
714
|
+
if (Array.isArray(input.fields)) {
|
|
715
|
+
const policyIssues = [];
|
|
716
|
+
try {
|
|
717
|
+
validatePolicy(input, options.policy ?? {}, policyIssues);
|
|
718
|
+
issues.push(...policyIssues);
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
}
|
|
569
722
|
return issues.length === 0 ? { valid: true, value: input, issues: [] } : { valid: false, issues };
|
|
570
723
|
}
|
|
571
724
|
function assertValidFormSchema(input) {
|
|
@@ -874,6 +1027,58 @@ async function dispatchWebhook(event, config, fetchImpl = globalThis.fetch) {
|
|
|
874
1027
|
}
|
|
875
1028
|
}
|
|
876
1029
|
|
|
1030
|
+
// src/field.ts
|
|
1031
|
+
var DEFAULT_OPTION = { id: "option-1", label: "Option 1" };
|
|
1032
|
+
function transformFieldType(field, nextType) {
|
|
1033
|
+
const common = {
|
|
1034
|
+
id: field.id,
|
|
1035
|
+
title: field.title,
|
|
1036
|
+
required: field.required,
|
|
1037
|
+
...field.description === void 0 ? {} : { description: field.description },
|
|
1038
|
+
...field.translationKey === void 0 ? {} : { translationKey: field.translationKey },
|
|
1039
|
+
...field.messages === void 0 ? {} : { messages: field.messages },
|
|
1040
|
+
...field.displayCondition === void 0 ? {} : { displayCondition: field.displayCondition },
|
|
1041
|
+
...field.translations === void 0 ? {} : { translations: field.translations },
|
|
1042
|
+
...field.metadata === void 0 ? {} : { metadata: field.metadata },
|
|
1043
|
+
...field.translationMetadata === void 0 ? {} : { translationMetadata: field.translationMetadata }
|
|
1044
|
+
};
|
|
1045
|
+
if (nextType === "text" || nextType === "textarea") {
|
|
1046
|
+
const textProperties = field.type === "text" || field.type === "textarea" ? {
|
|
1047
|
+
...field.placeholderKey === void 0 ? {} : { placeholderKey: field.placeholderKey },
|
|
1048
|
+
...field.minLength === void 0 ? {} : { minLength: field.minLength },
|
|
1049
|
+
...field.maxLength === void 0 ? {} : { maxLength: field.maxLength },
|
|
1050
|
+
...field.pattern === void 0 ? {} : { pattern: field.pattern }
|
|
1051
|
+
} : {};
|
|
1052
|
+
return { ...common, ...textProperties, type: nextType };
|
|
1053
|
+
}
|
|
1054
|
+
if (nextType === "number") {
|
|
1055
|
+
const numberProperties = field.type === "number" ? {
|
|
1056
|
+
...field.placeholderKey === void 0 ? {} : { placeholderKey: field.placeholderKey },
|
|
1057
|
+
...field.min === void 0 ? {} : { min: field.min },
|
|
1058
|
+
...field.max === void 0 ? {} : { max: field.max },
|
|
1059
|
+
...field.step === void 0 ? {} : { step: field.step }
|
|
1060
|
+
} : {};
|
|
1061
|
+
return { ...common, ...numberProperties, type: nextType };
|
|
1062
|
+
}
|
|
1063
|
+
if (nextType === "rating") {
|
|
1064
|
+
const ratingProperties = field.type === "rating" ? {
|
|
1065
|
+
...field.min === void 0 ? {} : { min: field.min },
|
|
1066
|
+
...field.max === void 0 ? {} : { max: field.max }
|
|
1067
|
+
} : { min: 1, max: 5 };
|
|
1068
|
+
return { ...common, ...ratingProperties, type: nextType };
|
|
1069
|
+
}
|
|
1070
|
+
if (nextType === "checkbox") return { ...common, type: nextType };
|
|
1071
|
+
const options = "options" in field && field.options.length > 0 ? field.options : [DEFAULT_OPTION];
|
|
1072
|
+
if (nextType === "multi-select") {
|
|
1073
|
+
const selectionProperties = field.type === "multi-select" ? {
|
|
1074
|
+
...field.minSelections === void 0 ? {} : { minSelections: field.minSelections },
|
|
1075
|
+
...field.maxSelections === void 0 ? {} : { maxSelections: field.maxSelections }
|
|
1076
|
+
} : {};
|
|
1077
|
+
return { ...common, ...selectionProperties, type: nextType, options };
|
|
1078
|
+
}
|
|
1079
|
+
return { ...common, type: nextType, options };
|
|
1080
|
+
}
|
|
1081
|
+
|
|
877
1082
|
// src/validation.ts
|
|
878
1083
|
var DEFAULT_MESSAGES = {
|
|
879
1084
|
required: "validation.required",
|
|
@@ -1062,7 +1267,7 @@ function withTranslationMetadata(node, locale, property, metadata) {
|
|
|
1062
1267
|
}
|
|
1063
1268
|
};
|
|
1064
1269
|
}
|
|
1065
|
-
function createSlot(kind, nodeId, property, locale, sourceText, existingText,
|
|
1270
|
+
function createSlot(kind, nodeId, property, locale, sourceText, existingText, nodeMetadata, existingTranslationMetadata) {
|
|
1066
1271
|
return {
|
|
1067
1272
|
kind,
|
|
1068
1273
|
nodeId,
|
|
@@ -1070,7 +1275,8 @@ function createSlot(kind, nodeId, property, locale, sourceText, existingText, me
|
|
|
1070
1275
|
locale,
|
|
1071
1276
|
sourceText,
|
|
1072
1277
|
...existingText === void 0 ? {} : { existingText },
|
|
1073
|
-
...
|
|
1278
|
+
...nodeMetadata === void 0 ? {} : { nodeMetadata, metadata: nodeMetadata },
|
|
1279
|
+
...existingTranslationMetadata === void 0 ? {} : { existingTranslationMetadata }
|
|
1074
1280
|
};
|
|
1075
1281
|
}
|
|
1076
1282
|
function translationSlots(schema, locale) {
|
|
@@ -1083,7 +1289,8 @@ function translationSlots(schema, locale) {
|
|
|
1083
1289
|
locale,
|
|
1084
1290
|
sourceText,
|
|
1085
1291
|
schema.translations?.[locale]?.[property],
|
|
1086
|
-
schema.metadata
|
|
1292
|
+
schema.metadata,
|
|
1293
|
+
schema.translationMetadata?.[locale]?.[property]
|
|
1087
1294
|
);
|
|
1088
1295
|
descriptors.push({
|
|
1089
1296
|
slot,
|
|
@@ -1107,7 +1314,8 @@ function translationSlots(schema, locale) {
|
|
|
1107
1314
|
locale,
|
|
1108
1315
|
sourceText,
|
|
1109
1316
|
field.translations?.[locale]?.[property],
|
|
1110
|
-
field.metadata
|
|
1317
|
+
field.metadata,
|
|
1318
|
+
field.translationMetadata?.[locale]?.[property]
|
|
1111
1319
|
);
|
|
1112
1320
|
descriptors.push({
|
|
1113
1321
|
slot,
|
|
@@ -1138,7 +1346,8 @@ function translationSlots(schema, locale) {
|
|
|
1138
1346
|
locale,
|
|
1139
1347
|
option.label,
|
|
1140
1348
|
option.translations?.[locale],
|
|
1141
|
-
option.metadata
|
|
1349
|
+
option.metadata,
|
|
1350
|
+
option.translationMetadata?.[locale]?.label
|
|
1142
1351
|
);
|
|
1143
1352
|
descriptors.push({
|
|
1144
1353
|
slot,
|
|
@@ -1175,7 +1384,8 @@ function translationSlots(schema, locale) {
|
|
|
1175
1384
|
locale,
|
|
1176
1385
|
sourceText,
|
|
1177
1386
|
page.translations?.[locale]?.[property],
|
|
1178
|
-
page.metadata
|
|
1387
|
+
page.metadata,
|
|
1388
|
+
page.translationMetadata?.[locale]?.[property]
|
|
1179
1389
|
);
|
|
1180
1390
|
descriptors.push({
|
|
1181
1391
|
slot,
|
|
@@ -1312,6 +1522,7 @@ async function resolveFormTranslation(schema, adapter, targetLocale, sourceLocal
|
|
|
1312
1522
|
resolveLocalizedSchema,
|
|
1313
1523
|
sanitizeSchema,
|
|
1314
1524
|
selectVisibleAnswers,
|
|
1525
|
+
transformFieldType,
|
|
1315
1526
|
validateAnswers,
|
|
1316
1527
|
validateFormSchema,
|
|
1317
1528
|
validatePageAnswers,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
type FieldType = "text" | "textarea" | "number" | "rating" | "select" | "multi-select" | "checkbox" | "radio";
|
|
2
|
+
interface FormPolicy {
|
|
3
|
+
readonly allowedFieldTypes?: readonly FieldType[];
|
|
4
|
+
readonly maxFields?: number;
|
|
5
|
+
readonly maxOptionsPerField?: number;
|
|
6
|
+
readonly requiredLocales?: readonly string[];
|
|
7
|
+
readonly maxTextLength?: number;
|
|
8
|
+
readonly maxSchemaBytes?: number;
|
|
9
|
+
}
|
|
2
10
|
type ConditionOperator = "equals" | "not_equals" | "contains" | "not_empty";
|
|
3
11
|
type ConditionValue = string | number | boolean;
|
|
4
12
|
type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
|
|
@@ -252,6 +260,12 @@ interface WebhookDispatchResult {
|
|
|
252
260
|
}
|
|
253
261
|
declare function dispatchWebhook<T>(event: FormEvent<T>, config: WebhookConfig, fetchImpl?: typeof fetch): Promise<WebhookDispatchResult>;
|
|
254
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Changes only the type-specific shape of a field. Authoring content and extension
|
|
265
|
+
* data are deliberately retained so UI adapters cannot accidentally discard them.
|
|
266
|
+
*/
|
|
267
|
+
declare function transformFieldType(field: FormField, nextType: QuestionType): FormField;
|
|
268
|
+
|
|
255
269
|
type SchemaStructureIssueType = "dangling_condition_reference" | "duplicate_question_id" | "duplicate_choice_id" | "self_condition_reference" | "cyclic_condition_reference";
|
|
256
270
|
interface SchemaStructureIssue {
|
|
257
271
|
readonly type: SchemaStructureIssueType;
|
|
@@ -262,7 +276,10 @@ interface SchemaStructureIssue {
|
|
|
262
276
|
declare function validateSchemaStructure(schema: FormSchema): SchemaStructureIssue[];
|
|
263
277
|
declare function sanitizeSchema(schema: FormSchema): FormSchema;
|
|
264
278
|
|
|
265
|
-
|
|
279
|
+
interface ValidateFormSchemaOptions {
|
|
280
|
+
readonly policy?: FormPolicy;
|
|
281
|
+
}
|
|
282
|
+
declare function validateFormSchema(input: unknown, options?: ValidateFormSchemaOptions): SchemaValidationResult;
|
|
266
283
|
declare function assertValidFormSchema(input: unknown): asserts input is FormSchema;
|
|
267
284
|
|
|
268
285
|
interface CreateSubmissionOptions extends ExtensibleNode {
|
|
@@ -279,6 +296,9 @@ interface TranslationSlot {
|
|
|
279
296
|
readonly locale: string;
|
|
280
297
|
readonly sourceText: string;
|
|
281
298
|
readonly existingText?: string;
|
|
299
|
+
readonly nodeMetadata?: Readonly<Record<string, JsonValue>>;
|
|
300
|
+
readonly existingTranslationMetadata?: Readonly<Record<string, JsonValue>>;
|
|
301
|
+
/** @deprecated Use nodeMetadata instead. */
|
|
282
302
|
readonly metadata?: Readonly<Record<string, JsonValue>>;
|
|
283
303
|
}
|
|
284
304
|
interface PopulateTranslationOptions {
|
|
@@ -306,4 +326,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
|
|
|
306
326
|
declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
|
|
307
327
|
declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
|
|
308
328
|
|
|
309
|
-
export { type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvExportOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type JsonValue, type LocalizedText, type MultiSelectField, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PopulateTranslationOptions, type Question, type QuestionAggregate, type QuestionType, type RatingField, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidationCode, type ValidationError, type ValidationIssue, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, createSubmission, dispatchWebhook, escapeCsvCell, exportResponsesToCsv, isDisplayConditionSatisfied, isQuestionVisible, populateSchemaTranslations, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
|
329
|
+
export { type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvExportOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type JsonValue, type LocalizedText, type MultiSelectField, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PopulateTranslationOptions, type Question, type QuestionAggregate, type QuestionType, type RatingField, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, createSubmission, dispatchWebhook, escapeCsvCell, exportResponsesToCsv, isDisplayConditionSatisfied, isQuestionVisible, populateSchemaTranslations, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
type FieldType = "text" | "textarea" | "number" | "rating" | "select" | "multi-select" | "checkbox" | "radio";
|
|
2
|
+
interface FormPolicy {
|
|
3
|
+
readonly allowedFieldTypes?: readonly FieldType[];
|
|
4
|
+
readonly maxFields?: number;
|
|
5
|
+
readonly maxOptionsPerField?: number;
|
|
6
|
+
readonly requiredLocales?: readonly string[];
|
|
7
|
+
readonly maxTextLength?: number;
|
|
8
|
+
readonly maxSchemaBytes?: number;
|
|
9
|
+
}
|
|
2
10
|
type ConditionOperator = "equals" | "not_equals" | "contains" | "not_empty";
|
|
3
11
|
type ConditionValue = string | number | boolean;
|
|
4
12
|
type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
|
|
@@ -252,6 +260,12 @@ interface WebhookDispatchResult {
|
|
|
252
260
|
}
|
|
253
261
|
declare function dispatchWebhook<T>(event: FormEvent<T>, config: WebhookConfig, fetchImpl?: typeof fetch): Promise<WebhookDispatchResult>;
|
|
254
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Changes only the type-specific shape of a field. Authoring content and extension
|
|
265
|
+
* data are deliberately retained so UI adapters cannot accidentally discard them.
|
|
266
|
+
*/
|
|
267
|
+
declare function transformFieldType(field: FormField, nextType: QuestionType): FormField;
|
|
268
|
+
|
|
255
269
|
type SchemaStructureIssueType = "dangling_condition_reference" | "duplicate_question_id" | "duplicate_choice_id" | "self_condition_reference" | "cyclic_condition_reference";
|
|
256
270
|
interface SchemaStructureIssue {
|
|
257
271
|
readonly type: SchemaStructureIssueType;
|
|
@@ -262,7 +276,10 @@ interface SchemaStructureIssue {
|
|
|
262
276
|
declare function validateSchemaStructure(schema: FormSchema): SchemaStructureIssue[];
|
|
263
277
|
declare function sanitizeSchema(schema: FormSchema): FormSchema;
|
|
264
278
|
|
|
265
|
-
|
|
279
|
+
interface ValidateFormSchemaOptions {
|
|
280
|
+
readonly policy?: FormPolicy;
|
|
281
|
+
}
|
|
282
|
+
declare function validateFormSchema(input: unknown, options?: ValidateFormSchemaOptions): SchemaValidationResult;
|
|
266
283
|
declare function assertValidFormSchema(input: unknown): asserts input is FormSchema;
|
|
267
284
|
|
|
268
285
|
interface CreateSubmissionOptions extends ExtensibleNode {
|
|
@@ -279,6 +296,9 @@ interface TranslationSlot {
|
|
|
279
296
|
readonly locale: string;
|
|
280
297
|
readonly sourceText: string;
|
|
281
298
|
readonly existingText?: string;
|
|
299
|
+
readonly nodeMetadata?: Readonly<Record<string, JsonValue>>;
|
|
300
|
+
readonly existingTranslationMetadata?: Readonly<Record<string, JsonValue>>;
|
|
301
|
+
/** @deprecated Use nodeMetadata instead. */
|
|
282
302
|
readonly metadata?: Readonly<Record<string, JsonValue>>;
|
|
283
303
|
}
|
|
284
304
|
interface PopulateTranslationOptions {
|
|
@@ -306,4 +326,4 @@ declare function calculatePageVisibility(schema: FormSchema, currentAnswers: Rea
|
|
|
306
326
|
declare function calculateFieldVisibility(schema: FormSchema, currentAnswers: Readonly<Record<string, unknown>>): Readonly<Record<string, boolean>>;
|
|
307
327
|
declare function selectVisibleAnswers(schema: FormSchema, currentAnswers: FormValues): FormValues;
|
|
308
328
|
|
|
309
|
-
export { type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvExportOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type JsonValue, type LocalizedText, type MultiSelectField, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PopulateTranslationOptions, type Question, type QuestionAggregate, type QuestionType, type RatingField, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidationCode, type ValidationError, type ValidationIssue, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, createSubmission, dispatchWebhook, escapeCsvCell, exportResponsesToCsv, isDisplayConditionSatisfied, isQuestionVisible, populateSchemaTranslations, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
|
329
|
+
export { type AnswerValidationResult, type AsyncTranslationAdapter, type BaseField, type CheckboxField, type CheckboxQuestionAggregate, type ChoiceDistributionEntry, type ChoiceOption, type ChoiceQuestionAggregate, type ConditionOperator, type ConditionValue, type CreateSubmissionOptions, type CrossTabulationResult, type CsvExportOptions, type DisplayCondition, type ExtensibleNode, type FieldOption, type FieldType, type FormAnalytics, type FormEvent, type FormEventType, type FormField, type FormPage, type FormPolicy, type FormResponse, type FormSchema, type FormStorageAdapter, type FormSubmission, type FormValue, type FormValues, type JsonValue, type LocalizedText, type MultiSelectField, type NumberField, type NumberQuestionAggregate, type NumericSummary, type OptionAggregate, type PopulateTranslationOptions, type Question, type QuestionAggregate, type QuestionType, type RatingField, type SchemaIssue, type SchemaStructureIssue, type SchemaStructureIssueType, type SchemaTranslations, type SchemaValidationResult, type SelectField, type StorageAdapter, type SubmissionQueryOptions, type TextField, type TextQuestionAggregate, type TranslationAdapter, type TranslationReport, type TranslationSlot, type ValidateFormSchemaOptions, type ValidationCode, type ValidationError, type ValidationIssue, type WebhookConfig, type WebhookDispatchResult, aggregateResponses, assertValidFormSchema, calculateChoiceDistribution, calculateCrossTabulation, calculateFieldVisibility, calculateNumericSummary, calculatePageVisibility, createSubmission, dispatchWebhook, escapeCsvCell, exportResponsesToCsv, isDisplayConditionSatisfied, isQuestionVisible, populateSchemaTranslations, resolveFormTranslation, resolveLocalizedSchema, sanitizeSchema, selectVisibleAnswers, transformFieldType, validateAnswers, validateFormSchema, validatePageAnswers, validateSchemaStructure };
|
package/dist/index.js
CHANGED
|
@@ -373,7 +373,151 @@ function validateField(value, path, issues) {
|
|
|
373
373
|
}
|
|
374
374
|
return true;
|
|
375
375
|
}
|
|
376
|
-
function
|
|
376
|
+
function collectSchemaText(schema) {
|
|
377
|
+
const entries = [{ path: "title", value: schema.title }];
|
|
378
|
+
if (schema.description !== void 0) entries.push({ path: "description", value: schema.description });
|
|
379
|
+
if (schema.completionMessage !== void 0)
|
|
380
|
+
entries.push({ path: "completionMessage", value: schema.completionMessage });
|
|
381
|
+
for (const [locale, translation] of Object.entries(schema.translations ?? {})) {
|
|
382
|
+
for (const property of ["title", "description", "completionMessage"]) {
|
|
383
|
+
const value = translation[property];
|
|
384
|
+
if (value !== void 0) entries.push({ path: `translations.${locale}.${property}`, value });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
388
|
+
entries.push({ path: `fields[${fieldIndex}].title`, value: field.title });
|
|
389
|
+
if (field.description !== void 0)
|
|
390
|
+
entries.push({ path: `fields[${fieldIndex}].description`, value: field.description });
|
|
391
|
+
for (const [locale, translation] of Object.entries(field.translations ?? {})) {
|
|
392
|
+
for (const property of ["title", "description"]) {
|
|
393
|
+
const value = translation[property];
|
|
394
|
+
if (value !== void 0)
|
|
395
|
+
entries.push({ path: `fields[${fieldIndex}].translations.${locale}.${property}`, value });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (!("options" in field)) return;
|
|
399
|
+
field.options.forEach((option, optionIndex) => {
|
|
400
|
+
entries.push({ path: `fields[${fieldIndex}].options[${optionIndex}].label`, value: option.label });
|
|
401
|
+
for (const [locale, value] of Object.entries(option.translations ?? {})) {
|
|
402
|
+
entries.push({ path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`, value });
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
schema.pages?.forEach((page, pageIndex) => {
|
|
407
|
+
if (page.title !== void 0) entries.push({ path: `pages[${pageIndex}].title`, value: page.title });
|
|
408
|
+
if (page.description !== void 0)
|
|
409
|
+
entries.push({ path: `pages[${pageIndex}].description`, value: page.description });
|
|
410
|
+
for (const [locale, translation] of Object.entries(page.translations ?? {})) {
|
|
411
|
+
for (const property of ["title", "description"]) {
|
|
412
|
+
const value = translation[property];
|
|
413
|
+
if (value !== void 0)
|
|
414
|
+
entries.push({ path: `pages[${pageIndex}].translations.${locale}.${property}`, value });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
return entries;
|
|
419
|
+
}
|
|
420
|
+
function addRequiredTranslationIssues(schema, locale, issues) {
|
|
421
|
+
if (!(schema.supportedLocales ?? []).includes(locale)) {
|
|
422
|
+
issue(issues, "supportedLocales", "required_locale_missing", `Required locale ${locale} is missing.`);
|
|
423
|
+
}
|
|
424
|
+
if (locale === schema.defaultLocale) return;
|
|
425
|
+
const required = [
|
|
426
|
+
{ path: `translations.${locale}.title`, value: schema.translations?.[locale]?.title }
|
|
427
|
+
];
|
|
428
|
+
if (schema.description !== void 0)
|
|
429
|
+
required.push({ path: `translations.${locale}.description`, value: schema.translations?.[locale]?.description });
|
|
430
|
+
if (schema.completionMessage !== void 0) {
|
|
431
|
+
required.push({
|
|
432
|
+
path: `translations.${locale}.completionMessage`,
|
|
433
|
+
value: schema.translations?.[locale]?.completionMessage
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
437
|
+
required.push({
|
|
438
|
+
path: `fields[${fieldIndex}].translations.${locale}.title`,
|
|
439
|
+
value: field.translations?.[locale]?.title
|
|
440
|
+
});
|
|
441
|
+
if (field.description !== void 0) {
|
|
442
|
+
required.push({
|
|
443
|
+
path: `fields[${fieldIndex}].translations.${locale}.description`,
|
|
444
|
+
value: field.translations?.[locale]?.description
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (!("options" in field)) return;
|
|
448
|
+
field.options.forEach((option, optionIndex) => {
|
|
449
|
+
required.push({
|
|
450
|
+
path: `fields[${fieldIndex}].options[${optionIndex}].translations.${locale}`,
|
|
451
|
+
value: option.translations?.[locale]
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
schema.pages?.forEach((page, pageIndex) => {
|
|
456
|
+
if (page.title !== void 0) {
|
|
457
|
+
required.push({
|
|
458
|
+
path: `pages[${pageIndex}].translations.${locale}.title`,
|
|
459
|
+
value: page.translations?.[locale]?.title
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (page.description !== void 0) {
|
|
463
|
+
required.push({
|
|
464
|
+
path: `pages[${pageIndex}].translations.${locale}.description`,
|
|
465
|
+
value: page.translations?.[locale]?.description
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
for (const translation of required) {
|
|
470
|
+
if (translation.value === void 0 || translation.value.trim().length === 0) {
|
|
471
|
+
issue(
|
|
472
|
+
issues,
|
|
473
|
+
translation.path,
|
|
474
|
+
"required_translation_missing",
|
|
475
|
+
`A translation for required locale ${locale} is missing.`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function validatePolicy(schema, policy, issues) {
|
|
481
|
+
if (policy.maxFields !== void 0 && schema.fields.length > policy.maxFields) {
|
|
482
|
+
issue(issues, "fields", "max_fields_exceeded", `At most ${policy.maxFields} fields are allowed.`);
|
|
483
|
+
}
|
|
484
|
+
schema.fields.forEach((field, fieldIndex) => {
|
|
485
|
+
if (policy.allowedFieldTypes !== void 0 && !policy.allowedFieldTypes.includes(field.type)) {
|
|
486
|
+
issue(issues, `fields[${fieldIndex}].type`, "disallowed_field_type", `Field type ${field.type} is not allowed.`);
|
|
487
|
+
}
|
|
488
|
+
if (policy.maxOptionsPerField !== void 0 && "options" in field && field.options.length > policy.maxOptionsPerField) {
|
|
489
|
+
issue(
|
|
490
|
+
issues,
|
|
491
|
+
`fields[${fieldIndex}].options`,
|
|
492
|
+
"max_options_exceeded",
|
|
493
|
+
`At most ${policy.maxOptionsPerField} options are allowed.`
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
if (policy.maxTextLength !== void 0) {
|
|
498
|
+
for (const entry of collectSchemaText(schema)) {
|
|
499
|
+
if (entry.value.length > policy.maxTextLength) {
|
|
500
|
+
issue(
|
|
501
|
+
issues,
|
|
502
|
+
entry.path,
|
|
503
|
+
"max_text_length_exceeded",
|
|
504
|
+
`Text must be at most ${policy.maxTextLength} characters.`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
for (const locale of policy.requiredLocales ?? []) addRequiredTranslationIssues(schema, locale, issues);
|
|
510
|
+
if (policy.maxSchemaBytes !== void 0) {
|
|
511
|
+
try {
|
|
512
|
+
const byteLength = new TextEncoder().encode(JSON.stringify(schema)).byteLength;
|
|
513
|
+
if (byteLength > policy.maxSchemaBytes) {
|
|
514
|
+
issue(issues, "$", "max_schema_bytes_exceeded", `Schema must be at most ${policy.maxSchemaBytes} bytes.`);
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function validateFormSchema(input, options = {}) {
|
|
377
521
|
const issues = [];
|
|
378
522
|
if (!isRecord(input)) {
|
|
379
523
|
return { valid: false, issues: [{ path: "$", code: "invalid_schema", message: "Expected a schema object." }] };
|
|
@@ -519,6 +663,14 @@ function validateFormSchema(input) {
|
|
|
519
663
|
}
|
|
520
664
|
}
|
|
521
665
|
}
|
|
666
|
+
if (Array.isArray(input.fields)) {
|
|
667
|
+
const policyIssues = [];
|
|
668
|
+
try {
|
|
669
|
+
validatePolicy(input, options.policy ?? {}, policyIssues);
|
|
670
|
+
issues.push(...policyIssues);
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
}
|
|
522
674
|
return issues.length === 0 ? { valid: true, value: input, issues: [] } : { valid: false, issues };
|
|
523
675
|
}
|
|
524
676
|
function assertValidFormSchema(input) {
|
|
@@ -827,6 +979,58 @@ async function dispatchWebhook(event, config, fetchImpl = globalThis.fetch) {
|
|
|
827
979
|
}
|
|
828
980
|
}
|
|
829
981
|
|
|
982
|
+
// src/field.ts
|
|
983
|
+
var DEFAULT_OPTION = { id: "option-1", label: "Option 1" };
|
|
984
|
+
function transformFieldType(field, nextType) {
|
|
985
|
+
const common = {
|
|
986
|
+
id: field.id,
|
|
987
|
+
title: field.title,
|
|
988
|
+
required: field.required,
|
|
989
|
+
...field.description === void 0 ? {} : { description: field.description },
|
|
990
|
+
...field.translationKey === void 0 ? {} : { translationKey: field.translationKey },
|
|
991
|
+
...field.messages === void 0 ? {} : { messages: field.messages },
|
|
992
|
+
...field.displayCondition === void 0 ? {} : { displayCondition: field.displayCondition },
|
|
993
|
+
...field.translations === void 0 ? {} : { translations: field.translations },
|
|
994
|
+
...field.metadata === void 0 ? {} : { metadata: field.metadata },
|
|
995
|
+
...field.translationMetadata === void 0 ? {} : { translationMetadata: field.translationMetadata }
|
|
996
|
+
};
|
|
997
|
+
if (nextType === "text" || nextType === "textarea") {
|
|
998
|
+
const textProperties = field.type === "text" || field.type === "textarea" ? {
|
|
999
|
+
...field.placeholderKey === void 0 ? {} : { placeholderKey: field.placeholderKey },
|
|
1000
|
+
...field.minLength === void 0 ? {} : { minLength: field.minLength },
|
|
1001
|
+
...field.maxLength === void 0 ? {} : { maxLength: field.maxLength },
|
|
1002
|
+
...field.pattern === void 0 ? {} : { pattern: field.pattern }
|
|
1003
|
+
} : {};
|
|
1004
|
+
return { ...common, ...textProperties, type: nextType };
|
|
1005
|
+
}
|
|
1006
|
+
if (nextType === "number") {
|
|
1007
|
+
const numberProperties = field.type === "number" ? {
|
|
1008
|
+
...field.placeholderKey === void 0 ? {} : { placeholderKey: field.placeholderKey },
|
|
1009
|
+
...field.min === void 0 ? {} : { min: field.min },
|
|
1010
|
+
...field.max === void 0 ? {} : { max: field.max },
|
|
1011
|
+
...field.step === void 0 ? {} : { step: field.step }
|
|
1012
|
+
} : {};
|
|
1013
|
+
return { ...common, ...numberProperties, type: nextType };
|
|
1014
|
+
}
|
|
1015
|
+
if (nextType === "rating") {
|
|
1016
|
+
const ratingProperties = field.type === "rating" ? {
|
|
1017
|
+
...field.min === void 0 ? {} : { min: field.min },
|
|
1018
|
+
...field.max === void 0 ? {} : { max: field.max }
|
|
1019
|
+
} : { min: 1, max: 5 };
|
|
1020
|
+
return { ...common, ...ratingProperties, type: nextType };
|
|
1021
|
+
}
|
|
1022
|
+
if (nextType === "checkbox") return { ...common, type: nextType };
|
|
1023
|
+
const options = "options" in field && field.options.length > 0 ? field.options : [DEFAULT_OPTION];
|
|
1024
|
+
if (nextType === "multi-select") {
|
|
1025
|
+
const selectionProperties = field.type === "multi-select" ? {
|
|
1026
|
+
...field.minSelections === void 0 ? {} : { minSelections: field.minSelections },
|
|
1027
|
+
...field.maxSelections === void 0 ? {} : { maxSelections: field.maxSelections }
|
|
1028
|
+
} : {};
|
|
1029
|
+
return { ...common, ...selectionProperties, type: nextType, options };
|
|
1030
|
+
}
|
|
1031
|
+
return { ...common, type: nextType, options };
|
|
1032
|
+
}
|
|
1033
|
+
|
|
830
1034
|
// src/validation.ts
|
|
831
1035
|
var DEFAULT_MESSAGES = {
|
|
832
1036
|
required: "validation.required",
|
|
@@ -1015,7 +1219,7 @@ function withTranslationMetadata(node, locale, property, metadata) {
|
|
|
1015
1219
|
}
|
|
1016
1220
|
};
|
|
1017
1221
|
}
|
|
1018
|
-
function createSlot(kind, nodeId, property, locale, sourceText, existingText,
|
|
1222
|
+
function createSlot(kind, nodeId, property, locale, sourceText, existingText, nodeMetadata, existingTranslationMetadata) {
|
|
1019
1223
|
return {
|
|
1020
1224
|
kind,
|
|
1021
1225
|
nodeId,
|
|
@@ -1023,7 +1227,8 @@ function createSlot(kind, nodeId, property, locale, sourceText, existingText, me
|
|
|
1023
1227
|
locale,
|
|
1024
1228
|
sourceText,
|
|
1025
1229
|
...existingText === void 0 ? {} : { existingText },
|
|
1026
|
-
...
|
|
1230
|
+
...nodeMetadata === void 0 ? {} : { nodeMetadata, metadata: nodeMetadata },
|
|
1231
|
+
...existingTranslationMetadata === void 0 ? {} : { existingTranslationMetadata }
|
|
1027
1232
|
};
|
|
1028
1233
|
}
|
|
1029
1234
|
function translationSlots(schema, locale) {
|
|
@@ -1036,7 +1241,8 @@ function translationSlots(schema, locale) {
|
|
|
1036
1241
|
locale,
|
|
1037
1242
|
sourceText,
|
|
1038
1243
|
schema.translations?.[locale]?.[property],
|
|
1039
|
-
schema.metadata
|
|
1244
|
+
schema.metadata,
|
|
1245
|
+
schema.translationMetadata?.[locale]?.[property]
|
|
1040
1246
|
);
|
|
1041
1247
|
descriptors.push({
|
|
1042
1248
|
slot,
|
|
@@ -1060,7 +1266,8 @@ function translationSlots(schema, locale) {
|
|
|
1060
1266
|
locale,
|
|
1061
1267
|
sourceText,
|
|
1062
1268
|
field.translations?.[locale]?.[property],
|
|
1063
|
-
field.metadata
|
|
1269
|
+
field.metadata,
|
|
1270
|
+
field.translationMetadata?.[locale]?.[property]
|
|
1064
1271
|
);
|
|
1065
1272
|
descriptors.push({
|
|
1066
1273
|
slot,
|
|
@@ -1091,7 +1298,8 @@ function translationSlots(schema, locale) {
|
|
|
1091
1298
|
locale,
|
|
1092
1299
|
option.label,
|
|
1093
1300
|
option.translations?.[locale],
|
|
1094
|
-
option.metadata
|
|
1301
|
+
option.metadata,
|
|
1302
|
+
option.translationMetadata?.[locale]?.label
|
|
1095
1303
|
);
|
|
1096
1304
|
descriptors.push({
|
|
1097
1305
|
slot,
|
|
@@ -1128,7 +1336,8 @@ function translationSlots(schema, locale) {
|
|
|
1128
1336
|
locale,
|
|
1129
1337
|
sourceText,
|
|
1130
1338
|
page.translations?.[locale]?.[property],
|
|
1131
|
-
page.metadata
|
|
1339
|
+
page.metadata,
|
|
1340
|
+
page.translationMetadata?.[locale]?.[property]
|
|
1132
1341
|
);
|
|
1133
1342
|
descriptors.push({
|
|
1134
1343
|
slot,
|
|
@@ -1264,6 +1473,7 @@ export {
|
|
|
1264
1473
|
resolveLocalizedSchema,
|
|
1265
1474
|
sanitizeSchema,
|
|
1266
1475
|
selectVisibleAnswers,
|
|
1476
|
+
transformFieldType,
|
|
1267
1477
|
validateAnswers,
|
|
1268
1478
|
validateFormSchema,
|
|
1269
1479
|
validatePageAnswers,
|