@defra/forms-engine-plugin 5.0.0-alpha.10 → 5.0.0-alpha.12

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 (99) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.LICENSE.txt +7 -0
  3. package/.public/javascripts/application.min.js.map +1 -1
  4. package/.public/javascripts/shared.min.js +1 -1
  5. package/.public/javascripts/shared.min.js.LICENSE.txt +7 -0
  6. package/.public/javascripts/shared.min.js.map +1 -1
  7. package/.server/client/javascripts/file-upload.js +1 -0
  8. package/.server/client/javascripts/file-upload.js.map +1 -1
  9. package/.server/client/javascripts/geospatial-map.js +2 -28
  10. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  11. package/.server/client/javascripts/govuk.js +2 -1
  12. package/.server/client/javascripts/govuk.js.map +1 -1
  13. package/.server/client/javascripts/location-map.js +3 -2
  14. package/.server/client/javascripts/location-map.js.map +1 -1
  15. package/.server/client/javascripts/map.d.ts +6 -0
  16. package/.server/client/javascripts/map.js +37 -0
  17. package/.server/client/javascripts/map.js.map +1 -1
  18. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +20 -2
  19. package/.server/server/plugins/engine/components/CheckboxesField.js +24 -12
  20. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  21. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +5 -0
  22. package/.server/server/plugins/engine/components/EastingNorthingField.js +17 -0
  23. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  24. package/.server/server/plugins/engine/components/FileUploadField.d.ts +10 -1
  25. package/.server/server/plugins/engine/components/FileUploadField.js +21 -7
  26. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  27. package/.server/server/plugins/engine/components/GeospatialField.d.ts +21 -0
  28. package/.server/server/plugins/engine/components/GeospatialField.js +32 -9
  29. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  30. package/.server/server/plugins/engine/components/LatLongField.d.ts +4 -0
  31. package/.server/server/plugins/engine/components/LocationFieldBase.js +4 -1
  32. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  33. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +5 -1
  34. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +10 -2
  35. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  36. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +8 -1
  37. package/.server/server/plugins/engine/components/helpers/geospatial.js +56 -15
  38. package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
  39. package/.server/server/plugins/engine/form-availability.d.ts +3 -2
  40. package/.server/server/plugins/engine/form-availability.js +3 -2
  41. package/.server/server/plugins/engine/form-availability.js.map +1 -1
  42. package/.server/server/plugins/engine/form-context.js +2 -2
  43. package/.server/server/plugins/engine/form-context.js.map +1 -1
  44. package/.server/server/plugins/engine/i18n/buildValidationMessages.d.ts +1 -1
  45. package/.server/server/plugins/engine/i18n/buildValidationMessages.js +30 -9
  46. package/.server/server/plugins/engine/i18n/buildValidationMessages.js.map +1 -1
  47. package/.server/server/plugins/engine/i18n/translations/cy.json +79 -13
  48. package/.server/server/plugins/engine/i18n/translations/en-GB.json +42 -13
  49. package/.server/server/plugins/engine/i18n/types.d.ts +1 -0
  50. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  51. package/.server/server/plugins/engine/models/unavailable-view-model.d.ts +11 -2
  52. package/.server/server/plugins/engine/models/unavailable-view-model.js +23 -4
  53. package/.server/server/plugins/engine/models/unavailable-view-model.js.map +1 -1
  54. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +6 -0
  55. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +34 -1
  56. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  57. package/.server/server/plugins/engine/pageControllers/validationOptions.d.ts +1 -1
  58. package/.server/server/plugins/engine/pageControllers/validationOptions.js +1 -1
  59. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  60. package/.server/server/plugins/engine/types.d.ts +4 -0
  61. package/.server/server/plugins/engine/types.js.map +1 -1
  62. package/.server/server/plugins/engine/unavailable-response.js +5 -2
  63. package/.server/server/plugins/engine/unavailable-response.js.map +1 -1
  64. package/.server/server/plugins/engine/views/components/_location-field-base.html +3 -2
  65. package/.server/server/plugins/engine/views/file-upload.html +1 -1
  66. package/.server/server/plugins/engine/views/unavailable.html +14 -11
  67. package/.server/server/utils/utils.d.ts +6 -0
  68. package/.server/server/utils/utils.js +18 -1
  69. package/.server/server/utils/utils.js.map +1 -1
  70. package/.server/server/utils/utils.test.js +21 -1
  71. package/.server/server/utils/utils.test.js.map +1 -1
  72. package/package.json +2 -1
  73. package/src/client/javascripts/file-upload.js +1 -0
  74. package/src/client/javascripts/geospatial-map.js +2 -31
  75. package/src/client/javascripts/govuk.js +2 -0
  76. package/src/client/javascripts/location-map.js +3 -1
  77. package/src/client/javascripts/map.js +42 -0
  78. package/src/server/plugins/engine/components/CheckboxesField.ts +34 -13
  79. package/src/server/plugins/engine/components/EastingNorthingField.ts +31 -0
  80. package/src/server/plugins/engine/components/FileUploadField.ts +29 -7
  81. package/src/server/plugins/engine/components/GeospatialField.ts +43 -11
  82. package/src/server/plugins/engine/components/LocationFieldBase.ts +3 -1
  83. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +13 -3
  84. package/src/server/plugins/engine/components/helpers/geospatial.ts +84 -21
  85. package/src/server/plugins/engine/form-availability.ts +8 -2
  86. package/src/server/plugins/engine/form-context.ts +4 -2
  87. package/src/server/plugins/engine/i18n/buildValidationMessages.ts +15 -10
  88. package/src/server/plugins/engine/i18n/translations/cy.json +79 -13
  89. package/src/server/plugins/engine/i18n/translations/en-GB.json +42 -13
  90. package/src/server/plugins/engine/i18n/types.ts +1 -0
  91. package/src/server/plugins/engine/models/unavailable-view-model.ts +26 -5
  92. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +42 -1
  93. package/src/server/plugins/engine/pageControllers/validationOptions.ts +2 -2
  94. package/src/server/plugins/engine/types.ts +4 -0
  95. package/src/server/plugins/engine/unavailable-response.ts +6 -2
  96. package/src/server/plugins/engine/views/components/_location-field-base.html +3 -2
  97. package/src/server/plugins/engine/views/file-upload.html +1 -1
  98. package/src/server/plugins/engine/views/unavailable.html +14 -11
  99. package/src/server/utils/utils.js +19 -1
@@ -1,5 +1,5 @@
1
1
  import { type CustomValidator } from 'joi';
2
- import { type EastingNorthingField } from '../../../../server/plugins/engine/components/EastingNorthingField.js';
2
+ import { EastingNorthingField } from '../../../../server/plugins/engine/components/EastingNorthingField.js';
3
3
  import { type GeospatialField } from '../../../../server/plugins/engine/components/GeospatialField.js';
4
4
  import { type LatLongField } from '../../../../server/plugins/engine/components/LatLongField.js';
5
5
  import { type OsGridRefField } from '../../../../server/plugins/engine/components/OsGridRefField.js';
@@ -24,6 +24,7 @@ export declare function getLocationFieldViewModel(component: LocationField, view
24
24
  };
25
25
  items: DateInputItem[];
26
26
  mapLayers: string;
27
+ country: import("@defra/forms-model").GeospatialFieldOptionsCountry | undefined;
27
28
  label: Label;
28
29
  type?: string;
29
30
  id: string;
@@ -66,13 +67,16 @@ export declare function getLocationFieldViewModel(component: LocationField, view
66
67
  uploadingLabel?: string;
67
68
  };
68
69
  } | {
70
+ instructionSummary: string;
69
71
  instructionText: string;
72
+ t: (key: string, opts?: Record<string, unknown>) => string;
70
73
  fieldset: {
71
74
  attributes?: string | Record<string, string>;
72
75
  legend?: Label;
73
76
  };
74
77
  items: DateInputItem[];
75
78
  mapLayers: string;
79
+ country: import("@defra/forms-model").GeospatialFieldOptionsCountry | undefined;
76
80
  label: Label;
77
81
  type?: string;
78
82
  id: string;
@@ -1,3 +1,4 @@
1
+ import { EastingNorthingField } from "./EastingNorthingField.js";
1
2
  import { isFormValue } from "./FormComponent.js";
2
3
  /**
3
4
  * Lowercases the start of a message for concatenation.
@@ -122,12 +123,19 @@ export function getLocationFieldViewModel(component, viewModel, context) {
122
123
  ...viewModel,
123
124
  fieldset,
124
125
  items,
125
- mapLayers: getMapLayers(component)
126
+ mapLayers: getMapLayers(component),
127
+ country: component instanceof EastingNorthingField ? component.options.countries?.at(0) : undefined
126
128
  };
127
129
  if (component.options.instructionText) {
130
+ const {
131
+ t,
132
+ tComponent
133
+ } = context.translator;
128
134
  return {
129
135
  ...result,
130
- instructionText: component.options.instructionText
136
+ instructionSummary: t('components.locationFieldBase.howToFind'),
137
+ instructionText: tComponent(component.def, 'instructionText') || component.options.instructionText,
138
+ t
131
139
  };
132
140
  }
133
141
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"LocationFieldHelpers.js","names":["isFormValue","lowercaseMessageStart","message","charAt","toLowerCase","slice","joinWithAnd","items","translator","t","length","leading","join","last","formatErrorList","messages","formattedMessages","map","msg","index","mergeCssClasses","classNames","tokens","flatMap","name","split","token","trim","filter","Boolean","undefined","Array","from","Set","deduplicateErrorsByHref","errors","error","self","findIndex","err","href","getMapLayers","component","mapLayers","options","layers","sssi","push","getLocationFieldViewModel","viewModel","context","collection","fieldset","existingFieldset","label","subViewModels","getViewModel","fieldErrors","forEach","model","errorMessage","text","hasFieldErrors","type","value","classes","prefix","suffix","toString","baseItem","id","showFieldsetError","legend","result","instructionText","createLocationFieldValidator","payload","helpers","values","getFormValueFromState","getStateFromValidForm","missing","keys","key","isState","required"],"sources":["../../../../../src/server/plugins/engine/components/LocationFieldHelpers.ts"],"sourcesContent":["import { type Context, type CustomValidator } from 'joi'\n\nimport { type EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type GeospatialField } from '~/src/server/plugins/engine/components/GeospatialField.js'\nimport { type LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'\nimport { type OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'\nimport {\n type DateInputItem,\n type Label,\n type RenderContext,\n type ViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n type FormPayload,\n type FormSubmissionError,\n type FormValue\n} from '~/src/server/plugins/engine/types.js'\n\nexport type LocationField =\n | InstanceType<typeof EastingNorthingField>\n | InstanceType<typeof LatLongField>\n\n/**\n * Lowercases the start of a message for concatenation.\n * Handles patterns like:\n * - \"Enter latitude\" -> \"enter latitude\"\n * - \"Enter a valid latitude...\" -> \"enter a valid latitude...\"\n * - \"latitude must be...\" -> \"latitude must be...\"\n */\nfunction lowercaseMessageStart(message: string): string {\n // Lowercase first character for a message\n return message.charAt(0).toLowerCase() + message.slice(1)\n}\n\nexport function joinWithAnd(items: string[], translator: Translator): string {\n const { t } = translator\n if (items.length === 2) {\n return `${items[0]} ${t('common.and')} ${items[1]}`\n }\n\n const leading = items.slice(0, -1).join(', ')\n const last = items[items.length - 1]\n return `${leading} ${t('common.and')} ${last}`\n}\n\nexport function formatErrorList(\n messages: string[],\n translator: Translator\n): string {\n if (!messages.length) {\n return ''\n }\n\n if (messages.length === 1) {\n return messages[0]\n }\n\n const formattedMessages = messages.map((msg, index) =>\n index === 0 ? msg : lowercaseMessageStart(msg)\n )\n\n return joinWithAnd(formattedMessages, translator)\n}\n\nexport function mergeCssClasses(...classNames: (string | undefined)[]) {\n const tokens = classNames\n .flatMap((name) => name?.split(/\\s+/) ?? [])\n .map((token) => token.trim())\n .filter(Boolean)\n\n if (!tokens.length) {\n return undefined\n }\n\n return Array.from(new Set(tokens)).join(' ')\n}\n\nexport function deduplicateErrorsByHref(\n errors?: FormSubmissionError[]\n): FormSubmissionError[] | undefined {\n if (!errors?.length) {\n return undefined\n }\n\n return errors.filter(\n (error, index, self) =>\n index === self.findIndex((err) => err.href === error.href)\n )\n}\n\nexport function getMapLayers(\n component:\n | GeospatialField\n | OsGridRefField\n | LatLongField\n | EastingNorthingField\n) {\n const { mapLayers } = component.options\n const layers = []\n\n if (mapLayers?.sssi) {\n layers.push('sssi')\n }\n\n return layers.join(',')\n}\n\nexport function getLocationFieldViewModel(\n component: LocationField,\n viewModel: ViewModel & {\n label: Label\n id: string\n name: string\n value: FormValue\n },\n context: RenderContext\n) {\n const { collection } = component\n const { fieldset: existingFieldset, label } = viewModel\n\n const subViewModels = collection.getViewModel(context)\n\n const fieldErrors: string[] = []\n subViewModels.forEach(({ model }) => {\n if (model.errorMessage?.text) {\n fieldErrors.push(model.errorMessage.text)\n }\n })\n\n const hasFieldErrors = fieldErrors.length > 0\n\n const items: DateInputItem[] = subViewModels.map(\n ({ model }): DateInputItem => {\n let { label, type, value, classes, prefix, suffix, errorMessage } = model\n\n if (label) {\n label.toString = () => label.text\n }\n\n if (!isFormValue(value)) {\n value = undefined\n }\n\n const baseItem: DateInputItem = {\n label,\n id: model.id,\n name: model.name,\n type,\n value,\n classes: mergeCssClasses(\n classes,\n hasFieldErrors ? 'govuk-input--error' : undefined\n ),\n prefix,\n suffix\n }\n\n if (!hasFieldErrors && errorMessage) {\n baseItem.errorMessage = errorMessage\n }\n\n return baseItem\n }\n )\n\n const showFieldsetError =\n hasFieldErrors || Boolean(viewModel.errorMessage?.text)\n\n viewModel.showFieldsetError = showFieldsetError\n\n if (hasFieldErrors) {\n viewModel.errorMessage = {\n text:\n fieldErrors.length === 1\n ? fieldErrors[0]\n : formatErrorList(fieldErrors, context.translator)\n }\n }\n\n const fieldset = existingFieldset ?? {\n legend: {\n text: label.text,\n classes: 'govuk-fieldset__legend--m'\n }\n }\n\n const result = {\n ...viewModel,\n fieldset,\n items,\n mapLayers: getMapLayers(component)\n }\n\n if (component.options.instructionText) {\n return {\n ...result,\n instructionText: component.options.instructionText\n }\n }\n\n return result\n}\n\n/**\n * Validator factory for location-based fields.\n * This creates a validator that ensures all required fields are present.\n */\nexport function createLocationFieldValidator(\n component: LocationField\n): CustomValidator {\n return (payload: FormPayload, helpers) => {\n const { collection, name, options } = component\n\n const values = component.getFormValueFromState(\n component.getStateFromValidForm(payload)\n )\n\n const context: Context = {\n missing: collection.keys,\n key: name\n }\n\n if (!component.isState(values)) {\n return options.required !== false\n ? helpers.error('object.required', context)\n : payload\n }\n\n return payload\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"LocationFieldHelpers.js","names":["EastingNorthingField","isFormValue","lowercaseMessageStart","message","charAt","toLowerCase","slice","joinWithAnd","items","translator","t","length","leading","join","last","formatErrorList","messages","formattedMessages","map","msg","index","mergeCssClasses","classNames","tokens","flatMap","name","split","token","trim","filter","Boolean","undefined","Array","from","Set","deduplicateErrorsByHref","errors","error","self","findIndex","err","href","getMapLayers","component","mapLayers","options","layers","sssi","push","getLocationFieldViewModel","viewModel","context","collection","fieldset","existingFieldset","label","subViewModels","getViewModel","fieldErrors","forEach","model","errorMessage","text","hasFieldErrors","type","value","classes","prefix","suffix","toString","baseItem","id","showFieldsetError","legend","result","country","countries","at","instructionText","tComponent","instructionSummary","def","createLocationFieldValidator","payload","helpers","values","getFormValueFromState","getStateFromValidForm","missing","keys","key","isState","required"],"sources":["../../../../../src/server/plugins/engine/components/LocationFieldHelpers.ts"],"sourcesContent":["import { type Context, type CustomValidator } from 'joi'\n\nimport { EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type GeospatialField } from '~/src/server/plugins/engine/components/GeospatialField.js'\nimport { type LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'\nimport { type OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'\nimport {\n type DateInputItem,\n type Label,\n type RenderContext,\n type ViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n type FormPayload,\n type FormSubmissionError,\n type FormValue\n} from '~/src/server/plugins/engine/types.js'\n\nexport type LocationField =\n | InstanceType<typeof EastingNorthingField>\n | InstanceType<typeof LatLongField>\n\n/**\n * Lowercases the start of a message for concatenation.\n * Handles patterns like:\n * - \"Enter latitude\" -> \"enter latitude\"\n * - \"Enter a valid latitude...\" -> \"enter a valid latitude...\"\n * - \"latitude must be...\" -> \"latitude must be...\"\n */\nfunction lowercaseMessageStart(message: string): string {\n // Lowercase first character for a message\n return message.charAt(0).toLowerCase() + message.slice(1)\n}\n\nexport function joinWithAnd(items: string[], translator: Translator): string {\n const { t } = translator\n if (items.length === 2) {\n return `${items[0]} ${t('common.and')} ${items[1]}`\n }\n\n const leading = items.slice(0, -1).join(', ')\n const last = items[items.length - 1]\n return `${leading} ${t('common.and')} ${last}`\n}\n\nexport function formatErrorList(\n messages: string[],\n translator: Translator\n): string {\n if (!messages.length) {\n return ''\n }\n\n if (messages.length === 1) {\n return messages[0]\n }\n\n const formattedMessages = messages.map((msg, index) =>\n index === 0 ? msg : lowercaseMessageStart(msg)\n )\n\n return joinWithAnd(formattedMessages, translator)\n}\n\nexport function mergeCssClasses(...classNames: (string | undefined)[]) {\n const tokens = classNames\n .flatMap((name) => name?.split(/\\s+/) ?? [])\n .map((token) => token.trim())\n .filter(Boolean)\n\n if (!tokens.length) {\n return undefined\n }\n\n return Array.from(new Set(tokens)).join(' ')\n}\n\nexport function deduplicateErrorsByHref(\n errors?: FormSubmissionError[]\n): FormSubmissionError[] | undefined {\n if (!errors?.length) {\n return undefined\n }\n\n return errors.filter(\n (error, index, self) =>\n index === self.findIndex((err) => err.href === error.href)\n )\n}\n\nexport function getMapLayers(\n component:\n | GeospatialField\n | OsGridRefField\n | LatLongField\n | EastingNorthingField\n) {\n const { mapLayers } = component.options\n const layers = []\n\n if (mapLayers?.sssi) {\n layers.push('sssi')\n }\n\n return layers.join(',')\n}\n\nexport function getLocationFieldViewModel(\n component: LocationField,\n viewModel: ViewModel & {\n label: Label\n id: string\n name: string\n value: FormValue\n },\n context: RenderContext\n) {\n const { collection } = component\n const { fieldset: existingFieldset, label } = viewModel\n\n const subViewModels = collection.getViewModel(context)\n\n const fieldErrors: string[] = []\n subViewModels.forEach(({ model }) => {\n if (model.errorMessage?.text) {\n fieldErrors.push(model.errorMessage.text)\n }\n })\n\n const hasFieldErrors = fieldErrors.length > 0\n\n const items: DateInputItem[] = subViewModels.map(\n ({ model }): DateInputItem => {\n let { label, type, value, classes, prefix, suffix, errorMessage } = model\n\n if (label) {\n label.toString = () => label.text\n }\n\n if (!isFormValue(value)) {\n value = undefined\n }\n\n const baseItem: DateInputItem = {\n label,\n id: model.id,\n name: model.name,\n type,\n value,\n classes: mergeCssClasses(\n classes,\n hasFieldErrors ? 'govuk-input--error' : undefined\n ),\n prefix,\n suffix\n }\n\n if (!hasFieldErrors && errorMessage) {\n baseItem.errorMessage = errorMessage\n }\n\n return baseItem\n }\n )\n\n const showFieldsetError =\n hasFieldErrors || Boolean(viewModel.errorMessage?.text)\n\n viewModel.showFieldsetError = showFieldsetError\n\n if (hasFieldErrors) {\n viewModel.errorMessage = {\n text:\n fieldErrors.length === 1\n ? fieldErrors[0]\n : formatErrorList(fieldErrors, context.translator)\n }\n }\n\n const fieldset = existingFieldset ?? {\n legend: {\n text: label.text,\n classes: 'govuk-fieldset__legend--m'\n }\n }\n\n const result = {\n ...viewModel,\n fieldset,\n items,\n mapLayers: getMapLayers(component),\n country:\n component instanceof EastingNorthingField\n ? component.options.countries?.at(0)\n : undefined\n }\n\n if (component.options.instructionText) {\n const { t, tComponent } = context.translator\n\n return {\n ...result,\n instructionSummary: t('components.locationFieldBase.howToFind'),\n instructionText:\n tComponent(component.def, 'instructionText') ||\n component.options.instructionText,\n t\n }\n }\n\n return result\n}\n\n/**\n * Validator factory for location-based fields.\n * This creates a validator that ensures all required fields are present.\n */\nexport function createLocationFieldValidator(\n component: LocationField\n): CustomValidator {\n return (payload: FormPayload, helpers) => {\n const { collection, name, options } = component\n\n const values = component.getFormValueFromState(\n component.getStateFromValidForm(payload)\n )\n\n const context: Context = {\n missing: collection.keys,\n key: name\n }\n\n if (!component.isState(values)) {\n return options.required !== false\n ? helpers.error('object.required', context)\n : payload\n }\n\n return payload\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,11 @@
1
- import { type GeospatialFieldComponent } from '@defra/forms-model';
1
+ import { type GeospatialFieldComponent, type GeospatialFieldOptionsCountry } from '@defra/forms-model';
2
2
  import JoiBase from 'joi';
3
+ import { type EastingNorthingField } from '../../../../../server/plugins/engine/components/EastingNorthingField.js';
3
4
  import { type Feature } from '../../../../../server/plugins/engine/types.js';
5
+ export declare function determineLimits(def: GeospatialFieldComponent, isOptional: boolean): {
6
+ min?: number;
7
+ max?: number;
8
+ length?: number;
9
+ };
4
10
  export declare function getGeospatialSchema(def: GeospatialFieldComponent): JoiBase.ArraySchema<Feature[]>;
11
+ export declare function getEastingNorthingCountryValidator(field: EastingNorthingField, country: GeospatialFieldOptionsCountry): JoiBase.CustomValidator;
@@ -1,14 +1,10 @@
1
- import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
2
1
  import Bourne from '@hapi/bourne';
3
2
  import { booleanWithin } from '@turf/boolean-within';
3
+ import { point } from '@turf/helpers';
4
+ // @ts-expect-error - no types
5
+ import OsGridRef from 'geodesy/osgridref.js';
4
6
  import JoiBase from 'joi';
5
7
  import { countries } from "../../../map/routes/index.js";
6
- const countriesDesc = {
7
- [GeospatialFieldOptionsCountryEnum.England]: 'England',
8
- [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',
9
- [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',
10
- [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'
11
- };
12
8
  const Joi = JoiBase.extend({
13
9
  type: 'array',
14
10
  base: JoiBase.array(),
@@ -73,21 +69,37 @@ const featureSchema = Joi.object().keys({
73
69
  properties: featurePropertiesSchema,
74
70
  geometry: featureGeometrySchema
75
71
  });
76
- function applySchemaConstraints(schema, def) {
72
+ export function determineLimits(def, isOptional) {
77
73
  const {
78
- options,
79
74
  schema: constraints
80
75
  } = def;
81
- const isOptional = options.required === false;
76
+ const limits = {};
82
77
  if (typeof constraints?.length === 'number') {
83
- schema = schema.length(constraints.length);
78
+ limits.length = constraints.length;
84
79
  } else {
85
80
  if (typeof constraints?.min === 'number') {
86
- schema = schema.min(constraints.min);
81
+ limits.min = constraints.min;
87
82
  } else if (!isOptional) {
88
- schema = schema.min(1);
83
+ limits.min = 1;
89
84
  }
90
- schema = schema.max(typeof constraints?.max === 'number' ? constraints.max : 50);
85
+ limits.max = typeof constraints?.max === 'number' ? constraints.max : 50;
86
+ }
87
+ return limits;
88
+ }
89
+ function applySchemaConstraints(schema, def) {
90
+ const {
91
+ options
92
+ } = def;
93
+ const isOptional = options.required === false;
94
+ const limits = determineLimits(def, isOptional);
95
+ if (limits.length !== undefined) {
96
+ schema = schema.length(limits.length);
97
+ }
98
+ if (limits.min !== undefined) {
99
+ schema = schema.min(limits.min);
100
+ }
101
+ if (limits.max !== undefined) {
102
+ schema = schema.max(limits.max);
91
103
  }
92
104
  if (isOptional) {
93
105
  schema = schema.optional();
@@ -113,7 +125,7 @@ export function getGeospatialSchema(def) {
113
125
  const result = booleanWithin(value, countryFeature);
114
126
  if (!result) {
115
127
  return helpers.error('any.custom', {
116
- country: countriesDesc[country]
128
+ country
117
129
  });
118
130
  }
119
131
 
@@ -122,4 +134,33 @@ export function getGeospatialSchema(def) {
122
134
  };
123
135
  return applySchemaConstraints(Joi.array().items(featureSchema.custom(validateCountryBounds)).unique('id'), def);
124
136
  }
137
+ export function getEastingNorthingCountryValidator(field, country) {
138
+ const validateCountryBounds = (value, helpers) => {
139
+ const countryFeature = countries.features.find(feature => feature.id === country);
140
+ if (!countryFeature) {
141
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
142
+ return value;
143
+ }
144
+ const eastingNorthing = field.getFormValueFromState(value);
145
+ if (!eastingNorthing) {
146
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
147
+ return value;
148
+ }
149
+ const gridRef = new OsGridRef(eastingNorthing.easting, eastingNorthing.northing);
150
+ const latLong = gridRef.toLatLon();
151
+ const coord = point([latLong.longitude, latLong.latitude]);
152
+ const within = booleanWithin(coord, countryFeature);
153
+ if (!within) {
154
+ return helpers.error('any.custom', {
155
+ country
156
+ }, {
157
+ path: [field.name]
158
+ });
159
+ }
160
+
161
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
162
+ return value;
163
+ };
164
+ return validateCountryBounds;
165
+ }
125
166
  //# sourceMappingURL=geospatial.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","applySchemaConstraints","schema","def","options","constraints","isOptional","length","max","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options, schema: constraints } = def\n const isOptional = options.required === false\n\n if (typeof constraints?.length === 'number') {\n schema = schema.length(constraints.length)\n } else {\n if (typeof constraints?.min === 'number') {\n schema = schema.min(constraints.min)\n } else if (!isOptional) {\n schema = schema.min(1)\n }\n\n schema = schema.max(\n typeof constraints?.max === 'number' ? constraints.max : 50\n )\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"geospatial.js","names":["Bourne","booleanWithin","point","OsGridRef","JoiBase","countries","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","determineLimits","def","isOptional","schema","constraints","limits","length","max","applySchemaConstraints","options","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom","getEastingNorthingCountryValidator","field","eastingNorthing","getFormValueFromState","gridRef","easting","northing","latLong","toLatLon","coord","longitude","latitude","within","path","name"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport { point } from '@turf/helpers'\n// @ts-expect-error - no types\nimport OsGridRef from 'geodesy/osgridref.js'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport { type EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nimport { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nexport function determineLimits(\n def: GeospatialFieldComponent,\n isOptional: boolean\n) {\n const { schema: constraints } = def\n const limits: { min?: number; max?: number; length?: number } = {}\n if (typeof constraints?.length === 'number') {\n limits.length = constraints.length\n } else {\n if (typeof constraints?.min === 'number') {\n limits.min = constraints.min\n } else if (!isOptional) {\n limits.min = 1\n }\n\n limits.max = typeof constraints?.max === 'number' ? constraints.max : 50\n }\n return limits\n}\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options } = def\n const isOptional = options.required === false\n\n const limits = determineLimits(def, isOptional)\n\n if (limits.length !== undefined) {\n schema = schema.length(limits.length)\n }\n if (limits.min !== undefined) {\n schema = schema.min(limits.min)\n }\n if (limits.max !== undefined) {\n schema = schema.max(limits.max)\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n\nexport function getEastingNorthingCountryValidator(\n field: EastingNorthingField,\n country: GeospatialFieldOptionsCountry\n) {\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const eastingNorthing = field.getFormValueFromState(\n value as EastingNorthingState\n )\n\n if (!eastingNorthing) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const gridRef = new OsGridRef(\n eastingNorthing.easting,\n eastingNorthing.northing\n )\n const latLong = gridRef.toLatLon()\n const coord = point([latLong.longitude, latLong.latitude])\n const within = booleanWithin(coord, countryFeature)\n\n if (!within) {\n return helpers.error(\n 'any.custom',\n {\n country\n },\n { path: [field.name] }\n )\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return validateCountryBounds\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,15 +1,16 @@
1
- import { FormStatus, type FormMetadata } from '@defra/forms-model';
1
+ import { FormStatus, type FormDefinition, type FormMetadata } from '@defra/forms-model';
2
2
  import Boom from '@hapi/boom';
3
3
  export interface OfflineBoomData {
4
4
  offline: true;
5
5
  metadata: FormMetadata;
6
+ definition: FormDefinition | undefined;
6
7
  }
7
8
  /**
8
9
  * Throws when the form has been taken offline. The plugin's
9
10
  * unavailable-response extension catches the marker and renders the
10
11
  * "Sorry, this form is unavailable" view at HTTP 200.
11
12
  */
12
- export declare function assertFormAvailable(metadata: FormMetadata, formState: FormStatus, isPreview: boolean): void;
13
+ export declare function assertFormAvailable(metadata: FormMetadata, definition: FormDefinition | undefined, formState: FormStatus, isPreview: boolean): void;
13
14
  /** Type guard for the offline Boom marker. */
14
15
  export declare function isOfflineBoom(err: unknown): err is Boom.Boom<OfflineBoomData> & {
15
16
  data: OfflineBoomData;
@@ -5,11 +5,12 @@ import Boom from '@hapi/boom';
5
5
  * unavailable-response extension catches the marker and renders the
6
6
  * "Sorry, this form is unavailable" view at HTTP 200.
7
7
  */
8
- export function assertFormAvailable(metadata, formState, isPreview) {
8
+ export function assertFormAvailable(metadata, definition, formState, isPreview) {
9
9
  if (metadata.offline === true && formState === FormStatus.Live && !isPreview) {
10
10
  const data = {
11
11
  offline: true,
12
- metadata
12
+ metadata,
13
+ definition
13
14
  };
14
15
  throw Boom.boomify(new Error(`Form ${metadata.slug} is offline`), {
15
16
  statusCode: 503,
@@ -1 +1 @@
1
- {"version":3,"file":"form-availability.js","names":["FormStatus","Boom","assertFormAvailable","metadata","formState","isPreview","offline","Live","data","boomify","Error","slug","statusCode","isOfflineBoom","err","isBoom"],"sources":["../../../../src/server/plugins/engine/form-availability.ts"],"sourcesContent":["import { FormStatus, type FormMetadata } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\n\nexport interface OfflineBoomData {\n offline: true\n metadata: FormMetadata\n}\n\n/**\n * Throws when the form has been taken offline. The plugin's\n * unavailable-response extension catches the marker and renders the\n * \"Sorry, this form is unavailable\" view at HTTP 200.\n */\nexport function assertFormAvailable(\n metadata: FormMetadata,\n formState: FormStatus,\n isPreview: boolean\n): void {\n if (\n metadata.offline === true &&\n formState === FormStatus.Live &&\n !isPreview\n ) {\n const data: OfflineBoomData = { offline: true, metadata }\n throw Boom.boomify(new Error(`Form ${metadata.slug} is offline`), {\n statusCode: 503,\n data\n })\n }\n}\n\n/** Type guard for the offline Boom marker. */\nexport function isOfflineBoom(\n err: unknown\n): err is Boom.Boom<OfflineBoomData> & { data: OfflineBoomData } {\n if (!Boom.isBoom(err)) return false\n const data = err.data as Partial<OfflineBoomData> | null | undefined\n return data?.offline === true && !!data.metadata\n}\n"],"mappings":"AAAA,SAASA,UAAU,QAA2B,oBAAoB;AAClE,OAAOC,IAAI,MAAM,YAAY;AAO7B;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CACjCC,QAAsB,EACtBC,SAAqB,EACrBC,SAAkB,EACZ;EACN,IACEF,QAAQ,CAACG,OAAO,KAAK,IAAI,IACzBF,SAAS,KAAKJ,UAAU,CAACO,IAAI,IAC7B,CAACF,SAAS,EACV;IACA,MAAMG,IAAqB,GAAG;MAAEF,OAAO,EAAE,IAAI;MAAEH;IAAS,CAAC;IACzD,MAAMF,IAAI,CAACQ,OAAO,CAAC,IAAIC,KAAK,CAAC,QAAQP,QAAQ,CAACQ,IAAI,aAAa,CAAC,EAAE;MAChEC,UAAU,EAAE,GAAG;MACfJ;IACF,CAAC,CAAC;EACJ;AACF;;AAEA;AACA,OAAO,SAASK,aAAaA,CAC3BC,GAAY,EACmD;EAC/D,IAAI,CAACb,IAAI,CAACc,MAAM,CAACD,GAAG,CAAC,EAAE,OAAO,KAAK;EACnC,MAAMN,IAAI,GAAGM,GAAG,CAACN,IAAmD;EACpE,OAAOA,IAAI,EAAEF,OAAO,KAAK,IAAI,IAAI,CAAC,CAACE,IAAI,CAACL,QAAQ;AAClD","ignoreList":[]}
1
+ {"version":3,"file":"form-availability.js","names":["FormStatus","Boom","assertFormAvailable","metadata","definition","formState","isPreview","offline","Live","data","boomify","Error","slug","statusCode","isOfflineBoom","err","isBoom"],"sources":["../../../../src/server/plugins/engine/form-availability.ts"],"sourcesContent":["import {\n FormStatus,\n type FormDefinition,\n type FormMetadata\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\n\nexport interface OfflineBoomData {\n offline: true\n metadata: FormMetadata\n definition: FormDefinition | undefined\n}\n\n/**\n * Throws when the form has been taken offline. The plugin's\n * unavailable-response extension catches the marker and renders the\n * \"Sorry, this form is unavailable\" view at HTTP 200.\n */\nexport function assertFormAvailable(\n metadata: FormMetadata,\n definition: FormDefinition | undefined,\n formState: FormStatus,\n isPreview: boolean\n): void {\n if (\n metadata.offline === true &&\n formState === FormStatus.Live &&\n !isPreview\n ) {\n const data: OfflineBoomData = { offline: true, metadata, definition }\n throw Boom.boomify(new Error(`Form ${metadata.slug} is offline`), {\n statusCode: 503,\n data\n })\n }\n}\n\n/** Type guard for the offline Boom marker. */\nexport function isOfflineBoom(\n err: unknown\n): err is Boom.Boom<OfflineBoomData> & { data: OfflineBoomData } {\n if (!Boom.isBoom(err)) return false\n const data = err.data as Partial<OfflineBoomData> | null | undefined\n return data?.offline === true && !!data.metadata\n}\n"],"mappings":"AAAA,SACEA,UAAU,QAGL,oBAAoB;AAC3B,OAAOC,IAAI,MAAM,YAAY;AAQ7B;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CACjCC,QAAsB,EACtBC,UAAsC,EACtCC,SAAqB,EACrBC,SAAkB,EACZ;EACN,IACEH,QAAQ,CAACI,OAAO,KAAK,IAAI,IACzBF,SAAS,KAAKL,UAAU,CAACQ,IAAI,IAC7B,CAACF,SAAS,EACV;IACA,MAAMG,IAAqB,GAAG;MAAEF,OAAO,EAAE,IAAI;MAAEJ,QAAQ;MAAEC;IAAW,CAAC;IACrE,MAAMH,IAAI,CAACS,OAAO,CAAC,IAAIC,KAAK,CAAC,QAAQR,QAAQ,CAACS,IAAI,aAAa,CAAC,EAAE;MAChEC,UAAU,EAAE,GAAG;MACfJ;IACF,CAAC,CAAC;EACJ;AACF;;AAEA;AACA,OAAO,SAASK,aAAaA,CAC3BC,GAAY,EACmD;EAC/D,IAAI,CAACd,IAAI,CAACe,MAAM,CAACD,GAAG,CAAC,EAAE,OAAO,KAAK;EACnC,MAAMN,IAAI,GAAGM,GAAG,CAACN,IAAmD;EACpE,OAAOA,IAAI,EAAEF,OAAO,KAAK,IAAI,IAAI,CAAC,CAACE,IAAI,CAACN,QAAQ;AAClD","ignoreList":[]}
@@ -16,8 +16,8 @@ export async function getFormModel(slug, state, options = {}) {
16
16
  const isPreview = isPreviewState(state, options);
17
17
  const formState = resolveState(state);
18
18
  const metadata = await formsService.getFormMetadata(slug);
19
- assertFormAvailable(metadata, formState, isPreview);
20
19
  const definition = await formsService.getFormDefinition(metadata.id, formState);
20
+ assertFormAvailable(metadata, definition, formState, isPreview);
21
21
  if (!definition) {
22
22
  throw Boom.notFound(`No definition found for form metadata ${metadata.id} (${slug}) ${state}`);
23
23
  }
@@ -66,7 +66,6 @@ export async function resolveFormModel(server, slug, state, options = {}) {
66
66
  const metadata = await formsService.getFormMetadata(slug);
67
67
  const formState = resolveState(state);
68
68
  const isPreview = options.isPreview ?? isPreviewState(state, options);
69
- assertFormAvailable(metadata, formState, isPreview);
70
69
  const stateMetadata = metadata[formState];
71
70
  if (!stateMetadata) {
72
71
  throw Boom.notFound(`No '${formState}' state for form metadata ${metadata.id}`);
@@ -95,6 +94,7 @@ export async function resolveFormModel(server, slug, state, options = {}) {
95
94
  };
96
95
  cache.set(cacheKey, entry);
97
96
  }
97
+ assertFormAvailable(metadata, entry.model.def, formState, isPreview);
98
98
  return entry.model;
99
99
  }
100
100
  function buildBasePath(routePrefix, slug, state, isPreview) {
@@ -1 +1 @@
1
- {"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","assertFormAvailable","checkEmailAddressForLiveFormSubmission","getCacheService","FormModel","TerminalPageController","defaultServices","FormStatus","resolveLanguage","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","definition","getFormDefinition","id","notFound","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","language","translator","createTranslator","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","notificationEmail","realm","modifiers","route","prefix","model","set","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../src/server/plugins/engine/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport { assertFormAvailable } from '~/src/server/plugins/engine/form-availability.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyFormRequest,\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n language?: string\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n assertFormAvailable(metadata, formState, isPreview)\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n const language = resolveLanguage(summaryRequest as unknown as AnyFormRequest)\n const translator = formModel.createTranslator(language)\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? [],\n translator\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n assertFormAvailable(metadata, formState, isPreview)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n server.app.models ??= new Map<string, { model: FormModel; updatedAt: Date }>()\n\n const cache = server.app.models\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n checkEmailAddressForLiveFormSubmission(\n metadata.notificationEmail,\n isPreview\n )\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","assertFormAvailable","checkEmailAddressForLiveFormSubmission","getCacheService","FormModel","TerminalPageController","defaultServices","FormStatus","resolveLanguage","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","definition","getFormDefinition","id","notFound","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","language","translator","createTranslator","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","notificationEmail","realm","modifiers","route","prefix","model","set","def","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../src/server/plugins/engine/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport { assertFormAvailable } from '~/src/server/plugins/engine/form-availability.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyFormRequest,\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n language?: string\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n assertFormAvailable(metadata, definition, formState, isPreview)\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n const language = resolveLanguage(summaryRequest as unknown as AnyFormRequest)\n const translator = formModel.createTranslator(language)\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? [],\n translator\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n server.app.models ??= new Map<string, { model: FormModel; updatedAt: Date }>()\n\n const cache = server.app.models\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n checkEmailAddressForLiveFormSubmission(\n metadata.notificationEmail,\n isPreview\n )\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n assertFormAvailable(metadata, entry.model.def, formState, isPreview)\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
@@ -7,7 +7,7 @@ import { type JoiExpression } from 'joi';
7
7
  * because Joi cannot resolve custom functions from a plain string.
8
8
  * Plain strings with only built-in tokens ({{#label}}, {{#limit}}) work without wrapping.
9
9
  */
10
- export declare function buildValidationMessages(t: (key: string) => string): {
10
+ export declare function buildValidationMessages(t: (key: string, options?: Record<string, unknown>) => string): {
11
11
  required: JoiExpression;
12
12
  selectRequired: JoiExpression;
13
13
  pattern: JoiExpression;
@@ -39,15 +39,36 @@ export function buildValidationMessages(t) {
39
39
  dateMin: t('components.dateField.validation.dateMin'),
40
40
  dateMax: t('components.dateField.validation.dateMax'),
41
41
  unicode: t('validation.unicode'),
42
- arrayMin: t('validation.arrayMin'),
43
- arrayMax: t('validation.arrayMax'),
44
- arrayLength: t('validation.arrayLength'),
45
- featuresMin: t('validation.featuresMin'),
46
- featuresMax: t('validation.featuresMax'),
47
- featuresLength: t('validation.featuresLength'),
48
- filesMin: t('validation.filesMin'),
49
- filesMax: t('validation.filesMax'),
50
- filesLength: t('validation.filesLength')
42
+ // These generic templates are shared across fields with differing limits, so a
43
+ // fixed singular count is used here — components needing accurate singular/plural
44
+ // text (e.g. FileUploadField) build their own per-instance messages instead.
45
+ arrayMin: t('validation.arrayMin', {
46
+ count: 1
47
+ }),
48
+ arrayMax: t('validation.arrayMax', {
49
+ count: 1
50
+ }),
51
+ arrayLength: t('validation.arrayLength', {
52
+ count: 1
53
+ }),
54
+ featuresMin: t('validation.featuresMin', {
55
+ count: 1
56
+ }),
57
+ featuresMax: t('validation.featuresMax', {
58
+ count: 1
59
+ }),
60
+ featuresLength: t('validation.featuresLength', {
61
+ count: 1
62
+ }),
63
+ filesMin: t('validation.filesMin', {
64
+ count: 1
65
+ }),
66
+ filesMax: t('validation.filesMax', {
67
+ count: 1
68
+ }),
69
+ filesLength: t('validation.filesLength', {
70
+ count: 1
71
+ })
51
72
  };
52
73
  }
53
74
  //# sourceMappingURL=buildValidationMessages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildValidationMessages.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","buildValidationMessages","t","required","expression","selectRequired","pattern","format","declarationRequired","objectRequired","objectMissing","selectYesNoRequired","max","min","minMax","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","dateFormat","dateMin","dateMax","unicode","arrayMin","arrayMax","arrayLength","featuresMin","featuresMax","featuresLength","filesMin","filesMax","filesLength"],"sources":["../../../../../src/server/plugins/engine/i18n/buildValidationMessages.ts"],"sourcesContent":["import joi, { type JoiExpression, type ReferenceOptions } from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * Builds a set of translated Joi message templates for a given language.\n * Called once per FormModel at construction time.\n *\n * Strings containing {{lowerFirst(#label)}} must be wrapped in joi.expression()\n * because Joi cannot resolve custom functions from a plain string.\n * Plain strings with only built-in tokens ({{#label}}, {{#limit}}) work without wrapping.\n */\nexport function buildValidationMessages(t: (key: string) => string) {\n return {\n // Expressions — contain lowerFirst custom function\n required: joi.expression(t('validation.required'), opts) as JoiExpression,\n selectRequired: joi.expression(\n t('validation.selectRequired'),\n opts\n ) as JoiExpression,\n pattern: joi.expression(t('validation.pattern'), opts) as JoiExpression,\n format: joi.expression(t('validation.format'), opts) as JoiExpression,\n declarationRequired: joi.expression(\n t('components.declarationField.validation.declarationRequired'),\n opts\n ) as JoiExpression,\n objectRequired: joi.expression(\n t('validation.objectRequired'),\n opts\n ) as JoiExpression,\n objectMissing: joi.expression(\n t('validation.objectMissing'),\n opts\n ) as JoiExpression,\n\n // Plain Joi template strings — no lowerFirst\n selectYesNoRequired: t(\n 'components.yesNoField.validation.selectYesNoRequired'\n ),\n max: t('validation.max'),\n min: t('validation.min'),\n minMax: t('validation.minMax'),\n number: t('validation.number'),\n numberPrecision: t('validation.numberPrecision'),\n numberInteger: t('validation.numberInteger'),\n numberMin: t('validation.numberMin'),\n numberMax: t('validation.numberMax'),\n maxWords: t('validation.maxWords'),\n dateFormat: t('components.dateField.validation.dateFormat'),\n dateMin: t('components.dateField.validation.dateMin'),\n dateMax: t('components.dateField.validation.dateMax'),\n\n unicode: t('validation.unicode'),\n arrayMin: t('validation.arrayMin'),\n arrayMax: t('validation.arrayMax'),\n arrayLength: t('validation.arrayLength'),\n featuresMin: t('validation.featuresMin'),\n featuresMax: t('validation.featuresMax'),\n featuresLength: t('validation.featuresLength'),\n filesMin: t('validation.filesMin'),\n filesMax: t('validation.filesMax'),\n filesLength: t('validation.filesLength')\n }\n}\n\nexport type ValidationMessages = ReturnType<typeof buildValidationMessages>\n"],"mappings":"AAAA,OAAOA,GAAG,MAAqD,KAAK;AAEpE,SAASC,6BAA6B;AAEtC,MAAMC,IAAI,GAAG;EACXC,SAAS,EAAE;IACTC,UAAU,EAAEH;EACd;AACF,CAAqB;;AAErB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,uBAAuBA,CAACC,CAA0B,EAAE;EAClE,OAAO;IACL;IACAC,QAAQ,EAAEP,GAAG,CAACQ,UAAU,CAACF,CAAC,CAAC,qBAAqB,CAAC,EAAEJ,IAAI,CAAkB;IACzEO,cAAc,EAAET,GAAG,CAACQ,UAAU,CAC5BF,CAAC,CAAC,2BAA2B,CAAC,EAC9BJ,IACF,CAAkB;IAClBQ,OAAO,EAAEV,GAAG,CAACQ,UAAU,CAACF,CAAC,CAAC,oBAAoB,CAAC,EAAEJ,IAAI,CAAkB;IACvES,MAAM,EAAEX,GAAG,CAACQ,UAAU,CAACF,CAAC,CAAC,mBAAmB,CAAC,EAAEJ,IAAI,CAAkB;IACrEU,mBAAmB,EAAEZ,GAAG,CAACQ,UAAU,CACjCF,CAAC,CAAC,4DAA4D,CAAC,EAC/DJ,IACF,CAAkB;IAClBW,cAAc,EAAEb,GAAG,CAACQ,UAAU,CAC5BF,CAAC,CAAC,2BAA2B,CAAC,EAC9BJ,IACF,CAAkB;IAClBY,aAAa,EAAEd,GAAG,CAACQ,UAAU,CAC3BF,CAAC,CAAC,0BAA0B,CAAC,EAC7BJ,IACF,CAAkB;IAElB;IACAa,mBAAmB,EAAET,CAAC,CACpB,sDACF,CAAC;IACDU,GAAG,EAAEV,CAAC,CAAC,gBAAgB,CAAC;IACxBW,GAAG,EAAEX,CAAC,CAAC,gBAAgB,CAAC;IACxBY,MAAM,EAAEZ,CAAC,CAAC,mBAAmB,CAAC;IAC9Ba,MAAM,EAAEb,CAAC,CAAC,mBAAmB,CAAC;IAC9Bc,eAAe,EAAEd,CAAC,CAAC,4BAA4B,CAAC;IAChDe,aAAa,EAAEf,CAAC,CAAC,0BAA0B,CAAC;IAC5CgB,SAAS,EAAEhB,CAAC,CAAC,sBAAsB,CAAC;IACpCiB,SAAS,EAAEjB,CAAC,CAAC,sBAAsB,CAAC;IACpCkB,QAAQ,EAAElB,CAAC,CAAC,qBAAqB,CAAC;IAClCmB,UAAU,EAAEnB,CAAC,CAAC,4CAA4C,CAAC;IAC3DoB,OAAO,EAAEpB,CAAC,CAAC,yCAAyC,CAAC;IACrDqB,OAAO,EAAErB,CAAC,CAAC,yCAAyC,CAAC;IAErDsB,OAAO,EAAEtB,CAAC,CAAC,oBAAoB,CAAC;IAChCuB,QAAQ,EAAEvB,CAAC,CAAC,qBAAqB,CAAC;IAClCwB,QAAQ,EAAExB,CAAC,CAAC,qBAAqB,CAAC;IAClCyB,WAAW,EAAEzB,CAAC,CAAC,wBAAwB,CAAC;IACxC0B,WAAW,EAAE1B,CAAC,CAAC,wBAAwB,CAAC;IACxC2B,WAAW,EAAE3B,CAAC,CAAC,wBAAwB,CAAC;IACxC4B,cAAc,EAAE5B,CAAC,CAAC,2BAA2B,CAAC;IAC9C6B,QAAQ,EAAE7B,CAAC,CAAC,qBAAqB,CAAC;IAClC8B,QAAQ,EAAE9B,CAAC,CAAC,qBAAqB,CAAC;IAClC+B,WAAW,EAAE/B,CAAC,CAAC,wBAAwB;EACzC,CAAC;AACH","ignoreList":[]}
1
+ {"version":3,"file":"buildValidationMessages.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","buildValidationMessages","t","required","expression","selectRequired","pattern","format","declarationRequired","objectRequired","objectMissing","selectYesNoRequired","max","min","minMax","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","dateFormat","dateMin","dateMax","unicode","arrayMin","count","arrayMax","arrayLength","featuresMin","featuresMax","featuresLength","filesMin","filesMax","filesLength"],"sources":["../../../../../src/server/plugins/engine/i18n/buildValidationMessages.ts"],"sourcesContent":["import joi, { type JoiExpression, type ReferenceOptions } from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * Builds a set of translated Joi message templates for a given language.\n * Called once per FormModel at construction time.\n *\n * Strings containing {{lowerFirst(#label)}} must be wrapped in joi.expression()\n * because Joi cannot resolve custom functions from a plain string.\n * Plain strings with only built-in tokens ({{#label}}, {{#limit}}) work without wrapping.\n */\nexport function buildValidationMessages(\n t: (key: string, options?: Record<string, unknown>) => string\n) {\n return {\n // Expressions — contain lowerFirst custom function\n required: joi.expression(t('validation.required'), opts) as JoiExpression,\n selectRequired: joi.expression(\n t('validation.selectRequired'),\n opts\n ) as JoiExpression,\n pattern: joi.expression(t('validation.pattern'), opts) as JoiExpression,\n format: joi.expression(t('validation.format'), opts) as JoiExpression,\n declarationRequired: joi.expression(\n t('components.declarationField.validation.declarationRequired'),\n opts\n ) as JoiExpression,\n objectRequired: joi.expression(\n t('validation.objectRequired'),\n opts\n ) as JoiExpression,\n objectMissing: joi.expression(\n t('validation.objectMissing'),\n opts\n ) as JoiExpression,\n\n // Plain Joi template strings — no lowerFirst\n selectYesNoRequired: t(\n 'components.yesNoField.validation.selectYesNoRequired'\n ),\n max: t('validation.max'),\n min: t('validation.min'),\n minMax: t('validation.minMax'),\n number: t('validation.number'),\n numberPrecision: t('validation.numberPrecision'),\n numberInteger: t('validation.numberInteger'),\n numberMin: t('validation.numberMin'),\n numberMax: t('validation.numberMax'),\n maxWords: t('validation.maxWords'),\n dateFormat: t('components.dateField.validation.dateFormat'),\n dateMin: t('components.dateField.validation.dateMin'),\n dateMax: t('components.dateField.validation.dateMax'),\n\n unicode: t('validation.unicode'),\n // These generic templates are shared across fields with differing limits, so a\n // fixed singular count is used here — components needing accurate singular/plural\n // text (e.g. FileUploadField) build their own per-instance messages instead.\n arrayMin: t('validation.arrayMin', { count: 1 }),\n arrayMax: t('validation.arrayMax', { count: 1 }),\n arrayLength: t('validation.arrayLength', { count: 1 }),\n featuresMin: t('validation.featuresMin', { count: 1 }),\n featuresMax: t('validation.featuresMax', { count: 1 }),\n featuresLength: t('validation.featuresLength', { count: 1 }),\n filesMin: t('validation.filesMin', { count: 1 }),\n filesMax: t('validation.filesMax', { count: 1 }),\n filesLength: t('validation.filesLength', { count: 1 })\n }\n}\n\nexport type ValidationMessages = ReturnType<typeof buildValidationMessages>\n"],"mappings":"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","ignoreList":[]}