@form-engine-ts/core 4.4.0 → 4.6.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 +16 -0
- package/dist/index.cjs +442 -69
- package/dist/index.d.cts +80 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +435 -69
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,8 @@ __export(index_exports, {
|
|
|
31
31
|
calculatePageVisibility: () => calculatePageVisibility,
|
|
32
32
|
cloneVersionToDraft: () => cloneVersionToDraft,
|
|
33
33
|
collectSchemaLocales: () => collectSchemaLocales,
|
|
34
|
+
collectTranslationSlots: () => collectTranslationSlots,
|
|
35
|
+
computeSourceTextHash: () => computeSourceTextHash,
|
|
34
36
|
createCloneTransitionPlan: () => createCloneTransitionPlan,
|
|
35
37
|
createDeleteDraftTransitionPlan: () => createDeleteDraftTransitionPlan,
|
|
36
38
|
createPublishTransitionPlan: () => createPublishTransitionPlan,
|
|
@@ -45,16 +47,21 @@ __export(index_exports, {
|
|
|
45
47
|
escapeCsvCell: () => escapeCsvCell,
|
|
46
48
|
exportResponsesToCsv: () => exportResponsesToCsv,
|
|
47
49
|
exportResponsesToCsvStream: () => exportResponsesToCsvStream,
|
|
50
|
+
getTranslationStatus: () => getTranslationStatus,
|
|
51
|
+
isDisplayConditionGroupSatisfied: () => isDisplayConditionGroupSatisfied,
|
|
48
52
|
isDisplayConditionSatisfied: () => isDisplayConditionSatisfied,
|
|
53
|
+
isManualTranslationMetadata: () => isManualTranslationMetadata,
|
|
49
54
|
isQuestionVisible: () => isQuestionVisible,
|
|
50
55
|
iterateSubmissionPages: () => iterateSubmissionPages,
|
|
51
56
|
jsonValuesEqual: () => jsonValuesEqual,
|
|
52
57
|
matchesSubmissionFilter: () => matchesSubmissionFilter,
|
|
53
58
|
matchesSubmissionPageFilters: () => matchesSubmissionPageFilters,
|
|
59
|
+
migrateSchemaTranslationMetadata: () => migrateSchemaTranslationMetadata,
|
|
54
60
|
normalizeSubmissionPageSize: () => normalizeSubmissionPageSize,
|
|
55
61
|
pipeResponsesToCsvStream: () => pipeResponsesToCsvStream,
|
|
56
62
|
populateSchemaTranslations: () => populateSchemaTranslations,
|
|
57
63
|
publishDraft: () => publishDraft,
|
|
64
|
+
removeLocaleFromSchema: () => removeLocaleFromSchema,
|
|
58
65
|
resolveFormTranslation: () => resolveFormTranslation,
|
|
59
66
|
resolveLocalizedSchema: () => resolveLocalizedSchema,
|
|
60
67
|
sanitizeSchema: () => sanitizeSchema,
|
|
@@ -116,6 +123,19 @@ function collectSchemaLocales(schema) {
|
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
// src/sanitization.ts
|
|
126
|
+
function displayRuleSourceIds(field) {
|
|
127
|
+
if (field.displayRule === void 0)
|
|
128
|
+
return field.displayCondition?.questionId === void 0 ? [] : [field.displayCondition.questionId];
|
|
129
|
+
const ids = [];
|
|
130
|
+
const visit = (group) => {
|
|
131
|
+
for (const condition of group.conditions) {
|
|
132
|
+
if ("logic" in condition) visit(condition);
|
|
133
|
+
else ids.push(condition.fieldId);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
visit(field.displayRule.condition);
|
|
137
|
+
return ids;
|
|
138
|
+
}
|
|
119
139
|
function registeredEntries(value, registeredLocales) {
|
|
120
140
|
if (value === void 0) return void 0;
|
|
121
141
|
const entries = Object.entries(value).filter(([locale]) => registeredLocales.has(locale));
|
|
@@ -183,26 +203,28 @@ function cyclicQuestionIds(fields) {
|
|
|
183
203
|
if (!firstById.has(field.id)) firstById.set(field.id, field);
|
|
184
204
|
}
|
|
185
205
|
const cyclic = /* @__PURE__ */ new Set();
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (existingIndex !== void 0) {
|
|
195
|
-
for (const id of path.slice(existingIndex)) cyclic.add(id);
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
pathIndex.set(currentId, path.length);
|
|
199
|
-
path.push(currentId);
|
|
200
|
-
const field = firstById.get(currentId);
|
|
201
|
-
const sourceId = field?.displayCondition?.questionId;
|
|
202
|
-
currentId = sourceId === currentId ? void 0 : sourceId;
|
|
206
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
207
|
+
const visited = /* @__PURE__ */ new Set();
|
|
208
|
+
const path = [];
|
|
209
|
+
const visit = (id) => {
|
|
210
|
+
if (visiting.has(id)) {
|
|
211
|
+
const start = path.indexOf(id);
|
|
212
|
+
for (const member of path.slice(start < 0 ? 0 : start)) cyclic.add(member);
|
|
213
|
+
return;
|
|
203
214
|
}
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
if (visited.has(id)) return;
|
|
216
|
+
visiting.add(id);
|
|
217
|
+
path.push(id);
|
|
218
|
+
const field = firstById.get(id);
|
|
219
|
+
if (field === void 0) return;
|
|
220
|
+
for (const sourceId of displayRuleSourceIds(field)) {
|
|
221
|
+
if (sourceId !== id && firstById.has(sourceId)) visit(sourceId);
|
|
222
|
+
}
|
|
223
|
+
path.pop();
|
|
224
|
+
visiting.delete(id);
|
|
225
|
+
visited.add(id);
|
|
226
|
+
};
|
|
227
|
+
for (const id of firstById.keys()) visit(id);
|
|
206
228
|
return cyclic;
|
|
207
229
|
}
|
|
208
230
|
function validateSchemaStructure(schema) {
|
|
@@ -234,20 +256,20 @@ function validateSchemaStructure(schema) {
|
|
|
234
256
|
}
|
|
235
257
|
}
|
|
236
258
|
for (const field of schema.fields) {
|
|
237
|
-
const sourceId
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
259
|
+
for (const sourceId of displayRuleSourceIds(field)) {
|
|
260
|
+
if (sourceId === field.id) {
|
|
261
|
+
issues.push({
|
|
262
|
+
type: "self_condition_reference",
|
|
263
|
+
questionId: field.id,
|
|
264
|
+
message: `Question "${field.id}" cannot depend on itself.`
|
|
265
|
+
});
|
|
266
|
+
} else if (!questionIds.has(sourceId)) {
|
|
267
|
+
issues.push({
|
|
268
|
+
type: "dangling_condition_reference",
|
|
269
|
+
questionId: field.id,
|
|
270
|
+
message: `Question "${field.id}" references missing question "${sourceId}".`
|
|
271
|
+
});
|
|
272
|
+
}
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
275
|
const cyclic = cyclicQuestionIds(schema.fields);
|
|
@@ -256,6 +278,11 @@ function validateSchemaStructure(schema) {
|
|
|
256
278
|
issues.push({
|
|
257
279
|
type: "cyclic_condition_reference",
|
|
258
280
|
questionId: field.id,
|
|
281
|
+
cycle: [
|
|
282
|
+
field.id,
|
|
283
|
+
...displayRuleSourceIds(field).filter((sourceId) => cyclic.has(sourceId)).slice(0, 1),
|
|
284
|
+
field.id
|
|
285
|
+
],
|
|
259
286
|
message: `Question "${field.id}" participates in a display-condition cycle.`
|
|
260
287
|
});
|
|
261
288
|
}
|
|
@@ -270,11 +297,17 @@ function sanitizeSchema(schema, options = {}) {
|
|
|
270
297
|
const cyclic = cyclicQuestionIds(schema.fields);
|
|
271
298
|
const sanitizedFields = schema.fields.map((sourceField) => {
|
|
272
299
|
const field = sanitizeFieldConstraints(sanitizeFieldLocales(sourceField, registeredLocales), options.policy);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
300
|
+
let sanitized = field;
|
|
301
|
+
const ruleSources = displayRuleSourceIds(field);
|
|
302
|
+
if (field.displayRule !== void 0 && (cyclic.has(field.id) || ruleSources.some((sourceId2) => sourceId2 === field.id || !existingQuestionIds.has(sourceId2)))) {
|
|
303
|
+
const { displayRule: _displayRule, ...withoutRule } = sanitized;
|
|
304
|
+
sanitized = withoutRule;
|
|
305
|
+
}
|
|
306
|
+
const sourceId = sanitized.displayCondition?.questionId;
|
|
307
|
+
if (sourceId !== void 0 && (!existingQuestionIds.has(sourceId) || sourceId === sanitized.id || cyclic.has(sanitized.id))) {
|
|
308
|
+
const { displayCondition: _displayCondition, ...withoutCondition } = sanitized;
|
|
309
|
+
return withoutCondition;
|
|
276
310
|
}
|
|
277
|
-
const { displayCondition: _displayCondition, ...sanitized } = field;
|
|
278
311
|
return sanitized;
|
|
279
312
|
});
|
|
280
313
|
const localizedSchema = sanitizeNodeLocales(schema, registeredLocales);
|
|
@@ -317,7 +350,17 @@ function sanitizeSchema(schema, options = {}) {
|
|
|
317
350
|
|
|
318
351
|
// src/schema.ts
|
|
319
352
|
var FIELD_TYPES = /* @__PURE__ */ new Set(["text", "textarea", "number", "rating", "select", "multi-select", "checkbox", "radio"]);
|
|
320
|
-
var CONDITION_OPERATORS = /* @__PURE__ */ new Set([
|
|
353
|
+
var CONDITION_OPERATORS = /* @__PURE__ */ new Set([
|
|
354
|
+
"equals",
|
|
355
|
+
"not_equals",
|
|
356
|
+
"contains",
|
|
357
|
+
"not_contains",
|
|
358
|
+
"is_empty",
|
|
359
|
+
"is_not_empty",
|
|
360
|
+
"greater_than",
|
|
361
|
+
"less_than",
|
|
362
|
+
"not_empty"
|
|
363
|
+
]);
|
|
321
364
|
function isRecord(value) {
|
|
322
365
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
323
366
|
}
|
|
@@ -474,7 +517,7 @@ function validateDisplayCondition(value, path, issues) {
|
|
|
474
517
|
return false;
|
|
475
518
|
}
|
|
476
519
|
const hasValue = Object.hasOwn(value, "value") && value.value !== void 0;
|
|
477
|
-
if (value.operator === "not_empty") {
|
|
520
|
+
if (value.operator === "not_empty" || value.operator === "is_empty" || value.operator === "is_not_empty") {
|
|
478
521
|
if (hasValue) issue(issues, `${path}.value`, "unexpected_condition_value", "not_empty does not accept a value.");
|
|
479
522
|
} else if (!hasValue) {
|
|
480
523
|
issue(issues, `${path}.value`, "missing_condition_value", `${value.operator} requires a value.`);
|
|
@@ -485,6 +528,75 @@ function validateDisplayCondition(value, path, issues) {
|
|
|
485
528
|
}
|
|
486
529
|
return true;
|
|
487
530
|
}
|
|
531
|
+
function validateDisplayConditionGroup(value, path, issues) {
|
|
532
|
+
if (!isRecord(value)) {
|
|
533
|
+
issue(issues, path, "invalid_condition_group", "Expected a display condition group.");
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
if (value.logic !== "all" && value.logic !== "any") {
|
|
537
|
+
issue(issues, `${path}.logic`, "invalid_condition_logic", "Expected all or any.");
|
|
538
|
+
}
|
|
539
|
+
if (!Array.isArray(value.conditions)) {
|
|
540
|
+
issue(issues, `${path}.conditions`, "invalid_condition_group", "Expected a conditions array.");
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
value.conditions.forEach((condition, index) => {
|
|
544
|
+
const conditionPath = `${path}.conditions[${index}]`;
|
|
545
|
+
if (isRecord(condition) && Object.hasOwn(condition, "logic")) {
|
|
546
|
+
validateDisplayConditionGroup(condition, conditionPath, issues);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (!isRecord(condition)) {
|
|
550
|
+
issue(issues, conditionPath, "invalid_condition", "Expected a field display condition.");
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (!isNonEmptyString(condition.fieldId)) {
|
|
554
|
+
issue(issues, `${conditionPath}.fieldId`, "invalid_condition_source", "Expected a field ID.");
|
|
555
|
+
}
|
|
556
|
+
if (typeof condition.operator !== "string" || !CONDITION_OPERATORS.has(condition.operator)) {
|
|
557
|
+
issue(issues, `${conditionPath}.operator`, "invalid_condition_operator", "Unsupported condition operator.");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const needsValue = !["not_empty", "is_empty", "is_not_empty"].includes(condition.operator);
|
|
561
|
+
const hasValue = Object.hasOwn(condition, "value") && condition.value !== void 0;
|
|
562
|
+
if (needsValue && !hasValue)
|
|
563
|
+
issue(issues, `${conditionPath}.value`, "missing_condition_value", "A value is required.");
|
|
564
|
+
});
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
function validateDisplayRule(value, path, issues) {
|
|
568
|
+
if (!isRecord(value)) {
|
|
569
|
+
issue(issues, path, "invalid_display_rule", "Expected a display rule object.");
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
if (value.action !== "show" && value.action !== "hide") {
|
|
573
|
+
issue(issues, `${path}.action`, "invalid_display_action", "Expected show or hide.");
|
|
574
|
+
}
|
|
575
|
+
validateDisplayConditionGroup(value.condition, `${path}.condition`, issues);
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
function validateSubmissionSettings(value, path, issues) {
|
|
579
|
+
if (!isRecord(value)) {
|
|
580
|
+
issue(issues, path, "invalid_submission_settings", "Expected submission settings to be an object.");
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
validateExtensibleNode(value, path, issues);
|
|
584
|
+
if (value.showConfirmationBeforeSubmit !== void 0 && typeof value.showConfirmationBeforeSubmit !== "boolean") {
|
|
585
|
+
issue(issues, `${path}.showConfirmationBeforeSubmit`, "invalid_submission_setting", "Expected a boolean.");
|
|
586
|
+
}
|
|
587
|
+
if (value.confirmationRenderMode !== void 0 && !["dialog", "inline", "replace"].includes(String(value.confirmationRenderMode))) {
|
|
588
|
+
issue(
|
|
589
|
+
issues,
|
|
590
|
+
`${path}.confirmationRenderMode`,
|
|
591
|
+
"invalid_submission_setting",
|
|
592
|
+
"Unsupported confirmation render mode."
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
for (const key of ["confirmButtonLabel", "cancelButtonLabel"]) {
|
|
596
|
+
if (value[key] !== void 0 && !isNonEmptyString(value[key]))
|
|
597
|
+
issue(issues, `${path}.${key}`, "invalid_submission_setting", "Expected non-empty text.");
|
|
598
|
+
}
|
|
599
|
+
}
|
|
488
600
|
function validateField(value, path, issues) {
|
|
489
601
|
if (!isRecord(value)) {
|
|
490
602
|
issue(issues, path, "invalid_field", "Expected a field object.");
|
|
@@ -508,6 +620,7 @@ function validateField(value, path, issues) {
|
|
|
508
620
|
if (value.displayCondition !== void 0) {
|
|
509
621
|
validateDisplayCondition(value.displayCondition, `${path}.displayCondition`, issues);
|
|
510
622
|
}
|
|
623
|
+
if (value.displayRule !== void 0) validateDisplayRule(value.displayRule, `${path}.displayRule`, issues);
|
|
511
624
|
if (value.translations !== void 0) validateLocalizedTextMap(value.translations, `${path}.translations`, issues);
|
|
512
625
|
if (typeof value.type !== "string" || !FIELD_TYPES.has(value.type)) {
|
|
513
626
|
issue(issues, `${path}.type`, "invalid_field_type", "Unsupported field type.");
|
|
@@ -892,6 +1005,8 @@ function validateFormSchema(input, options = {}) {
|
|
|
892
1005
|
}
|
|
893
1006
|
}
|
|
894
1007
|
if (input.translations !== void 0) validateLocalizedTextMap(input.translations, "translations", issues);
|
|
1008
|
+
if (input.submissionSettings !== void 0)
|
|
1009
|
+
validateSubmissionSettings(input.submissionSettings, "submissionSettings", issues);
|
|
895
1010
|
if (!Array.isArray(input.fields) || input.fields.length === 0) {
|
|
896
1011
|
issue(issues, "fields", "invalid_fields", "Expected at least one field.");
|
|
897
1012
|
} else {
|
|
@@ -915,8 +1030,19 @@ function validateFormSchema(input, options = {}) {
|
|
|
915
1030
|
continue;
|
|
916
1031
|
}
|
|
917
1032
|
const fieldIndex = inputFields.findIndex((field) => isRecord(field) && field.id === structuralIssue.questionId);
|
|
918
|
-
const
|
|
919
|
-
|
|
1033
|
+
const currentField = inputFields[fieldIndex];
|
|
1034
|
+
const usesDisplayRule = isRecord(currentField) && currentField.displayRule !== void 0;
|
|
1035
|
+
const code = structuralIssue.type === "dangling_condition_reference" ? "unknown_condition_source" : structuralIssue.type === "self_condition_reference" ? "self_condition" : usesDisplayRule ? "circular_display_condition" : "condition_cycle";
|
|
1036
|
+
const path = usesDisplayRule ? `fields[${fieldIndex}].displayRule` : `fields[${fieldIndex}].displayCondition`;
|
|
1037
|
+
if (usesDisplayRule && structuralIssue.type === "cyclic_condition_reference") {
|
|
1038
|
+
issues.push({
|
|
1039
|
+
path,
|
|
1040
|
+
code,
|
|
1041
|
+
type: code,
|
|
1042
|
+
message: structuralIssue.message,
|
|
1043
|
+
...structuralIssue.cycle === void 0 ? {} : { cycle: structuralIssue.cycle }
|
|
1044
|
+
});
|
|
1045
|
+
} else issue(issues, path, code, structuralIssue.message);
|
|
920
1046
|
}
|
|
921
1047
|
if (input.pages !== void 0) {
|
|
922
1048
|
if (!Array.isArray(input.pages) || input.pages.length === 0) {
|
|
@@ -1025,23 +1151,61 @@ function normalizeString(value) {
|
|
|
1025
1151
|
return value.trim().normalize("NFKC").toLowerCase();
|
|
1026
1152
|
}
|
|
1027
1153
|
function valuesEqual(left, right) {
|
|
1028
|
-
|
|
1154
|
+
if (typeof left === "string" && typeof right === "string") return normalizeString(left) === normalizeString(right);
|
|
1155
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
1156
|
+
return left.length === right.length && left.every((item, index) => valuesEqual(item, right[index]));
|
|
1157
|
+
}
|
|
1158
|
+
return left === right;
|
|
1159
|
+
}
|
|
1160
|
+
function evaluateFieldCondition(condition, currentAnswers) {
|
|
1161
|
+
const sourceId = "fieldId" in condition ? condition.fieldId : condition.questionId;
|
|
1162
|
+
const answer = currentAnswers[sourceId];
|
|
1163
|
+
if (isEmpty(answer) && ["equals", "not_equals", "contains"].includes(condition.operator)) return false;
|
|
1164
|
+
if (condition.operator === "is_empty") return isEmpty(answer);
|
|
1165
|
+
if (condition.operator === "is_not_empty" || condition.operator === "not_empty") return !isEmpty(answer);
|
|
1166
|
+
if (condition.operator === "contains" || condition.operator === "not_contains") {
|
|
1167
|
+
const contains = typeof answer === "string" && typeof condition.value === "string" ? normalizeString(answer).includes(normalizeString(condition.value)) : Array.isArray(answer) && answer.some((item) => valuesEqual(item, condition.value));
|
|
1168
|
+
return condition.operator === "not_contains" ? !contains : contains;
|
|
1169
|
+
}
|
|
1170
|
+
if (condition.value === void 0) return false;
|
|
1171
|
+
if (condition.operator === "equals") return valuesEqual(answer, condition.value);
|
|
1172
|
+
if (condition.operator === "not_equals") return !valuesEqual(answer, condition.value);
|
|
1173
|
+
if (condition.operator === "greater_than")
|
|
1174
|
+
return typeof answer === "number" && typeof condition.value === "number" && answer > condition.value;
|
|
1175
|
+
if (condition.operator === "less_than")
|
|
1176
|
+
return typeof answer === "number" && typeof condition.value === "number" && answer < condition.value;
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
function isDisplayConditionGroupSatisfied(group, currentAnswers) {
|
|
1180
|
+
const results = group.conditions.map(
|
|
1181
|
+
(condition) => "logic" in condition ? isDisplayConditionGroupSatisfied(condition, currentAnswers) : evaluateFieldCondition(condition, currentAnswers)
|
|
1182
|
+
);
|
|
1183
|
+
return group.logic === "all" ? results.every(Boolean) : results.some(Boolean);
|
|
1029
1184
|
}
|
|
1030
1185
|
function isQuestionVisible(question, currentAnswers) {
|
|
1186
|
+
if (question.displayRule !== void 0) {
|
|
1187
|
+
const satisfied = isDisplayConditionGroupSatisfied(question.displayRule.condition, currentAnswers);
|
|
1188
|
+
return question.displayRule.action === "hide" ? !satisfied : satisfied;
|
|
1189
|
+
}
|
|
1031
1190
|
return isDisplayConditionSatisfied(question.displayCondition, currentAnswers);
|
|
1032
1191
|
}
|
|
1033
1192
|
function isDisplayConditionSatisfied(condition, currentAnswers) {
|
|
1034
1193
|
if (condition === void 0) return true;
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (condition
|
|
1040
|
-
if (
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1194
|
+
if ("logic" in condition) return isDisplayConditionGroupSatisfied(condition, currentAnswers);
|
|
1195
|
+
return evaluateFieldCondition(condition, currentAnswers);
|
|
1196
|
+
}
|
|
1197
|
+
function conditionSourceIds(condition) {
|
|
1198
|
+
if (condition === void 0) return [];
|
|
1199
|
+
if ("questionId" in condition) return [condition.questionId];
|
|
1200
|
+
const ids = [];
|
|
1201
|
+
const visit = (group) => {
|
|
1202
|
+
for (const item of group.conditions) {
|
|
1203
|
+
if ("logic" in item) visit(item);
|
|
1204
|
+
else ids.push(item.fieldId);
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
visit(condition.condition);
|
|
1208
|
+
return ids;
|
|
1045
1209
|
}
|
|
1046
1210
|
function calculateBaseFieldVisibility(schema, currentAnswers) {
|
|
1047
1211
|
const fields = new Map(schema.fields.map((field) => [field.id, field]));
|
|
@@ -1052,10 +1216,12 @@ function calculateBaseFieldVisibility(schema, currentAnswers) {
|
|
|
1052
1216
|
if (existing !== void 0) return existing;
|
|
1053
1217
|
if (resolving.has(field.id)) return false;
|
|
1054
1218
|
resolving.add(field.id);
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1219
|
+
const sources = field.displayRule === void 0 ? conditionSourceIds(field.displayCondition) : conditionSourceIds(field.displayRule);
|
|
1220
|
+
const sourcesVisible = sources.every((sourceId) => {
|
|
1221
|
+
const source = fields.get(sourceId);
|
|
1222
|
+
return source !== void 0 && source.id !== field.id && resolve(source);
|
|
1223
|
+
});
|
|
1224
|
+
const visible = sourcesVisible && isQuestionVisible(field, currentAnswers);
|
|
1059
1225
|
resolving.delete(field.id);
|
|
1060
1226
|
resolved.set(field.id, visible);
|
|
1061
1227
|
return visible;
|
|
@@ -2053,6 +2219,39 @@ function createSubmission(schema, values, options) {
|
|
|
2053
2219
|
}
|
|
2054
2220
|
|
|
2055
2221
|
// src/translation.ts
|
|
2222
|
+
var isManualTranslationMetadata = (metadata) => {
|
|
2223
|
+
if (metadata === void 0) return false;
|
|
2224
|
+
if ("isManuallyEdited" in metadata && metadata.isManuallyEdited === true || "isManual" in metadata && metadata.isManual === true)
|
|
2225
|
+
return true;
|
|
2226
|
+
return typeof metadata.translationSource === "string" && metadata.translationSource.toLowerCase() === "manual";
|
|
2227
|
+
};
|
|
2228
|
+
var computeSourceTextHash = (text) => {
|
|
2229
|
+
const normalized = text.normalize("NFKC").trim().replace(/\s+/gu, " ");
|
|
2230
|
+
let hash = 2166136261;
|
|
2231
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
2232
|
+
hash ^= normalized.charCodeAt(index);
|
|
2233
|
+
hash = Math.imul(hash, 16777619);
|
|
2234
|
+
}
|
|
2235
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
2236
|
+
};
|
|
2237
|
+
function getTranslationStatus(sourceText, translatedText, metadata) {
|
|
2238
|
+
return getTranslationStatusWithManualOverride(sourceText, translatedText, metadata, void 0);
|
|
2239
|
+
}
|
|
2240
|
+
function getTranslationStatusWithManualOverride(sourceText, translatedText, metadata, manualOverride) {
|
|
2241
|
+
if (translatedText === void 0 || translatedText.trim() === "") return "missing";
|
|
2242
|
+
if (metadata === void 0) return "translated";
|
|
2243
|
+
const currentHash = computeSourceTextHash(sourceText);
|
|
2244
|
+
const sourceHash = typeof metadata.sourceTextHash === "string" ? metadata.sourceTextHash : void 0;
|
|
2245
|
+
const isManual = manualOverride ?? isManualTranslationMetadata(metadata);
|
|
2246
|
+
if (isManual) return sourceHash === void 0 || sourceHash === currentHash ? "manual" : "manual-stale";
|
|
2247
|
+
return sourceHash === void 0 || sourceHash === currentHash ? "translated" : "stale";
|
|
2248
|
+
}
|
|
2249
|
+
async function translateBatch(adapter, texts, locale, sourceLocale) {
|
|
2250
|
+
if ("translateBatch" in adapter) return adapter.translateBatch(texts, locale, sourceLocale);
|
|
2251
|
+
return texts.map(
|
|
2252
|
+
(text) => adapter.translate(text, locale, sourceLocale === void 0 ? void 0 : { sourceLocale }) ?? text
|
|
2253
|
+
);
|
|
2254
|
+
}
|
|
2056
2255
|
function mergeLocalizedText(translations, locale, property, value) {
|
|
2057
2256
|
return { ...translations, [locale]: { ...translations?.[locale], [property]: value } };
|
|
2058
2257
|
}
|
|
@@ -2069,19 +2268,27 @@ function withTranslationMetadata(node, locale, property, metadata) {
|
|
|
2069
2268
|
}
|
|
2070
2269
|
};
|
|
2071
2270
|
}
|
|
2072
|
-
function createSlot(kind, nodeId, property, locale, sourceText, existingText, nodeMetadata, existingTranslationMetadata) {
|
|
2271
|
+
function createSlot(kind, nodeId, property, locale, sourceText, existingText, nodeMetadata, existingTranslationMetadata, options = {}) {
|
|
2272
|
+
const path = `${kind}.${nodeId}.${property}`;
|
|
2273
|
+
const manual = options.isManualTranslation?.(existingTranslationMetadata, { path, locale }) ?? isManualTranslationMetadata(existingTranslationMetadata);
|
|
2274
|
+
const normalizedMetadata = existingTranslationMetadata === void 0 ? void 0 : options.normalizeMetadata === void 0 ? existingTranslationMetadata : { ...options.normalizeMetadata(existingTranslationMetadata, sourceText) };
|
|
2275
|
+
const status = getTranslationStatusWithManualOverride(sourceText, existingText, normalizedMetadata, manual);
|
|
2073
2276
|
return {
|
|
2074
2277
|
kind,
|
|
2075
2278
|
nodeId,
|
|
2076
2279
|
property,
|
|
2077
2280
|
locale,
|
|
2078
2281
|
sourceText,
|
|
2282
|
+
target: { kind, ...nodeId.length === 0 ? {} : { id: nodeId }, property },
|
|
2283
|
+
path,
|
|
2284
|
+
sourceTextHash: computeSourceTextHash(sourceText),
|
|
2285
|
+
status,
|
|
2079
2286
|
...existingText === void 0 ? {} : { existingText },
|
|
2080
2287
|
...nodeMetadata === void 0 ? {} : { nodeMetadata, metadata: nodeMetadata },
|
|
2081
|
-
...
|
|
2288
|
+
...normalizedMetadata === void 0 ? {} : { existingTranslationMetadata: normalizedMetadata }
|
|
2082
2289
|
};
|
|
2083
2290
|
}
|
|
2084
|
-
function translationSlots(schema, locale) {
|
|
2291
|
+
function translationSlots(schema, locale, options = {}) {
|
|
2085
2292
|
const descriptors = [];
|
|
2086
2293
|
const addFormSlot = (property, sourceText) => {
|
|
2087
2294
|
const slot = createSlot(
|
|
@@ -2092,10 +2299,15 @@ function translationSlots(schema, locale) {
|
|
|
2092
2299
|
sourceText,
|
|
2093
2300
|
schema.translations?.[locale]?.[property],
|
|
2094
2301
|
schema.metadata,
|
|
2095
|
-
schema.translationMetadata?.[locale]?.[property]
|
|
2302
|
+
schema.translationMetadata?.[locale]?.[property],
|
|
2303
|
+
options
|
|
2096
2304
|
);
|
|
2097
2305
|
descriptors.push({
|
|
2098
2306
|
slot,
|
|
2307
|
+
manual: options.isManualTranslation?.(schema.translationMetadata?.[locale]?.[property], {
|
|
2308
|
+
path: slot.path ?? "",
|
|
2309
|
+
locale
|
|
2310
|
+
}) ?? isManualTranslationMetadata(schema.translationMetadata?.[locale]?.[property]),
|
|
2099
2311
|
apply: (current, value, metadata) => withTranslationMetadata(
|
|
2100
2312
|
{ ...current, translations: mergeLocalizedText(current.translations, locale, property, value) },
|
|
2101
2313
|
locale,
|
|
@@ -2117,10 +2329,15 @@ function translationSlots(schema, locale) {
|
|
|
2117
2329
|
sourceText,
|
|
2118
2330
|
field.translations?.[locale]?.[property],
|
|
2119
2331
|
field.metadata,
|
|
2120
|
-
field.translationMetadata?.[locale]?.[property]
|
|
2332
|
+
field.translationMetadata?.[locale]?.[property],
|
|
2333
|
+
options
|
|
2121
2334
|
);
|
|
2122
2335
|
descriptors.push({
|
|
2123
2336
|
slot,
|
|
2337
|
+
manual: options.isManualTranslation?.(field.translationMetadata?.[locale]?.[property], {
|
|
2338
|
+
path: slot.path ?? "",
|
|
2339
|
+
locale
|
|
2340
|
+
}) ?? isManualTranslationMetadata(field.translationMetadata?.[locale]?.[property]),
|
|
2124
2341
|
apply: (current, value, metadata) => ({
|
|
2125
2342
|
...current,
|
|
2126
2343
|
fields: current.fields.map(
|
|
@@ -2149,10 +2366,15 @@ function translationSlots(schema, locale) {
|
|
|
2149
2366
|
option.label,
|
|
2150
2367
|
option.translations?.[locale],
|
|
2151
2368
|
option.metadata,
|
|
2152
|
-
option.translationMetadata?.[locale]?.label
|
|
2369
|
+
option.translationMetadata?.[locale]?.label,
|
|
2370
|
+
options
|
|
2153
2371
|
);
|
|
2154
2372
|
descriptors.push({
|
|
2155
2373
|
slot,
|
|
2374
|
+
manual: options.isManualTranslation?.(option.translationMetadata?.[locale]?.label, {
|
|
2375
|
+
path: slot.path ?? "",
|
|
2376
|
+
locale
|
|
2377
|
+
}) ?? isManualTranslationMetadata(option.translationMetadata?.[locale]?.label),
|
|
2156
2378
|
apply: (current, value, metadata) => ({
|
|
2157
2379
|
...current,
|
|
2158
2380
|
fields: current.fields.map((candidate, candidateIndex) => {
|
|
@@ -2187,10 +2409,15 @@ function translationSlots(schema, locale) {
|
|
|
2187
2409
|
sourceText,
|
|
2188
2410
|
page.translations?.[locale]?.[property],
|
|
2189
2411
|
page.metadata,
|
|
2190
|
-
page.translationMetadata?.[locale]?.[property]
|
|
2412
|
+
page.translationMetadata?.[locale]?.[property],
|
|
2413
|
+
options
|
|
2191
2414
|
);
|
|
2192
2415
|
descriptors.push({
|
|
2193
2416
|
slot,
|
|
2417
|
+
manual: options.isManualTranslation?.(page.translationMetadata?.[locale]?.[property], {
|
|
2418
|
+
path: slot.path ?? "",
|
|
2419
|
+
locale
|
|
2420
|
+
}) ?? isManualTranslationMetadata(page.translationMetadata?.[locale]?.[property]),
|
|
2194
2421
|
apply: (current, value, metadata) => ({
|
|
2195
2422
|
...current,
|
|
2196
2423
|
...current.pages === void 0 ? {} : {
|
|
@@ -2214,6 +2441,125 @@ function translationSlots(schema, locale) {
|
|
|
2214
2441
|
});
|
|
2215
2442
|
return descriptors;
|
|
2216
2443
|
}
|
|
2444
|
+
function removeLocaleRecord(record, locale) {
|
|
2445
|
+
if (record === void 0) return void 0;
|
|
2446
|
+
const remaining = Object.entries(record).filter(([candidate]) => candidate !== locale);
|
|
2447
|
+
return remaining.length === 0 ? void 0 : Object.fromEntries(remaining);
|
|
2448
|
+
}
|
|
2449
|
+
function removeLocalizedNodeLocale(node, locale) {
|
|
2450
|
+
const translations = removeLocaleRecord(node.translations, locale);
|
|
2451
|
+
const translationMetadata = removeLocaleRecord(node.translationMetadata, locale);
|
|
2452
|
+
const { translations: _translations, translationMetadata: _translationMetadata, ...base } = node;
|
|
2453
|
+
return {
|
|
2454
|
+
...base,
|
|
2455
|
+
...translations === void 0 ? {} : { translations },
|
|
2456
|
+
...translationMetadata === void 0 ? {} : { translationMetadata }
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
function migrateMetadata(metadata, sourceText, defaultLocale, customMigrator) {
|
|
2460
|
+
if (customMigrator !== void 0) return customMigrator(metadata, sourceText);
|
|
2461
|
+
const record = metadata !== null && typeof metadata === "object" ? metadata : void 0;
|
|
2462
|
+
const sourceLocale = typeof record?.sourceLocale === "string" ? record.sourceLocale : defaultLocale ?? "";
|
|
2463
|
+
const translationSource = isManualTranslationMetadata(record) ? "manual" : "automatic";
|
|
2464
|
+
return {
|
|
2465
|
+
sourceLocale,
|
|
2466
|
+
sourceTextHash: computeSourceTextHash(sourceText),
|
|
2467
|
+
translationSource,
|
|
2468
|
+
...typeof record?.translatedAt === "string" ? { translatedAt: record.translatedAt } : {},
|
|
2469
|
+
...typeof record?.editedAt === "string" ? { editedAt: record.editedAt } : {}
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
function migrateNodeMetadata(node, sourceTexts, defaultLocale, customMigrator) {
|
|
2473
|
+
if (node.translationMetadata === void 0) return node;
|
|
2474
|
+
const translationMetadata = Object.fromEntries(
|
|
2475
|
+
Object.entries(node.translationMetadata).map(([locale, properties]) => [
|
|
2476
|
+
locale,
|
|
2477
|
+
Object.fromEntries(
|
|
2478
|
+
Object.entries(properties).map(([property, metadata]) => [
|
|
2479
|
+
property,
|
|
2480
|
+
migrateMetadata(metadata, sourceTexts[property] ?? "", defaultLocale, customMigrator)
|
|
2481
|
+
])
|
|
2482
|
+
)
|
|
2483
|
+
])
|
|
2484
|
+
);
|
|
2485
|
+
return { ...node, translationMetadata };
|
|
2486
|
+
}
|
|
2487
|
+
var migrateSchemaTranslationMetadata = (schema, customMigrator) => {
|
|
2488
|
+
const migratedSchema = migrateNodeMetadata(
|
|
2489
|
+
schema,
|
|
2490
|
+
{
|
|
2491
|
+
title: schema.title,
|
|
2492
|
+
...schema.description === void 0 ? {} : { description: schema.description },
|
|
2493
|
+
...schema.completionMessage === void 0 ? {} : { completionMessage: schema.completionMessage }
|
|
2494
|
+
},
|
|
2495
|
+
schema.defaultLocale,
|
|
2496
|
+
customMigrator
|
|
2497
|
+
);
|
|
2498
|
+
const fields = schema.fields.map((field) => {
|
|
2499
|
+
const migratedField = migrateNodeMetadata(
|
|
2500
|
+
field,
|
|
2501
|
+
{ title: field.title, ...field.description === void 0 ? {} : { description: field.description } },
|
|
2502
|
+
schema.defaultLocale,
|
|
2503
|
+
customMigrator
|
|
2504
|
+
);
|
|
2505
|
+
if (!("options" in migratedField)) return migratedField;
|
|
2506
|
+
return {
|
|
2507
|
+
...migratedField,
|
|
2508
|
+
options: migratedField.options.map(
|
|
2509
|
+
(option) => migrateNodeMetadata(option, { label: option.label }, schema.defaultLocale, customMigrator)
|
|
2510
|
+
)
|
|
2511
|
+
};
|
|
2512
|
+
});
|
|
2513
|
+
const pages = schema.pages?.map(
|
|
2514
|
+
(page) => migrateNodeMetadata(
|
|
2515
|
+
page,
|
|
2516
|
+
{
|
|
2517
|
+
...page.title === void 0 ? {} : { title: page.title },
|
|
2518
|
+
...page.description === void 0 ? {} : { description: page.description }
|
|
2519
|
+
},
|
|
2520
|
+
schema.defaultLocale,
|
|
2521
|
+
customMigrator
|
|
2522
|
+
)
|
|
2523
|
+
);
|
|
2524
|
+
return {
|
|
2525
|
+
...migratedSchema,
|
|
2526
|
+
fields,
|
|
2527
|
+
...pages === void 0 ? {} : { pages }
|
|
2528
|
+
};
|
|
2529
|
+
};
|
|
2530
|
+
var removeLocaleFromSchema = (schema, localeToRemove) => {
|
|
2531
|
+
if (localeToRemove === schema.defaultLocale) {
|
|
2532
|
+
throw new Error(`Cannot remove defaultLocale: ${localeToRemove}`);
|
|
2533
|
+
}
|
|
2534
|
+
const form = removeLocalizedNodeLocale(schema, localeToRemove);
|
|
2535
|
+
const fields = schema.fields.map((field) => {
|
|
2536
|
+
const localizedField = removeLocalizedNodeLocale(field, localeToRemove);
|
|
2537
|
+
if (!("options" in localizedField)) return localizedField;
|
|
2538
|
+
return {
|
|
2539
|
+
...localizedField,
|
|
2540
|
+
options: localizedField.options.map((option) => {
|
|
2541
|
+
const translations = removeLocaleRecord(option.translations, localeToRemove);
|
|
2542
|
+
const translationMetadata = removeLocaleRecord(option.translationMetadata, localeToRemove);
|
|
2543
|
+
const { translations: _translations, translationMetadata: _translationMetadata, ...base } = option;
|
|
2544
|
+
return {
|
|
2545
|
+
...base,
|
|
2546
|
+
...translations === void 0 ? {} : { translations },
|
|
2547
|
+
...translationMetadata === void 0 ? {} : { translationMetadata }
|
|
2548
|
+
};
|
|
2549
|
+
})
|
|
2550
|
+
};
|
|
2551
|
+
});
|
|
2552
|
+
const pages = schema.pages?.map((page) => removeLocalizedNodeLocale(page, localeToRemove));
|
|
2553
|
+
return {
|
|
2554
|
+
...form,
|
|
2555
|
+
supportedLocales: (schema.supportedLocales ?? []).filter((locale) => locale !== localeToRemove),
|
|
2556
|
+
fields,
|
|
2557
|
+
...pages === void 0 ? {} : { pages }
|
|
2558
|
+
};
|
|
2559
|
+
};
|
|
2560
|
+
function collectTranslationSlots(schema, locale) {
|
|
2561
|
+
return translationSlots(schema, locale).map((descriptor) => descriptor.slot);
|
|
2562
|
+
}
|
|
2217
2563
|
function resolveLocalizedSchema(schema, targetLocale) {
|
|
2218
2564
|
if (targetLocale === void 0 || targetLocale.length === 0 || targetLocale === schema.defaultLocale) return schema;
|
|
2219
2565
|
const formTranslation = schema.translations?.[targetLocale];
|
|
@@ -2270,17 +2616,32 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2270
2616
|
}
|
|
2271
2617
|
const updatedSlots = [];
|
|
2272
2618
|
const skippedSlots = [];
|
|
2619
|
+
const staleSlots = [];
|
|
2620
|
+
const skippedReasons = {};
|
|
2273
2621
|
let result = schema;
|
|
2274
2622
|
for (const locale of locales) {
|
|
2275
|
-
const descriptors = translationSlots(schema, locale);
|
|
2623
|
+
const descriptors = translationSlots(schema, locale, options);
|
|
2624
|
+
if (options.markStaleTranslations ?? true) {
|
|
2625
|
+
for (const descriptor of descriptors) {
|
|
2626
|
+
if (descriptor.slot.status === "stale" || descriptor.slot.status === "manual-stale")
|
|
2627
|
+
staleSlots.push(descriptor.slot);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2276
2630
|
const selected = [];
|
|
2277
2631
|
for (const descriptor of descriptors) {
|
|
2278
|
-
const
|
|
2632
|
+
const status = descriptor.slot.status ?? "missing";
|
|
2633
|
+
const manual = descriptor.manual || status === "manual" || status === "manual-stale";
|
|
2634
|
+
const requestedOverwrite = options.shouldOverwrite?.(descriptor.slot);
|
|
2635
|
+
const shouldTranslate = (options.preserveManualTranslations ?? true) && manual ? false : requestedOverwrite ?? (options.overwrite === "all" || (options.overwrite === "stale-and-missing" || options.overwrite === void 0 ? status === "missing" || status === "stale" || !(options.preserveManualTranslations ?? true) && status === "manual-stale" : status === "missing"));
|
|
2279
2636
|
if (shouldTranslate) selected.push(descriptor);
|
|
2280
|
-
else
|
|
2637
|
+
else {
|
|
2638
|
+
skippedSlots.push(descriptor.slot);
|
|
2639
|
+
skippedReasons[descriptor.slot.path ?? `${descriptor.slot.kind}.${descriptor.slot.nodeId}.${descriptor.slot.property}`] = manual ? "manual" : "unchanged";
|
|
2640
|
+
}
|
|
2281
2641
|
}
|
|
2282
2642
|
if (selected.length === 0) continue;
|
|
2283
|
-
const translated = await
|
|
2643
|
+
const translated = await translateBatch(
|
|
2644
|
+
adapter,
|
|
2284
2645
|
selected.map((descriptor) => descriptor.slot.sourceText),
|
|
2285
2646
|
locale,
|
|
2286
2647
|
schema.defaultLocale
|
|
@@ -2291,7 +2652,12 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2291
2652
|
selected.forEach((descriptor, index) => {
|
|
2292
2653
|
const translatedText = translated[index];
|
|
2293
2654
|
if (translatedText === void 0) throw new Error("Translation adapter returned an unexpected result.");
|
|
2294
|
-
const metadata = options.createMetadata?.(descriptor.slot, translatedText)
|
|
2655
|
+
const metadata = options.createMetadata?.(descriptor.slot, translatedText) ?? {
|
|
2656
|
+
sourceLocale: schema.defaultLocale ?? "",
|
|
2657
|
+
sourceTextHash: computeSourceTextHash(descriptor.slot.sourceText),
|
|
2658
|
+
translationSource: "automatic",
|
|
2659
|
+
translatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2660
|
+
};
|
|
2295
2661
|
result = descriptor.apply(result, translatedText, metadata);
|
|
2296
2662
|
updatedSlots.push(descriptor.slot);
|
|
2297
2663
|
});
|
|
@@ -2305,7 +2671,7 @@ async function populateSchemaTranslations(schema, targetLocales, adapter, option
|
|
|
2305
2671
|
];
|
|
2306
2672
|
if (supportedLocales.length > 0) result = { ...result, supportedLocales };
|
|
2307
2673
|
assertValidFormSchema(result);
|
|
2308
|
-
return { schema: result, report: { updatedSlots, skippedSlots } };
|
|
2674
|
+
return { schema: result, report: { updatedSlots, skippedSlots, staleSlots, skippedReasons } };
|
|
2309
2675
|
}
|
|
2310
2676
|
async function resolveFormTranslation(schema, adapter, targetLocale, sourceLocale) {
|
|
2311
2677
|
const populated = await populateSchemaTranslations(
|
|
@@ -2609,6 +2975,8 @@ function assertVersionMutable(status) {
|
|
|
2609
2975
|
calculatePageVisibility,
|
|
2610
2976
|
cloneVersionToDraft,
|
|
2611
2977
|
collectSchemaLocales,
|
|
2978
|
+
collectTranslationSlots,
|
|
2979
|
+
computeSourceTextHash,
|
|
2612
2980
|
createCloneTransitionPlan,
|
|
2613
2981
|
createDeleteDraftTransitionPlan,
|
|
2614
2982
|
createPublishTransitionPlan,
|
|
@@ -2623,16 +2991,21 @@ function assertVersionMutable(status) {
|
|
|
2623
2991
|
escapeCsvCell,
|
|
2624
2992
|
exportResponsesToCsv,
|
|
2625
2993
|
exportResponsesToCsvStream,
|
|
2994
|
+
getTranslationStatus,
|
|
2995
|
+
isDisplayConditionGroupSatisfied,
|
|
2626
2996
|
isDisplayConditionSatisfied,
|
|
2997
|
+
isManualTranslationMetadata,
|
|
2627
2998
|
isQuestionVisible,
|
|
2628
2999
|
iterateSubmissionPages,
|
|
2629
3000
|
jsonValuesEqual,
|
|
2630
3001
|
matchesSubmissionFilter,
|
|
2631
3002
|
matchesSubmissionPageFilters,
|
|
3003
|
+
migrateSchemaTranslationMetadata,
|
|
2632
3004
|
normalizeSubmissionPageSize,
|
|
2633
3005
|
pipeResponsesToCsvStream,
|
|
2634
3006
|
populateSchemaTranslations,
|
|
2635
3007
|
publishDraft,
|
|
3008
|
+
removeLocaleFromSchema,
|
|
2636
3009
|
resolveFormTranslation,
|
|
2637
3010
|
resolveLocalizedSchema,
|
|
2638
3011
|
sanitizeSchema,
|