@case-framework/survey-assistant 0.1.0 → 0.2.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 +71 -11
- package/dist/{authoring-references-X6uRaWuf.mjs → authoring-references-DYuOAdVx.mjs} +26 -2
- package/dist/authoring-references-DYuOAdVx.mjs.map +1 -0
- package/dist/{capabilities-B6tvE7fK.mjs → capabilities-DZ0a_o2y.mjs} +2 -2
- package/dist/{capabilities-B6tvE7fK.mjs.map → capabilities-DZ0a_o2y.mjs.map} +1 -1
- package/dist/{capabilities-DvSKtjAw.d.mts → capabilities-DlC1l6qy.d.mts} +4 -1
- package/dist/capabilities-DlC1l6qy.d.mts.map +1 -0
- package/dist/capabilities-default.d.mts +1 -1
- package/dist/capabilities-default.d.mts.map +1 -1
- package/dist/capabilities-default.mjs +6 -3
- package/dist/capabilities-default.mjs.map +1 -1
- package/dist/{constants-Jw0k8ojS.d.mts → constants-56jf_4ls.d.mts} +36 -9
- package/dist/constants-56jf_4ls.d.mts.map +1 -0
- package/dist/{controller-proxy-BRDusFiO.d.mts → controller-proxy-2VH0b_5E.d.mts} +29 -4
- package/dist/controller-proxy-2VH0b_5E.d.mts.map +1 -0
- package/dist/{controller-proxy-Duxo2IFE.mjs → controller-proxy-ClsY4lFI.mjs} +74 -5
- package/dist/controller-proxy-ClsY4lFI.mjs.map +1 -0
- package/dist/{digest-BsoVq49o.d.mts → digest-BvXpeWcR.d.mts} +2 -2
- package/dist/{digest-BsoVq49o.d.mts.map → digest-BvXpeWcR.d.mts.map} +1 -1
- package/dist/digest.d.mts +1 -1
- package/dist/{engine-sPvuixWg.mjs → engine-DQrCO_ur.mjs} +392 -50
- package/dist/engine-DQrCO_ur.mjs.map +1 -0
- package/dist/engine.d.mts +2 -2
- package/dist/engine.mjs +2 -2
- package/dist/{index-MpEMQzTm.d.mts → index-B_x2IYi7.d.mts} +5 -1
- package/dist/index-B_x2IYi7.d.mts.map +1 -0
- package/dist/{index-BWNHkpkh.d.mts → index-C_x686M7.d.mts} +27 -5
- package/dist/index-C_x686M7.d.mts.map +1 -0
- package/dist/{index-TFtvBiSQ.d.mts → index-DVxVyhAu.d.mts} +31 -9
- package/dist/index-DVxVyhAu.d.mts.map +1 -0
- package/dist/{index-CUqbBjkc.d.mts → index-DZ6KBwVR.d.mts} +16 -3
- package/dist/index-DZ6KBwVR.d.mts.map +1 -0
- package/dist/{memory-thread-repository-CJkkVE2-.mjs → memory-thread-repository-Cm1BJyRw.mjs} +110 -41
- package/dist/memory-thread-repository-Cm1BJyRw.mjs.map +1 -0
- package/dist/{protocol-DV0ka9WT.mjs → protocol-7EEgcYja.mjs} +70 -97
- package/dist/protocol-7EEgcYja.mjs.map +1 -0
- package/dist/protocol.d.mts +3 -3
- package/dist/protocol.mjs +2 -2
- package/dist/{react-VuYe-cPh.mjs → react-B_5smfiK.mjs} +145 -38
- package/dist/react-B_5smfiK.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -2
- package/dist/react-integration.mjs +2 -2
- package/dist/react.d.mts +3 -3
- package/dist/react.mjs +3 -3
- package/dist/references/assistant-operations.md +6 -0
- package/dist/references/core-rules.md +6 -0
- package/dist/references/embedded-forms.md +21 -14
- package/dist/references/expressions.md +3 -0
- package/dist/references/item-types.md +37 -10
- package/dist/references/source-material-surveys.md +9 -1
- package/dist/references/survey-data-model.md +13 -0
- package/dist/request-context-BDBw4-Gw.mjs +79 -0
- package/dist/request-context-BDBw4-Gw.mjs.map +1 -0
- package/dist/request-guardrails-h4t6u8mP.mjs +129 -0
- package/dist/request-guardrails-h4t6u8mP.mjs.map +1 -0
- package/dist/server-agent.d.mts +6 -2
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +39 -24
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +21 -5
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +98 -32
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +2 -78
- package/dist/server-tasks.mjs +103 -46
- package/dist/server-tasks.mjs.map +1 -1
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +89 -21
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +136 -5
- package/dist/server.mjs.map +1 -0
- package/dist/storage-postgres.d.mts +12 -2
- package/dist/storage-postgres.d.mts.map +1 -1
- package/dist/storage-postgres.mjs +11 -6
- package/dist/storage-postgres.mjs.map +1 -1
- package/dist/tasks-COQnhSeT.d.mts +140 -0
- package/dist/tasks-COQnhSeT.d.mts.map +1 -0
- package/dist/{thread-documents-DamIq9Od.d.mts → thread-documents-BlxNjc7S.d.mts} +21 -5
- package/dist/thread-documents-BlxNjc7S.d.mts.map +1 -0
- package/dist/{thread-handlers-DpynUc64.d.mts → thread-handlers-l79JBl5m.d.mts} +38 -5
- package/dist/thread-handlers-l79JBl5m.d.mts.map +1 -0
- package/dist/{tools-Hz2p7vyv.mjs → tools-DDXaJ8Za.mjs} +257 -38
- package/dist/tools-DDXaJ8Za.mjs.map +1 -0
- package/dist/ui.d.mts +5 -3
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +91 -12
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +234 -0
- package/package.json +9 -7
- package/dist/authoring-references-Cy8J-DWt.d.mts +0 -48
- package/dist/authoring-references-Cy8J-DWt.d.mts.map +0 -1
- package/dist/authoring-references-X6uRaWuf.mjs.map +0 -1
- package/dist/capabilities-DvSKtjAw.d.mts.map +0 -1
- package/dist/constants-Jw0k8ojS.d.mts.map +0 -1
- package/dist/controller-proxy-BRDusFiO.d.mts.map +0 -1
- package/dist/controller-proxy-Duxo2IFE.mjs.map +0 -1
- package/dist/engine-sPvuixWg.mjs.map +0 -1
- package/dist/index-BWNHkpkh.d.mts.map +0 -1
- package/dist/index-CUqbBjkc.d.mts.map +0 -1
- package/dist/index-MpEMQzTm.d.mts.map +0 -1
- package/dist/index-TFtvBiSQ.d.mts.map +0 -1
- package/dist/memory-thread-repository-CJkkVE2-.mjs.map +0 -1
- package/dist/protocol-DV0ka9WT.mjs.map +0 -1
- package/dist/react-VuYe-cPh.mjs.map +0 -1
- package/dist/request-context-jcmXT_Q2.mjs +0 -44
- package/dist/request-context-jcmXT_Q2.mjs.map +0 -1
- package/dist/request-guardrails-C59IWnH8.mjs +0 -73
- package/dist/request-guardrails-C59IWnH8.mjs.map +0 -1
- package/dist/server-tasks.d.mts.map +0 -1
- package/dist/thread-documents-DamIq9Od.d.mts.map +0 -1
- package/dist/thread-handlers-DpynUc64.d.mts.map +0 -1
- package/dist/tools-Hz2p7vyv.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as assertSurveyAssistantCapabilities } from "./capabilities-
|
|
1
|
+
import { P as rawSurveyDocumentSchema, c as surveyAssistantTurnFinalSchema, u as surveyChangeSetSchema } from "./protocol-7EEgcYja.mjs";
|
|
2
|
+
import { n as assertSurveyAssistantCapabilities } from "./capabilities-DZ0a_o2y.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { ContentType, Expression, ExpressionType, FunctionExpressionNames, JsonPatchError, ReservedSurveyItemTypes, SurveyTranslations, ValueReferenceMethod, ValueType, applyJsonPatch, generateId, getContentPlainText, isContentEmpty, parseJsonPointer, structuredCloneMethod } from "@case-framework/survey-core";
|
|
5
5
|
//#region src/engine/context.ts
|
|
@@ -161,7 +161,7 @@ const buildDerivedContext = (snapshot, trustedCapabilities, requestedFocusItemId
|
|
|
161
161
|
inspection: {
|
|
162
162
|
surveyKey: explicitRoot?.key,
|
|
163
163
|
locales,
|
|
164
|
-
itemCount: rawItems.length,
|
|
164
|
+
itemCount: rawItems.filter((item) => item.id !== explicitRoot?.id).length,
|
|
165
165
|
outline,
|
|
166
166
|
focus,
|
|
167
167
|
focusItem,
|
|
@@ -476,6 +476,7 @@ const translationEntrySchema = z.object({
|
|
|
476
476
|
text: z.string()
|
|
477
477
|
});
|
|
478
478
|
const choiceOptionDetailsSchema = z.object({
|
|
479
|
+
position: z.number().int().positive().describe("One-based position in the stored option order."),
|
|
479
480
|
id: z.string(),
|
|
480
481
|
key: z.string().optional(),
|
|
481
482
|
labelContentKey: z.string(),
|
|
@@ -486,6 +487,7 @@ const choiceOptionDetailsSchema = z.object({
|
|
|
486
487
|
embeddedFormResponseSlotPrefix: z.string().optional()
|
|
487
488
|
});
|
|
488
489
|
const formSelectOptionDetailsSchema = z.object({
|
|
490
|
+
position: z.number().int().positive().describe("One-based position in the stored select-option order."),
|
|
489
491
|
id: z.string(),
|
|
490
492
|
value: z.string().optional(),
|
|
491
493
|
labelContentKey: z.string()
|
|
@@ -498,6 +500,7 @@ const formFieldGroupLayoutSchema = z.object({
|
|
|
498
500
|
xl: z.number().int().positive()
|
|
499
501
|
});
|
|
500
502
|
const formFieldDetailsSchema = z.object({
|
|
503
|
+
position: z.number().int().positive().describe("One-based position in this field group's stored field order."),
|
|
501
504
|
id: z.string(),
|
|
502
505
|
key: z.string(),
|
|
503
506
|
type: z.string(),
|
|
@@ -516,6 +519,7 @@ const formFieldDetailsSchema = z.object({
|
|
|
516
519
|
selectOptions: z.array(formSelectOptionDetailsSchema).optional()
|
|
517
520
|
});
|
|
518
521
|
const formFieldGroupDetailsSchema = z.object({
|
|
522
|
+
position: z.number().int().positive().describe("One-based position in the form's stored field-group order."),
|
|
519
523
|
id: z.string(),
|
|
520
524
|
titleContentKey: z.string(),
|
|
521
525
|
descriptionContentKey: z.string(),
|
|
@@ -533,7 +537,8 @@ const itemConfigDetailsSchema = z.object({
|
|
|
533
537
|
summary: z.array(z.string()),
|
|
534
538
|
mainResponseSlotId: z.string().optional(),
|
|
535
539
|
choiceOptions: z.array(choiceOptionDetailsSchema).optional(),
|
|
536
|
-
formFieldGroups: z.array(formFieldGroupDetailsSchema).optional()
|
|
540
|
+
formFieldGroups: z.array(formFieldGroupDetailsSchema).optional(),
|
|
541
|
+
listOrdering: z.record(z.string(), z.string()).optional().describe("Meaning of each list in these config details.")
|
|
537
542
|
});
|
|
538
543
|
const normalizeText = (value) => value?.trim().toLowerCase() ?? "";
|
|
539
544
|
const clampLimit = (limit) => Math.min(50, Math.max(1, limit ?? 12));
|
|
@@ -668,11 +673,13 @@ const buildChoiceConfigDetails = (rawItem) => {
|
|
|
668
673
|
const rawOptions = asArray(config?.options);
|
|
669
674
|
const mainResponseSlotId = asString(config?.id) ?? rawItem.id;
|
|
670
675
|
if (rawItem.itemType !== "choiceItem" && rawOptions.length === 0) return;
|
|
671
|
-
const choiceOptions = rawOptions.
|
|
676
|
+
const choiceOptions = rawOptions.map((option, index) => {
|
|
677
|
+
if (!isRecord$5(option)) return;
|
|
672
678
|
const id = asString(option.id);
|
|
673
679
|
if (!id) return;
|
|
674
680
|
const embeddedFormFieldGroups = asArray((isRecord$5(option.embeddedForm) ? option.embeddedForm : void 0)?.fieldGroups);
|
|
675
681
|
const details = {
|
|
682
|
+
position: index + 1,
|
|
676
683
|
id,
|
|
677
684
|
labelContentKey: getChoiceOptionLabelContentKey(id)
|
|
678
685
|
};
|
|
@@ -699,10 +706,11 @@ const buildChoiceConfigDetails = (rawItem) => {
|
|
|
699
706
|
`responseSlot=${mainResponseSlotId}`
|
|
700
707
|
],
|
|
701
708
|
mainResponseSlotId,
|
|
702
|
-
choiceOptions
|
|
709
|
+
choiceOptions,
|
|
710
|
+
listOrdering: { choiceOptions: config?.shuffleOptions === true ? "Stored configured order; positions are one-based. Respondents see a shuffled order, so this is not a stable respondent-visible order." : "Stored configured order; positions are one-based and match respondent-visible order." }
|
|
703
711
|
};
|
|
704
712
|
};
|
|
705
|
-
const buildFormFieldDetails = (field, translationNamespace, responseSlotPrefix) => {
|
|
713
|
+
const buildFormFieldDetails = (field, position, translationNamespace, responseSlotPrefix) => {
|
|
706
714
|
const id = asString(field.id);
|
|
707
715
|
const key = asString(field.key);
|
|
708
716
|
const type = asString(field.type);
|
|
@@ -711,10 +719,12 @@ const buildFormFieldDetails = (field, translationNamespace, responseSlotPrefix)
|
|
|
711
719
|
const controllerType = asString(controller?.type);
|
|
712
720
|
const validations = asArray(field.validations).filter(isRecord$5);
|
|
713
721
|
const validationMessageContentKeys = validations.map((validation) => asString(validation.id)).filter((validationId) => Boolean(validationId)).map((validationId) => getFormFieldContentKey(id, createContentKey("validations", validationId, "message"), translationNamespace));
|
|
714
|
-
const selectOptions = controllerType === "select" ? asArray(controller?.options).
|
|
722
|
+
const selectOptions = controllerType === "select" ? asArray(controller?.options).map((option, index) => {
|
|
723
|
+
if (!isRecord$5(option)) return;
|
|
715
724
|
const optionId = asString(option.id);
|
|
716
725
|
if (!optionId) return;
|
|
717
726
|
const details = {
|
|
727
|
+
position: index + 1,
|
|
718
728
|
id: optionId,
|
|
719
729
|
labelContentKey: getFormFieldOptionLabelContentKey(id, optionId, translationNamespace)
|
|
720
730
|
};
|
|
@@ -723,6 +733,7 @@ const buildFormFieldDetails = (field, translationNamespace, responseSlotPrefix)
|
|
|
723
733
|
return details;
|
|
724
734
|
}).filter((option) => Boolean(option)) : void 0;
|
|
725
735
|
return {
|
|
736
|
+
position,
|
|
726
737
|
id,
|
|
727
738
|
key,
|
|
728
739
|
type,
|
|
@@ -745,12 +756,14 @@ const buildFormConfigDetails = (rawItem, configOverride, translationNamespace, r
|
|
|
745
756
|
const configCandidate = configOverride ?? rawItem.config;
|
|
746
757
|
const rawGroups = asArray((isRecord$5(configCandidate) ? configCandidate : void 0)?.fieldGroups);
|
|
747
758
|
if (rawItem.itemType !== "formItem" && rawGroups.length === 0) return;
|
|
748
|
-
const formFieldGroups = rawGroups.
|
|
759
|
+
const formFieldGroups = rawGroups.map((group, groupIndex) => {
|
|
760
|
+
if (!isRecord$5(group)) return;
|
|
749
761
|
const id = asString(group.id);
|
|
750
762
|
if (!id) return;
|
|
751
|
-
const fields = asArray(group.fields).
|
|
763
|
+
const fields = asArray(group.fields).map((field, fieldIndex) => isRecord$5(field) ? buildFormFieldDetails(field, fieldIndex + 1, translationNamespace, responseSlotPrefix) : void 0).filter((field) => Boolean(field));
|
|
752
764
|
const layout = isRecord$5(group.layout) ? group.layout : void 0;
|
|
753
765
|
return {
|
|
766
|
+
position: groupIndex + 1,
|
|
754
767
|
id,
|
|
755
768
|
titleContentKey: getFormFieldGroupContentKey(id, "title", translationNamespace),
|
|
756
769
|
descriptionContentKey: getFormFieldGroupContentKey(id, "description", translationNamespace),
|
|
@@ -770,7 +783,12 @@ const buildFormConfigDetails = (rawItem, configOverride, translationNamespace, r
|
|
|
770
783
|
return {
|
|
771
784
|
kind: "form",
|
|
772
785
|
summary: [`form item config: ${formFieldGroups.length} group(s), ${fieldCount} field(s)`],
|
|
773
|
-
formFieldGroups
|
|
786
|
+
formFieldGroups,
|
|
787
|
+
listOrdering: {
|
|
788
|
+
formFieldGroups: "Stored configured order; positions are one-based.",
|
|
789
|
+
"formFieldGroups[].fields": "Stored configured order within each field group; positions are one-based.",
|
|
790
|
+
"formFieldGroups[].fields[].selectOptions": "Stored configured order within each select field; positions are one-based."
|
|
791
|
+
}
|
|
774
792
|
};
|
|
775
793
|
};
|
|
776
794
|
const buildItemConfigDetails = (rawItem) => {
|
|
@@ -1434,18 +1452,17 @@ const isRecord$3 = (value) => typeof value === "object" && value !== null && !Ar
|
|
|
1434
1452
|
const getString = (value) => typeof value === "string" ? value.trim() : void 0;
|
|
1435
1453
|
const normalizeChoiceMaxSelection = (config) => {
|
|
1436
1454
|
if (config.maxSelection === null) return null;
|
|
1437
|
-
if (typeof config.maxSelection === "number"
|
|
1455
|
+
if (typeof config.maxSelection === "number") return config.maxSelection;
|
|
1438
1456
|
if (typeof config.maxSelection === "string") {
|
|
1439
1457
|
const normalized = config.maxSelection.trim().toLowerCase();
|
|
1440
1458
|
if (normalized === "multiple" || normalized === "multi" || normalized === "unlimited" || normalized === "none") return null;
|
|
1441
|
-
|
|
1442
|
-
|
|
1459
|
+
if (/^[1-9]\d*$/.test(normalized)) return Number.parseInt(normalized, 10);
|
|
1460
|
+
return config.maxSelection;
|
|
1443
1461
|
}
|
|
1444
1462
|
const mode = getString(config.mode)?.toLowerCase() ?? getString(config.selectionType)?.toLowerCase() ?? getString(config.selectionMode)?.toLowerCase();
|
|
1445
1463
|
if (mode === "multiple" || mode === "multi" || mode === "multi-select" || mode === "multiselect" || mode === "checkbox" || mode === "checkboxes") return null;
|
|
1446
1464
|
return 1;
|
|
1447
1465
|
};
|
|
1448
|
-
const humanizeKey = (value) => value.replace(/[_-]+/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1449
1466
|
const normalizeLayout = (value) => {
|
|
1450
1467
|
const baseLayout = {
|
|
1451
1468
|
base: 1,
|
|
@@ -1456,10 +1473,7 @@ const normalizeLayout = (value) => {
|
|
|
1456
1473
|
};
|
|
1457
1474
|
if (!isRecord$3(value)) return baseLayout;
|
|
1458
1475
|
const normalized = { ...baseLayout };
|
|
1459
|
-
for (const key of Object.keys(normalized))
|
|
1460
|
-
const count = value[key];
|
|
1461
|
-
if (typeof count === "number" && Number.isFinite(count)) normalized[key] = Math.min(4, Math.max(1, Math.round(count)));
|
|
1462
|
-
}
|
|
1476
|
+
for (const key of Object.keys(normalized)) if (value[key] !== void 0) normalized[key] = value[key];
|
|
1463
1477
|
return normalized;
|
|
1464
1478
|
};
|
|
1465
1479
|
const normalizeKey = (value) => value.trim().replace(/[.\s]+/g, "_").replace(/[^A-Za-z0-9_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -1669,14 +1683,9 @@ const appendMarker = (text, marker) => {
|
|
|
1669
1683
|
};
|
|
1670
1684
|
const getOptionNote = (option) => getString(option.note) ?? getString(option.description) ?? getString(option.helpText) ?? getString(option.instruction);
|
|
1671
1685
|
const getFootnoteMarker = (option) => getString(option.footnoteMarker) ?? getString(option.marker);
|
|
1672
|
-
const hasEmbeddedForm = (option) => isRecord$3(option.embeddedForm);
|
|
1673
|
-
const stripTrailingEmbeddedFormCue = (text, option) => {
|
|
1674
|
-
if (!hasEmbeddedForm(option)) return text;
|
|
1675
|
-
return text.replace(/\s*[,;:]?\s*(namelijk|namely|please specify|specify|explain|explanation|toelichting)\s*[:.]?\s*$/iu, "").trim();
|
|
1676
|
-
};
|
|
1677
1686
|
const getOptionTranslationLabel = (option) => {
|
|
1678
1687
|
const directLabel = getString(option.label) ?? getString(option.text) ?? getString(option.title);
|
|
1679
|
-
if (directLabel) return appendMarker(
|
|
1688
|
+
if (directLabel) return appendMarker(appendParenthetical(directLabel, getOptionNote(option)), getFootnoteMarker(option));
|
|
1680
1689
|
if (!isRecord$3(option.translations)) return;
|
|
1681
1690
|
for (const localeValue of Object.values(option.translations)) {
|
|
1682
1691
|
if (!isRecord$3(localeValue)) continue;
|
|
@@ -1684,7 +1693,7 @@ const getOptionTranslationLabel = (option) => {
|
|
|
1684
1693
|
if (translatedLabel) {
|
|
1685
1694
|
const translatedNote = getString(localeValue.note) ?? getString(localeValue.description) ?? getString(localeValue.helpText) ?? getString(localeValue.instruction);
|
|
1686
1695
|
const translatedMarker = getString(localeValue.footnoteMarker) ?? getString(localeValue.marker);
|
|
1687
|
-
return appendMarker(
|
|
1696
|
+
return appendMarker(appendParenthetical(translatedLabel, translatedNote), translatedMarker);
|
|
1688
1697
|
}
|
|
1689
1698
|
}
|
|
1690
1699
|
};
|
|
@@ -1723,27 +1732,49 @@ const normalizeLooseField = (field) => {
|
|
|
1723
1732
|
const id = rawId ? normalizeKey(rawId) : void 0;
|
|
1724
1733
|
if (!id) return { translations: [] };
|
|
1725
1734
|
const rawType = getString(field.type) ?? getString(field.controlType) ?? "input";
|
|
1726
|
-
const type = rawType === "text" || rawType === "email" ? "input" : rawType === "checkbox" ? "switch" : rawType;
|
|
1727
|
-
const controller =
|
|
1728
|
-
type
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1735
|
+
const type = rawType === "text" || rawType === "email" ? "input" : rawType === "date" ? "datepicker" : rawType === "checkbox" ? "switch" : rawType;
|
|
1736
|
+
const controller = (() => {
|
|
1737
|
+
switch (type) {
|
|
1738
|
+
case "textarea": return {
|
|
1739
|
+
type: "textarea",
|
|
1740
|
+
rows: 3
|
|
1741
|
+
};
|
|
1742
|
+
case "number": return {
|
|
1743
|
+
type: "number",
|
|
1744
|
+
step: 1
|
|
1745
|
+
};
|
|
1746
|
+
case "datepicker": return {
|
|
1747
|
+
type: "datepicker",
|
|
1748
|
+
mode: "year-month-day"
|
|
1749
|
+
};
|
|
1750
|
+
case "switch": return {
|
|
1751
|
+
type: "switch",
|
|
1752
|
+
defaultChecked: false
|
|
1753
|
+
};
|
|
1754
|
+
case "select": return {
|
|
1755
|
+
type: "select",
|
|
1756
|
+
options: []
|
|
1757
|
+
};
|
|
1758
|
+
case "slider": return {
|
|
1759
|
+
type: "slider",
|
|
1760
|
+
min: 0,
|
|
1761
|
+
max: 100,
|
|
1762
|
+
step: 1,
|
|
1763
|
+
defaultValue: 50,
|
|
1764
|
+
showValue: true
|
|
1765
|
+
};
|
|
1766
|
+
default: return {
|
|
1767
|
+
type: "input",
|
|
1768
|
+
inputMode: rawType === "email" ? "email" : "text"
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
})();
|
|
1772
|
+
const translations = [];
|
|
1773
|
+
const label = getString(field.label);
|
|
1774
|
+
if (label) translations.push({
|
|
1744
1775
|
contentKey: `fields.${id}.label`,
|
|
1745
|
-
plainText:
|
|
1746
|
-
}
|
|
1776
|
+
plainText: label
|
|
1777
|
+
});
|
|
1747
1778
|
const placeholder = getString(field.placeholder);
|
|
1748
1779
|
if (placeholder) translations.push({
|
|
1749
1780
|
contentKey: `fields.${id}.placeholder`,
|
|
@@ -1762,7 +1793,7 @@ const normalizeLooseField = (field) => {
|
|
|
1762
1793
|
return {
|
|
1763
1794
|
field: {
|
|
1764
1795
|
id,
|
|
1765
|
-
key: getString(field.key) ?? id
|
|
1796
|
+
key: getString(field.key) ?? id,
|
|
1766
1797
|
type,
|
|
1767
1798
|
required: field.required === true,
|
|
1768
1799
|
controller: normalizeSelectControllerOptions({
|
|
@@ -2028,6 +2059,149 @@ const validateTranslationBranchShapes = ({ operation, survey }) => {
|
|
|
2028
2059
|
return issues;
|
|
2029
2060
|
};
|
|
2030
2061
|
const getRawItemsFromSurvey = (survey) => Array.isArray(survey.surveyItems) ? survey.surveyItems : [];
|
|
2062
|
+
const FORM_LAYOUT_BREAKPOINTS = [
|
|
2063
|
+
"base",
|
|
2064
|
+
"xs",
|
|
2065
|
+
"sm",
|
|
2066
|
+
"md",
|
|
2067
|
+
"xl"
|
|
2068
|
+
];
|
|
2069
|
+
const collectInvalidFieldGroupLayouts = ({ fieldGroups, itemId, prefix, invalid }) => {
|
|
2070
|
+
if (!Array.isArray(fieldGroups)) return;
|
|
2071
|
+
for (const [groupIndex, group] of fieldGroups.entries()) {
|
|
2072
|
+
if (!isRecord$2(group) || !isRecord$2(group.layout)) continue;
|
|
2073
|
+
const groupId = getTrimmedString(group.id) ?? String(groupIndex);
|
|
2074
|
+
for (const breakpoint of FORM_LAYOUT_BREAKPOINTS) {
|
|
2075
|
+
const value = group.layout[breakpoint];
|
|
2076
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 4)) {
|
|
2077
|
+
const path = `${prefix}.${groupId}.layout.${breakpoint}`;
|
|
2078
|
+
invalid.set(`${itemId}\u0000${path}`, {
|
|
2079
|
+
itemId,
|
|
2080
|
+
path,
|
|
2081
|
+
value
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
2087
|
+
const collectInvalidSurveyFormLayouts = (survey) => {
|
|
2088
|
+
const invalid = /* @__PURE__ */ new Map();
|
|
2089
|
+
for (const item of survey.surveyItems) {
|
|
2090
|
+
const config = isRecord$2(item.config) ? item.config : void 0;
|
|
2091
|
+
if (item.itemType === FORM_ITEM_TYPE) {
|
|
2092
|
+
collectInvalidFieldGroupLayouts({
|
|
2093
|
+
fieldGroups: config?.fieldGroups,
|
|
2094
|
+
itemId: item.id,
|
|
2095
|
+
prefix: "fieldGroups",
|
|
2096
|
+
invalid
|
|
2097
|
+
});
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
if (item.itemType !== CHOICE_ITEM_TYPE || !Array.isArray(config?.options)) continue;
|
|
2101
|
+
for (const option of config.options) {
|
|
2102
|
+
if (!isRecord$2(option) || !isRecord$2(option.embeddedForm)) continue;
|
|
2103
|
+
const optionId = getTrimmedString(option.id);
|
|
2104
|
+
if (optionId) collectInvalidFieldGroupLayouts({
|
|
2105
|
+
fieldGroups: option.embeddedForm.fieldGroups,
|
|
2106
|
+
itemId: item.id,
|
|
2107
|
+
prefix: `embeddedForms.${optionId}.fieldGroups`,
|
|
2108
|
+
invalid
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return invalid;
|
|
2113
|
+
};
|
|
2114
|
+
const addRequiredLocalizedContent = (required, itemId, contentKey) => {
|
|
2115
|
+
required.set(`${itemId}\u0000${contentKey}`, {
|
|
2116
|
+
itemId,
|
|
2117
|
+
contentKey
|
|
2118
|
+
});
|
|
2119
|
+
};
|
|
2120
|
+
const collectFieldLocalizedContent = ({ fieldGroups, itemId, prefix, required }) => {
|
|
2121
|
+
if (!Array.isArray(fieldGroups)) return;
|
|
2122
|
+
for (const group of fieldGroups) {
|
|
2123
|
+
if (!isRecord$2(group) || !Array.isArray(group.fields)) continue;
|
|
2124
|
+
for (const field of group.fields) {
|
|
2125
|
+
if (!isRecord$2(field)) continue;
|
|
2126
|
+
const fieldId = getTrimmedString(field.id);
|
|
2127
|
+
if (!fieldId) continue;
|
|
2128
|
+
addRequiredLocalizedContent(required, itemId, `${prefix}.${fieldId}.label`);
|
|
2129
|
+
const controller = isRecord$2(field.controller) ? field.controller : void 0;
|
|
2130
|
+
if (field.type !== "select" || controller?.type !== "select") continue;
|
|
2131
|
+
for (const option of Array.isArray(controller.options) ? controller.options : []) {
|
|
2132
|
+
const optionId = isRecord$2(option) ? getTrimmedString(option.id) : void 0;
|
|
2133
|
+
if (optionId) addRequiredLocalizedContent(required, itemId, `${prefix}.${fieldId}.options.${optionId}.label`);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
const collectRequiredLocalizedContent = (survey, snapshot) => {
|
|
2139
|
+
const required = /* @__PURE__ */ new Map();
|
|
2140
|
+
const manifestByItemType = new Map(snapshot.capabilities.itemTypes.map((entry) => [entry.itemType, entry]));
|
|
2141
|
+
for (const item of survey.surveyItems) {
|
|
2142
|
+
const manifest = manifestByItemType.get(item.itemType);
|
|
2143
|
+
for (const surface of manifest?.contentSurfaces ?? []) if (surface.requiredOnCreate && !surface.pattern) addRequiredLocalizedContent(required, item.id, surface.key);
|
|
2144
|
+
const config = isRecord$2(item.config) ? item.config : void 0;
|
|
2145
|
+
if (item.itemType === FORM_ITEM_TYPE) {
|
|
2146
|
+
collectFieldLocalizedContent({
|
|
2147
|
+
fieldGroups: config?.fieldGroups,
|
|
2148
|
+
itemId: item.id,
|
|
2149
|
+
prefix: "fields",
|
|
2150
|
+
required
|
|
2151
|
+
});
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
if (item.itemType !== CHOICE_ITEM_TYPE || !Array.isArray(config?.options)) continue;
|
|
2155
|
+
for (const option of config.options) {
|
|
2156
|
+
if (!isRecord$2(option)) continue;
|
|
2157
|
+
const optionId = getTrimmedString(option.id);
|
|
2158
|
+
if (!optionId) continue;
|
|
2159
|
+
addRequiredLocalizedContent(required, item.id, `options.${optionId}.label`);
|
|
2160
|
+
if (isRecord$2(option.embeddedForm)) collectFieldLocalizedContent({
|
|
2161
|
+
fieldGroups: option.embeddedForm.fieldGroups,
|
|
2162
|
+
itemId: item.id,
|
|
2163
|
+
prefix: `embeddedForms.${optionId}.fields`,
|
|
2164
|
+
required
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
return required;
|
|
2169
|
+
};
|
|
2170
|
+
const hasNonEmptySurveyTranslation = (survey, { itemId, contentKey }) => Object.values(survey.translations ?? {}).some((localeTranslations) => {
|
|
2171
|
+
const content = localeTranslations?.itemTranslations?.[itemId]?.[contentKey];
|
|
2172
|
+
return isContent(content) && !isContentEmpty(content);
|
|
2173
|
+
});
|
|
2174
|
+
const validateNewRawPatchLocalizedContent = ({ operation, originalSurvey, patchedSurvey, snapshot }) => {
|
|
2175
|
+
const existingRequired = collectRequiredLocalizedContent(originalSurvey, snapshot);
|
|
2176
|
+
const existingMissing = new Set(Array.from(existingRequired).filter(([, binding]) => !hasNonEmptySurveyTranslation(originalSurvey, binding)).map(([identity]) => identity));
|
|
2177
|
+
const missing = Array.from(collectRequiredLocalizedContent(patchedSurvey, snapshot)).filter(([identity, binding]) => {
|
|
2178
|
+
if (hasNonEmptySurveyTranslation(patchedSurvey, binding)) return false;
|
|
2179
|
+
return !existingMissing.has(identity);
|
|
2180
|
+
}).map(([, binding]) => binding);
|
|
2181
|
+
if (missing.length === 0) return [];
|
|
2182
|
+
return [createIssue({
|
|
2183
|
+
code: "missing-raw-patch-localized-content",
|
|
2184
|
+
message: `The raw patch introduces respondent-visible content without its required translation. Add non-empty Content values in the same patches array for: ${missing.map(({ itemId, contentKey }) => `/translations/<locale>/itemTranslations/${itemId}/${contentKey}`).join(", ")}. Content keys use config ids exactly and are case-sensitive; for embedded fields, embeddedForms.<optionId> uses the choice option's id, not its key or embeddedForm id.`,
|
|
2185
|
+
operationIndex: 0,
|
|
2186
|
+
operationId: getOperationId(operation),
|
|
2187
|
+
itemId: missing[0]?.itemId
|
|
2188
|
+
})];
|
|
2189
|
+
};
|
|
2190
|
+
const validateNewRawPatchFormLayouts = ({ operation, originalSurvey, patchedSurvey }) => {
|
|
2191
|
+
const existingInvalid = collectInvalidSurveyFormLayouts(originalSurvey);
|
|
2192
|
+
const introducedInvalid = Array.from(collectInvalidSurveyFormLayouts(patchedSurvey)).filter(([identity, entry]) => {
|
|
2193
|
+
const existing = existingInvalid.get(identity);
|
|
2194
|
+
return !existing || !Object.is(existing.value, entry.value);
|
|
2195
|
+
}).map(([, entry]) => entry);
|
|
2196
|
+
if (introducedInvalid.length === 0) return [];
|
|
2197
|
+
return [createIssue({
|
|
2198
|
+
code: "invalid-form-layout-columns",
|
|
2199
|
+
message: `Form layout values are responsive column counts from 1 to 4, not field spans. Use 1 for a lone full-width field. Invalid values: ${introducedInvalid.map(({ path, value }) => `${path}=${JSON.stringify(value)}`).join(", ")}.`,
|
|
2200
|
+
operationIndex: 0,
|
|
2201
|
+
operationId: getOperationId(operation),
|
|
2202
|
+
itemId: introducedInvalid[0]?.itemId
|
|
2203
|
+
})];
|
|
2204
|
+
};
|
|
2031
2205
|
const createStructureIssue = ({ code, message, operationId, itemId }) => createIssue({
|
|
2032
2206
|
code,
|
|
2033
2207
|
message,
|
|
@@ -2350,6 +2524,15 @@ const validateSurveyJsonPatchOperation = ({ operation, snapshot }) => {
|
|
|
2350
2524
|
issues.push(...validateTranslationBranchShapes({
|
|
2351
2525
|
operation,
|
|
2352
2526
|
survey: patchedSurvey
|
|
2527
|
+
}), ...validateNewRawPatchLocalizedContent({
|
|
2528
|
+
operation,
|
|
2529
|
+
originalSurvey: snapshot.survey,
|
|
2530
|
+
patchedSurvey,
|
|
2531
|
+
snapshot
|
|
2532
|
+
}), ...validateNewRawPatchFormLayouts({
|
|
2533
|
+
operation,
|
|
2534
|
+
originalSurvey: snapshot.survey,
|
|
2535
|
+
patchedSurvey
|
|
2353
2536
|
}), ...validateSurveyTreeStructure(patchedSurvey, operationId, new Set(snapshot.capabilities.itemTypes.map((entry) => entry.itemType))));
|
|
2354
2537
|
const documentResult = rawSurveyDocumentSchema.safeParse(patchedSurvey);
|
|
2355
2538
|
if (!documentResult.success) {
|
|
@@ -2763,6 +2946,66 @@ const getRawFields = (fieldGroups) => {
|
|
|
2763
2946
|
}
|
|
2764
2947
|
return fields;
|
|
2765
2948
|
};
|
|
2949
|
+
const getRequiredFormContentKeys = ({ fieldGroups, prefix }) => {
|
|
2950
|
+
const required = /* @__PURE__ */ new Set();
|
|
2951
|
+
for (const field of getRawFields(fieldGroups)) {
|
|
2952
|
+
const fieldId = getTrimmedString(field.id);
|
|
2953
|
+
if (!fieldId) continue;
|
|
2954
|
+
required.add(`${prefix}.${fieldId}.label`);
|
|
2955
|
+
const controller = isRecord$2(field.controller) ? field.controller : void 0;
|
|
2956
|
+
if (field.type !== "select" || controller?.type !== "select") continue;
|
|
2957
|
+
for (const option of Array.isArray(controller.options) ? controller.options : []) {
|
|
2958
|
+
const optionId = isRecord$2(option) ? getTrimmedString(option.id) : void 0;
|
|
2959
|
+
if (optionId) required.add(`${prefix}.${fieldId}.options.${optionId}.label`);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
return Array.from(required);
|
|
2963
|
+
};
|
|
2964
|
+
const getMissingFormOperationContentKeys = ({ fieldGroups, itemId, prefix, snapshot, translations }) => getRequiredFormContentKeys({
|
|
2965
|
+
fieldGroups,
|
|
2966
|
+
prefix
|
|
2967
|
+
}).filter((contentKey) => {
|
|
2968
|
+
if (translations?.some((translation) => translation.contentKey === contentKey && getTranslationPlainText(translation).length > 0)) return false;
|
|
2969
|
+
return !hasNonEmptySurveyTranslation(snapshot.survey, {
|
|
2970
|
+
itemId,
|
|
2971
|
+
contentKey
|
|
2972
|
+
});
|
|
2973
|
+
});
|
|
2974
|
+
const getInvalidFormLayoutIssue = ({ fieldGroups, itemId, operationId, operationIndex, prefix }) => {
|
|
2975
|
+
const invalid = /* @__PURE__ */ new Map();
|
|
2976
|
+
collectInvalidFieldGroupLayouts({
|
|
2977
|
+
fieldGroups,
|
|
2978
|
+
itemId,
|
|
2979
|
+
prefix,
|
|
2980
|
+
invalid
|
|
2981
|
+
});
|
|
2982
|
+
const entries = Array.from(invalid.values());
|
|
2983
|
+
if (entries.length === 0) return;
|
|
2984
|
+
return createIssue({
|
|
2985
|
+
code: "invalid-form-layout-columns",
|
|
2986
|
+
message: `Form layout values are responsive column counts from 1 to 4, not field spans. Use 1 for a lone full-width field. Invalid values: ${entries.map(({ path, value }) => `${path}=${JSON.stringify(value)}`).join(", ")}.`,
|
|
2987
|
+
operationIndex,
|
|
2988
|
+
operationId,
|
|
2989
|
+
itemId
|
|
2990
|
+
});
|
|
2991
|
+
};
|
|
2992
|
+
const getFormOperationContentIssue = ({ fieldGroups, itemId, operationId, operationIndex, prefix, snapshot, translations }) => {
|
|
2993
|
+
const missingContentKeys = getMissingFormOperationContentKeys({
|
|
2994
|
+
fieldGroups,
|
|
2995
|
+
itemId,
|
|
2996
|
+
prefix,
|
|
2997
|
+
snapshot,
|
|
2998
|
+
translations
|
|
2999
|
+
});
|
|
3000
|
+
if (missingContentKeys.length === 0) return;
|
|
3001
|
+
return createIssue({
|
|
3002
|
+
code: "missing-form-field-content",
|
|
3003
|
+
message: "Introduced form fields and select options need visible labels. Add non-empty translations for: " + missingContentKeys.join(", ") + ".",
|
|
3004
|
+
operationIndex,
|
|
3005
|
+
operationId,
|
|
3006
|
+
itemId
|
|
3007
|
+
});
|
|
3008
|
+
};
|
|
2766
3009
|
const getMissingSelectOptionLabelContentKeys = ({ fieldGroups, prefix, translations }) => {
|
|
2767
3010
|
const missingContentKeys = [];
|
|
2768
3011
|
for (const field of getRawFields(fieldGroups)) {
|
|
@@ -2890,6 +3133,30 @@ const getChoiceEmbeddedFormFieldLabelIssue = ({ operation, operationId, operatio
|
|
|
2890
3133
|
itemId
|
|
2891
3134
|
});
|
|
2892
3135
|
};
|
|
3136
|
+
const getCreateFormLayoutIssue = ({ operation, operationId, operationIndex, itemId }) => {
|
|
3137
|
+
if (!isRecord$2(operation.item.config)) return;
|
|
3138
|
+
if (operation.item.itemType === FORM_ITEM_TYPE) return getInvalidFormLayoutIssue({
|
|
3139
|
+
fieldGroups: operation.item.config.fieldGroups,
|
|
3140
|
+
itemId,
|
|
3141
|
+
operationId,
|
|
3142
|
+
operationIndex,
|
|
3143
|
+
prefix: "fieldGroups"
|
|
3144
|
+
});
|
|
3145
|
+
if (operation.item.itemType !== CHOICE_ITEM_TYPE || !Array.isArray(operation.item.config.options)) return;
|
|
3146
|
+
for (const option of operation.item.config.options) {
|
|
3147
|
+
if (!isRecord$2(option) || !isRecord$2(option.embeddedForm)) continue;
|
|
3148
|
+
const optionId = getTrimmedString(option.id);
|
|
3149
|
+
if (!optionId) continue;
|
|
3150
|
+
const issue = getInvalidFormLayoutIssue({
|
|
3151
|
+
fieldGroups: option.embeddedForm.fieldGroups,
|
|
3152
|
+
itemId,
|
|
3153
|
+
operationId,
|
|
3154
|
+
operationIndex,
|
|
3155
|
+
prefix: `embeddedForms.${optionId}.fieldGroups`
|
|
3156
|
+
});
|
|
3157
|
+
if (issue) return issue;
|
|
3158
|
+
}
|
|
3159
|
+
};
|
|
2893
3160
|
const resolveRawItemByIdentifier = ({ identifier, indexById, outlineById, rawItemsById }) => {
|
|
2894
3161
|
const direct = rawItemsById.get(identifier);
|
|
2895
3162
|
if (direct) return {
|
|
@@ -3310,6 +3577,13 @@ const validateSurveyOperations = ({ operations, snapshot: snapshotInput }) => {
|
|
|
3310
3577
|
itemId
|
|
3311
3578
|
});
|
|
3312
3579
|
if (formFieldLabelIssue) issues.push(formFieldLabelIssue);
|
|
3580
|
+
const formLayoutIssue = getCreateFormLayoutIssue({
|
|
3581
|
+
operation: normalized,
|
|
3582
|
+
operationId,
|
|
3583
|
+
operationIndex,
|
|
3584
|
+
itemId
|
|
3585
|
+
});
|
|
3586
|
+
if (formLayoutIssue) issues.push(formLayoutIssue);
|
|
3313
3587
|
for (const translation of normalized.translations ?? []) {
|
|
3314
3588
|
addIntroducedLocaleWarning({
|
|
3315
3589
|
locale: translation.locale,
|
|
@@ -3663,6 +3937,27 @@ const validateSurveyOperations = ({ operations, snapshot: snapshotInput }) => {
|
|
|
3663
3937
|
}));
|
|
3664
3938
|
break;
|
|
3665
3939
|
}
|
|
3940
|
+
const layoutIssue = getInvalidFormLayoutIssue({
|
|
3941
|
+
fieldGroups: [normalized.group],
|
|
3942
|
+
itemId: item.itemId,
|
|
3943
|
+
operationId,
|
|
3944
|
+
operationIndex,
|
|
3945
|
+
prefix: normalized.target.optionId ? `embeddedForms.${normalized.target.optionId}.fieldGroups` : "fieldGroups"
|
|
3946
|
+
});
|
|
3947
|
+
const contentIssue = Array.isArray(normalized.group.fields) ? getFormOperationContentIssue({
|
|
3948
|
+
fieldGroups: [normalized.group],
|
|
3949
|
+
itemId: item.itemId,
|
|
3950
|
+
operationId,
|
|
3951
|
+
operationIndex,
|
|
3952
|
+
prefix: normalized.target.optionId ? `embeddedForms.${normalized.target.optionId}.fields` : "fields",
|
|
3953
|
+
snapshot,
|
|
3954
|
+
translations: normalized.translations
|
|
3955
|
+
}) : void 0;
|
|
3956
|
+
if (layoutIssue || contentIssue) {
|
|
3957
|
+
if (layoutIssue) issues.push(layoutIssue);
|
|
3958
|
+
if (contentIssue) issues.push(contentIssue);
|
|
3959
|
+
break;
|
|
3960
|
+
}
|
|
3666
3961
|
const existingIndex = groups.findIndex((group) => group.id === normalized.group.id);
|
|
3667
3962
|
const existing = existingIndex >= 0 ? groups[existingIndex] : void 0;
|
|
3668
3963
|
const nextGroup = normalizeFormFieldGroupRecord({
|
|
@@ -3739,6 +4034,19 @@ const validateSurveyOperations = ({ operations, snapshot: snapshotInput }) => {
|
|
|
3739
4034
|
}));
|
|
3740
4035
|
break;
|
|
3741
4036
|
}
|
|
4037
|
+
const contentIssue = getFormOperationContentIssue({
|
|
4038
|
+
fieldGroups: [{ fields: [normalized.field] }],
|
|
4039
|
+
itemId: item.itemId,
|
|
4040
|
+
operationId,
|
|
4041
|
+
operationIndex,
|
|
4042
|
+
prefix: normalized.target.optionId ? `embeddedForms.${normalized.target.optionId}.fields` : "fields",
|
|
4043
|
+
snapshot,
|
|
4044
|
+
translations: normalized.translations
|
|
4045
|
+
});
|
|
4046
|
+
if (contentIssue) {
|
|
4047
|
+
issues.push(contentIssue);
|
|
4048
|
+
break;
|
|
4049
|
+
}
|
|
3742
4050
|
if (groups.flatMap((group) => group.fields).find((field) => field.id !== normalized.field.id && field.key === normalized.field.key)) {
|
|
3743
4051
|
issues.push(createIssue({
|
|
3744
4052
|
code: "duplicate-form-field-key",
|
|
@@ -3858,6 +4166,27 @@ const validateSurveyOperations = ({ operations, snapshot: snapshotInput }) => {
|
|
|
3858
4166
|
}
|
|
3859
4167
|
options[optionIndex] = { ...options[optionIndex] };
|
|
3860
4168
|
if (normalized.kind === "set-embedded-form") {
|
|
4169
|
+
const layoutIssue = getInvalidFormLayoutIssue({
|
|
4170
|
+
fieldGroups: normalized.form.fieldGroups,
|
|
4171
|
+
itemId: item.itemId,
|
|
4172
|
+
operationId,
|
|
4173
|
+
operationIndex,
|
|
4174
|
+
prefix: `embeddedForms.${normalized.optionId}.fieldGroups`
|
|
4175
|
+
});
|
|
4176
|
+
const contentIssue = getFormOperationContentIssue({
|
|
4177
|
+
fieldGroups: normalized.form.fieldGroups,
|
|
4178
|
+
itemId: item.itemId,
|
|
4179
|
+
operationId,
|
|
4180
|
+
operationIndex,
|
|
4181
|
+
prefix: `embeddedForms.${normalized.optionId}.fields`,
|
|
4182
|
+
snapshot,
|
|
4183
|
+
translations: normalized.translations
|
|
4184
|
+
});
|
|
4185
|
+
if (layoutIssue || contentIssue) {
|
|
4186
|
+
if (layoutIssue) issues.push(layoutIssue);
|
|
4187
|
+
if (contentIssue) issues.push(contentIssue);
|
|
4188
|
+
break;
|
|
4189
|
+
}
|
|
3861
4190
|
options[optionIndex].embeddedForm = normalized.form;
|
|
3862
4191
|
for (const translation of normalized.translations ?? []) addIntroducedLocaleWarning({
|
|
3863
4192
|
locale: translation.locale,
|
|
@@ -4280,6 +4609,18 @@ var TurnChangeCollector = class {
|
|
|
4280
4609
|
const ambiguousModeOnly = modeRequired && !candidateWasRejected && continuationIssue?.code === "change-mode-required";
|
|
4281
4610
|
const shouldClearActive = continuationMode === "replace" || continuationIssue !== void 0 && !ambiguousModeOnly || candidateWasRejected;
|
|
4282
4611
|
const supersedesChangeIds = shouldClearActive ? [...this.#activeChangeSetIds] : [];
|
|
4612
|
+
const recovery = continuationIssue !== void 0 || candidateWasRejected ? ambiguousModeOnly ? {
|
|
4613
|
+
turnAuthorization: "blocked",
|
|
4614
|
+
candidateAuthorization: "not-authorized",
|
|
4615
|
+
requiredAction: "choose-continuation-mode",
|
|
4616
|
+
allowedContinuationModes: ["append", "replace"]
|
|
4617
|
+
} : {
|
|
4618
|
+
turnAuthorization: "blocked",
|
|
4619
|
+
candidateAuthorization: "not-authorized",
|
|
4620
|
+
requiredAction: "resubmit-complete-proposal",
|
|
4621
|
+
allowedContinuationModes: ["replace"],
|
|
4622
|
+
requiredContinuationMode: "replace"
|
|
4623
|
+
} : void 0;
|
|
4283
4624
|
const registered = surveyChangeSetSchema.parse({
|
|
4284
4625
|
...candidate,
|
|
4285
4626
|
turnId: this.turnId,
|
|
@@ -4291,7 +4632,8 @@ var TurnChangeCollector = class {
|
|
|
4291
4632
|
issues: [...candidate.validation.issues, continuationIssue]
|
|
4292
4633
|
}
|
|
4293
4634
|
} : {},
|
|
4294
|
-
...supersedesChangeIds.length > 0 ? { supersedesChangeIds } : {}
|
|
4635
|
+
...supersedesChangeIds.length > 0 ? { supersedesChangeIds } : {},
|
|
4636
|
+
...recovery ? { recovery } : {}
|
|
4295
4637
|
});
|
|
4296
4638
|
this.#candidates.set(registered.id, registered);
|
|
4297
4639
|
const isValidated = registered.status === "validated" && registered.validation.valid;
|
|
@@ -4840,7 +5182,7 @@ const auditSurvey = (context, options = {}) => {
|
|
|
4840
5182
|
total: 0
|
|
4841
5183
|
}),
|
|
4842
5184
|
metrics: {
|
|
4843
|
-
itemCount:
|
|
5185
|
+
itemCount: context.inspection.itemCount,
|
|
4844
5186
|
localeCount: locales.length,
|
|
4845
5187
|
responseSlotCount: context.inspection.responseSlots.length,
|
|
4846
5188
|
assetCount: Object.keys(assets).length,
|
|
@@ -4854,4 +5196,4 @@ const auditSurvey = (context, options = {}) => {
|
|
|
4854
5196
|
//#endregion
|
|
4855
5197
|
export { deriveResponseSlotTypes as $, getRawSurvey$1 as A, resolveItemSummary as B, clampLimit as C, formFieldGroupLayoutSchema as D, formFieldGroupDetailsSchema as E, mapKeyIndexById as F, summarizeKeyIndexEntry as G, summarizeFocusItemNode as H, mapOutlineById as I, translationEntrySchema as J, summarizeOutlineNode as K, mapRawItemsById as L, itemConfigDetailsSchema as M, itemMatchesQuery as N, formSelectOptionDetailsSchema as O, itemSummarySchema as P, createSurveyAssistantEngineContext as Q, normalizeText as R, choiceOptionDetailsSchema as S, formFieldDetailsSchema as T, summarizeItemById as U, sliceWithCursor as V, summarizeKeyIndexEntries as W, inferExpressionReturnType as X, unique as Y, validateExpression as Z, buildConfigSearchText as _, SURVEY_CHANGE_CONTINUATION_MODES as a, buildItemTranslationEntries as b, buildSurveyDocumentVersion as c, normalizeSurveyChangeOperations as d, buildExpressionMutationPatches as f, MAX_LIMIT as g, DEFAULT_LIMIT as h, SURVEY_CHANGE_CONTINUATION_DESCRIPTION as i, getRawSurveyItems as j, getKeyIndexEntries as k, createSurveyChangeSet as l, expressionSchema as m, SurveyAssistantTaskResultCache as n, TurnChangeCollector as o, expressionMutationSchema as p, summarizeRawItem as q, buildSurveyAssistantTaskContext as r, MAX_SURVEY_ASSISTANT_OPERATIONS_PER_PROPOSAL as s, auditSurvey as t, validateSurveyOperations as u, buildEmbeddedFormDetails as v, findDirectChildrenByFullKey as w, buildSurveyTranslations as x, buildItemConfigDetails as y, parseCursor as z };
|
|
4856
5198
|
|
|
4857
|
-
//# sourceMappingURL=engine-
|
|
5199
|
+
//# sourceMappingURL=engine-DQrCO_ur.mjs.map
|