@case-framework/survey-assistant 0.1.0 → 0.1.1

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.
Files changed (113) hide show
  1. package/README.md +69 -9
  2. package/dist/{authoring-references-X6uRaWuf.mjs → authoring-references-DYuOAdVx.mjs} +26 -2
  3. package/dist/authoring-references-DYuOAdVx.mjs.map +1 -0
  4. package/dist/{capabilities-B6tvE7fK.mjs → capabilities-DZ0a_o2y.mjs} +2 -2
  5. package/dist/{capabilities-B6tvE7fK.mjs.map → capabilities-DZ0a_o2y.mjs.map} +1 -1
  6. package/dist/{capabilities-DvSKtjAw.d.mts → capabilities-DlC1l6qy.d.mts} +4 -1
  7. package/dist/capabilities-DlC1l6qy.d.mts.map +1 -0
  8. package/dist/capabilities-default.d.mts +1 -1
  9. package/dist/capabilities-default.d.mts.map +1 -1
  10. package/dist/capabilities-default.mjs +6 -3
  11. package/dist/capabilities-default.mjs.map +1 -1
  12. package/dist/{constants-Jw0k8ojS.d.mts → constants-56jf_4ls.d.mts} +36 -9
  13. package/dist/constants-56jf_4ls.d.mts.map +1 -0
  14. package/dist/{controller-proxy-BRDusFiO.d.mts → controller-proxy-2VH0b_5E.d.mts} +29 -4
  15. package/dist/controller-proxy-2VH0b_5E.d.mts.map +1 -0
  16. package/dist/{controller-proxy-Duxo2IFE.mjs → controller-proxy-ClsY4lFI.mjs} +74 -5
  17. package/dist/controller-proxy-ClsY4lFI.mjs.map +1 -0
  18. package/dist/{digest-BsoVq49o.d.mts → digest-BvXpeWcR.d.mts} +2 -2
  19. package/dist/{digest-BsoVq49o.d.mts.map → digest-BvXpeWcR.d.mts.map} +1 -1
  20. package/dist/digest.d.mts +1 -1
  21. package/dist/{engine-sPvuixWg.mjs → engine-DQrCO_ur.mjs} +392 -50
  22. package/dist/engine-DQrCO_ur.mjs.map +1 -0
  23. package/dist/engine.d.mts +2 -2
  24. package/dist/engine.mjs +2 -2
  25. package/dist/{index-MpEMQzTm.d.mts → index-B_x2IYi7.d.mts} +5 -1
  26. package/dist/index-B_x2IYi7.d.mts.map +1 -0
  27. package/dist/{index-BWNHkpkh.d.mts → index-C_x686M7.d.mts} +27 -5
  28. package/dist/index-C_x686M7.d.mts.map +1 -0
  29. package/dist/{index-TFtvBiSQ.d.mts → index-DVxVyhAu.d.mts} +31 -9
  30. package/dist/index-DVxVyhAu.d.mts.map +1 -0
  31. package/dist/{index-CUqbBjkc.d.mts → index-DZ6KBwVR.d.mts} +16 -3
  32. package/dist/index-DZ6KBwVR.d.mts.map +1 -0
  33. package/dist/{memory-thread-repository-CJkkVE2-.mjs → memory-thread-repository-Cm1BJyRw.mjs} +110 -41
  34. package/dist/memory-thread-repository-Cm1BJyRw.mjs.map +1 -0
  35. package/dist/{protocol-DV0ka9WT.mjs → protocol-7EEgcYja.mjs} +70 -97
  36. package/dist/protocol-7EEgcYja.mjs.map +1 -0
  37. package/dist/protocol.d.mts +3 -3
  38. package/dist/protocol.mjs +2 -2
  39. package/dist/{react-VuYe-cPh.mjs → react-B_5smfiK.mjs} +145 -38
  40. package/dist/react-B_5smfiK.mjs.map +1 -0
  41. package/dist/react-integration.d.mts +2 -2
  42. package/dist/react-integration.mjs +2 -2
  43. package/dist/react.d.mts +3 -3
  44. package/dist/react.mjs +3 -3
  45. package/dist/references/assistant-operations.md +6 -0
  46. package/dist/references/core-rules.md +6 -0
  47. package/dist/references/embedded-forms.md +21 -14
  48. package/dist/references/expressions.md +3 -0
  49. package/dist/references/item-types.md +37 -10
  50. package/dist/references/source-material-surveys.md +9 -1
  51. package/dist/references/survey-data-model.md +13 -0
  52. package/dist/request-context-BDBw4-Gw.mjs +79 -0
  53. package/dist/request-context-BDBw4-Gw.mjs.map +1 -0
  54. package/dist/request-guardrails-h4t6u8mP.mjs +129 -0
  55. package/dist/request-guardrails-h4t6u8mP.mjs.map +1 -0
  56. package/dist/server-agent.d.mts +6 -2
  57. package/dist/server-agent.d.mts.map +1 -1
  58. package/dist/server-agent.mjs +39 -24
  59. package/dist/server-agent.mjs.map +1 -1
  60. package/dist/server-runtime.d.mts +21 -5
  61. package/dist/server-runtime.d.mts.map +1 -1
  62. package/dist/server-runtime.mjs +98 -32
  63. package/dist/server-runtime.mjs.map +1 -1
  64. package/dist/server-tasks.d.mts +2 -78
  65. package/dist/server-tasks.mjs +103 -46
  66. package/dist/server-tasks.mjs.map +1 -1
  67. package/dist/server-tools.d.mts +1 -1
  68. package/dist/server-tools.mjs +1 -1
  69. package/dist/server.d.mts +89 -21
  70. package/dist/server.d.mts.map +1 -1
  71. package/dist/server.mjs +136 -5
  72. package/dist/server.mjs.map +1 -0
  73. package/dist/storage-postgres.d.mts +12 -2
  74. package/dist/storage-postgres.d.mts.map +1 -1
  75. package/dist/storage-postgres.mjs +11 -6
  76. package/dist/storage-postgres.mjs.map +1 -1
  77. package/dist/tasks-COQnhSeT.d.mts +140 -0
  78. package/dist/tasks-COQnhSeT.d.mts.map +1 -0
  79. package/dist/{thread-documents-DamIq9Od.d.mts → thread-documents-BlxNjc7S.d.mts} +21 -5
  80. package/dist/thread-documents-BlxNjc7S.d.mts.map +1 -0
  81. package/dist/{thread-handlers-DpynUc64.d.mts → thread-handlers-l79JBl5m.d.mts} +38 -5
  82. package/dist/thread-handlers-l79JBl5m.d.mts.map +1 -0
  83. package/dist/{tools-Hz2p7vyv.mjs → tools-DDXaJ8Za.mjs} +257 -38
  84. package/dist/tools-DDXaJ8Za.mjs.map +1 -0
  85. package/dist/ui.d.mts +5 -3
  86. package/dist/ui.d.mts.map +1 -1
  87. package/dist/ui.mjs +91 -12
  88. package/dist/ui.mjs.map +1 -1
  89. package/docs/integration.md +234 -0
  90. package/package.json +7 -5
  91. package/dist/authoring-references-Cy8J-DWt.d.mts +0 -48
  92. package/dist/authoring-references-Cy8J-DWt.d.mts.map +0 -1
  93. package/dist/authoring-references-X6uRaWuf.mjs.map +0 -1
  94. package/dist/capabilities-DvSKtjAw.d.mts.map +0 -1
  95. package/dist/constants-Jw0k8ojS.d.mts.map +0 -1
  96. package/dist/controller-proxy-BRDusFiO.d.mts.map +0 -1
  97. package/dist/controller-proxy-Duxo2IFE.mjs.map +0 -1
  98. package/dist/engine-sPvuixWg.mjs.map +0 -1
  99. package/dist/index-BWNHkpkh.d.mts.map +0 -1
  100. package/dist/index-CUqbBjkc.d.mts.map +0 -1
  101. package/dist/index-MpEMQzTm.d.mts.map +0 -1
  102. package/dist/index-TFtvBiSQ.d.mts.map +0 -1
  103. package/dist/memory-thread-repository-CJkkVE2-.mjs.map +0 -1
  104. package/dist/protocol-DV0ka9WT.mjs.map +0 -1
  105. package/dist/react-VuYe-cPh.mjs.map +0 -1
  106. package/dist/request-context-jcmXT_Q2.mjs +0 -44
  107. package/dist/request-context-jcmXT_Q2.mjs.map +0 -1
  108. package/dist/request-guardrails-C59IWnH8.mjs +0 -73
  109. package/dist/request-guardrails-C59IWnH8.mjs.map +0 -1
  110. package/dist/server-tasks.d.mts.map +0 -1
  111. package/dist/thread-documents-DamIq9Od.d.mts.map +0 -1
  112. package/dist/thread-handlers-DpynUc64.d.mts.map +0 -1
  113. package/dist/tools-Hz2p7vyv.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { N as rawSurveyDocumentSchema, c as surveyAssistantTurnFinalSchema, u as surveyChangeSetSchema } from "./protocol-DV0ka9WT.mjs";
2
- import { n as assertSurveyAssistantCapabilities } from "./capabilities-B6tvE7fK.mjs";
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.filter(isRecord$5).map((option) => {
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).filter(isRecord$5).map((option) => {
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.filter(isRecord$5).map((group) => {
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).filter(isRecord$5).map((field) => buildFormFieldDetails(field, translationNamespace, responseSlotPrefix)).filter((field) => Boolean(field));
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" && Number.isFinite(config.maxSelection)) return Math.max(1, Math.floor(config.maxSelection));
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
- const parsed = Number.parseInt(normalized, 10);
1442
- if (Number.isFinite(parsed) && parsed >= 1) return parsed;
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(stripTrailingEmbeddedFormCue(appendParenthetical(directLabel, getOptionNote(option)), option), getFootnoteMarker(option));
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(stripTrailingEmbeddedFormCue(appendParenthetical(translatedLabel, translatedNote), option), translatedMarker);
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 = rawType === "textarea" ? {
1728
- type: "textarea",
1729
- rows: 3
1730
- } : rawType === "number" ? {
1731
- type: "number",
1732
- step: 1
1733
- } : rawType === "checkbox" ? {
1734
- type: "switch",
1735
- defaultChecked: false
1736
- } : rawType === "select" ? {
1737
- type: "select",
1738
- options: []
1739
- } : {
1740
- type: "input",
1741
- inputMode: rawType === "email" ? "email" : "text"
1742
- };
1743
- const translations = [{
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: getString(field.label) ?? humanizeKey(id)
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.toUpperCase(),
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: items.length,
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-sPvuixWg.mjs.map
5199
+ //# sourceMappingURL=engine-DQrCO_ur.mjs.map