@defra/forms-engine-plugin 5.0.0-alpha.21 → 5.0.0-alpha.23

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 (39) hide show
  1. package/.server/server/forms/components.json +8 -0
  2. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +4 -0
  3. package/.server/server/plugins/engine/components/TrackingField.d.ts +21 -0
  4. package/.server/server/plugins/engine/components/TrackingField.js +46 -0
  5. package/.server/server/plugins/engine/components/TrackingField.js.map +1 -0
  6. package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
  7. package/.server/server/plugins/engine/components/helpers/components.js +3 -0
  8. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  9. package/.server/server/plugins/engine/components/index.d.ts +1 -0
  10. package/.server/server/plugins/engine/components/index.js +1 -0
  11. package/.server/server/plugins/engine/components/index.js.map +1 -1
  12. package/.server/server/plugins/engine/helpers.d.ts +1 -0
  13. package/.server/server/plugins/engine/options.js +2 -1
  14. package/.server/server/plugins/engine/options.js.map +1 -1
  15. package/.server/server/plugins/engine/plugin.js +3 -0
  16. package/.server/server/plugins/engine/plugin.js.map +1 -1
  17. package/.server/server/plugins/engine/referenceNumbers.d.ts +1 -0
  18. package/.server/server/plugins/engine/referenceNumbers.js +1 -0
  19. package/.server/server/plugins/engine/referenceNumbers.js.map +1 -1
  20. package/.server/server/plugins/engine/routes/index.js +9 -4
  21. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  22. package/.server/server/plugins/engine/types.d.ts +2 -0
  23. package/.server/server/plugins/engine/types.js.map +1 -1
  24. package/.server/server/plugins/engine/views/components/trackingfield.html +3 -0
  25. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  26. package/.server/typings/hapi/index.d.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/server/forms/components.json +8 -0
  29. package/src/server/forms/register-as-a-unicorn-breeder.yaml +4 -0
  30. package/src/server/plugins/engine/components/TrackingField.ts +64 -0
  31. package/src/server/plugins/engine/components/helpers/components.ts +5 -0
  32. package/src/server/plugins/engine/components/index.ts +1 -0
  33. package/src/server/plugins/engine/options.js +2 -1
  34. package/src/server/plugins/engine/plugin.ts +6 -0
  35. package/src/server/plugins/engine/referenceNumbers.ts +2 -0
  36. package/src/server/plugins/engine/routes/index.ts +9 -3
  37. package/src/server/plugins/engine/types.ts +5 -0
  38. package/src/server/plugins/engine/views/components/trackingfield.html +3 -0
  39. package/src/typings/hapi/index.d.ts +2 -0
@@ -6,6 +6,14 @@
6
6
  "path": "/all-components",
7
7
  "title": "All Components",
8
8
  "components": [
9
+ {
10
+ "type": "TrackingField",
11
+ "name": "trackingField",
12
+ "title": "Tracking field",
13
+ "hint": "Help text",
14
+ "options": {},
15
+ "schema": {}
16
+ },
9
17
  {
10
18
  "type": "TextField",
11
19
  "name": "textField",
@@ -5,6 +5,10 @@ pages:
5
5
  - path: '/whats-your-name'
6
6
  title: What's your name?
7
7
  components:
8
+ - type: TrackingField
9
+ name: hasSeenNamePage
10
+ title: Tracks if the user has seen the name page
11
+ options: {}
8
12
  - type: TextField
9
13
  name: textField
10
14
  title: Name
@@ -0,0 +1,21 @@
1
+ import { type TrackingFieldComponent } from '@defra/forms-model';
2
+ import { type BooleanSchema } from 'joi';
3
+ import { FormComponent } from '../../../../server/plugins/engine/components/FormComponent.js';
4
+ import { type ErrorMessageTemplateList, type FormState, type FormStateValue, type FormSubmissionState } from '../../../../server/plugins/engine/types.js';
5
+ export declare class TrackingField extends FormComponent {
6
+ formSchema: BooleanSchema;
7
+ stateSchema: BooleanSchema;
8
+ options: TrackingFieldComponent['options'];
9
+ constructor(def: TrackingFieldComponent, props: ConstructorParameters<typeof FormComponent>[1]);
10
+ getFormValueFromState(state: FormSubmissionState): NonNullable<FormStateValue> | undefined;
11
+ isValue(value?: FormStateValue | FormState): value is true;
12
+ /**
13
+ * For error preview page that shows all possible errors on a component
14
+ */
15
+ getAllPossibleErrors(): ErrorMessageTemplateList;
16
+ static isTracking(value?: FormStateValue | FormState): value is true;
17
+ /**
18
+ * Static version of getAllPossibleErrors that doesn't require a component instance.
19
+ */
20
+ static getAllPossibleErrors(): ErrorMessageTemplateList;
21
+ }
@@ -0,0 +1,46 @@
1
+ import joi from 'joi';
2
+ import { FormComponent, isFormValue } from "./FormComponent.js";
3
+ import { messageTemplate } from "../pageControllers/validationOptions.js";
4
+ export class TrackingField extends FormComponent {
5
+ constructor(def, props) {
6
+ super(def, props);
7
+ const formSchema = joi.boolean().valid(true).label(this.label).required();
8
+ this.formSchema = formSchema.default(false);
9
+ this.stateSchema = formSchema.default(null).allow(null);
10
+ this.schema = {};
11
+ this.options = {};
12
+ }
13
+ getFormValueFromState(state) {
14
+ const {
15
+ name
16
+ } = this;
17
+ return this.getFormValue(state[name]);
18
+ }
19
+ isValue(value) {
20
+ return TrackingField.isTracking(value);
21
+ }
22
+
23
+ /**
24
+ * For error preview page that shows all possible errors on a component
25
+ */
26
+ getAllPossibleErrors() {
27
+ return TrackingField.getAllPossibleErrors();
28
+ }
29
+ static isTracking(value) {
30
+ return isFormValue(value) && typeof value === 'boolean';
31
+ }
32
+
33
+ /**
34
+ * Static version of getAllPossibleErrors that doesn't require a component instance.
35
+ */
36
+ static getAllPossibleErrors() {
37
+ return {
38
+ baseErrors: [{
39
+ type: 'required',
40
+ template: messageTemplate.required
41
+ }],
42
+ advancedSettingsErrors: []
43
+ };
44
+ }
45
+ }
46
+ //# sourceMappingURL=TrackingField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrackingField.js","names":["joi","FormComponent","isFormValue","messageTemplate","TrackingField","constructor","def","props","formSchema","boolean","valid","label","required","default","stateSchema","allow","schema","options","getFormValueFromState","state","name","getFormValue","isValue","value","isTracking","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/TrackingField.ts"],"sourcesContent":["import { type TrackingFieldComponent } from '@defra/forms-model'\nimport joi, { type BooleanSchema } from 'joi'\n\nimport {\n FormComponent,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class TrackingField extends FormComponent {\n declare formSchema: BooleanSchema\n declare stateSchema: BooleanSchema\n declare options: TrackingFieldComponent['options']\n\n constructor(\n def: TrackingFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const formSchema = joi.boolean().valid(true).label(this.label).required()\n\n this.formSchema = formSchema.default(false)\n this.stateSchema = formSchema.default(null).allow(null)\n this.schema = {}\n this.options = {}\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { name } = this\n return this.getFormValue(state[name])\n }\n\n isValue(value?: FormStateValue | FormState): value is true {\n return TrackingField.isTracking(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return TrackingField.getAllPossibleErrors()\n }\n\n static isTracking(value?: FormStateValue | FormState): value is true {\n return isFormValue(value) && typeof value === 'boolean'\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [{ type: 'required', template: messageTemplate.required }],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AACA,OAAOA,GAAG,MAA8B,KAAK;AAE7C,SACEC,aAAa,EACbC,WAAW;AAEb,SAASC,eAAe;AAQxB,OAAO,MAAMC,aAAa,SAASH,aAAa,CAAC;EAK/CI,WAAWA,CACTC,GAA2B,EAC3BC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAMC,UAAU,GAAGR,GAAG,CAACS,OAAO,CAAC,CAAC,CAACC,KAAK,CAAC,IAAI,CAAC,CAACC,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC,CAACC,QAAQ,CAAC,CAAC;IAEzE,IAAI,CAACJ,UAAU,GAAGA,UAAU,CAACK,OAAO,CAAC,KAAK,CAAC;IAC3C,IAAI,CAACC,WAAW,GAAGN,UAAU,CAACK,OAAO,CAAC,IAAI,CAAC,CAACE,KAAK,CAAC,IAAI,CAAC;IACvD,IAAI,CAACC,MAAM,GAAG,CAAC,CAAC;IAChB,IAAI,CAACC,OAAO,GAAG,CAAC,CAAC;EACnB;EAEAC,qBAAqBA,CAACC,KAA0B,EAAE;IAChD,MAAM;MAAEC;IAAK,CAAC,GAAG,IAAI;IACrB,OAAO,IAAI,CAACC,YAAY,CAACF,KAAK,CAACC,IAAI,CAAC,CAAC;EACvC;EAEAE,OAAOA,CAACC,KAAkC,EAAiB;IACzD,OAAOnB,aAAa,CAACoB,UAAU,CAACD,KAAK,CAAC;EACxC;;EAEA;AACF;AACA;EACEE,oBAAoBA,CAAA,EAA6B;IAC/C,OAAOrB,aAAa,CAACqB,oBAAoB,CAAC,CAAC;EAC7C;EAEA,OAAOD,UAAUA,CAACD,KAAkC,EAAiB;IACnE,OAAOrB,WAAW,CAACqB,KAAK,CAAC,IAAI,OAAOA,KAAK,KAAK,SAAS;EACzD;;EAEA;AACF;AACA;EACE,OAAOE,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,CAAC;QAAEC,IAAI,EAAE,UAAU;QAAEC,QAAQ,EAAEzB,eAAe,CAACS;MAAS,CAAC,CAAC;MACtEiB,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
@@ -5,7 +5,7 @@ import * as Components from '../../../../../server/plugins/engine/components/ind
5
5
  import { type Translator } from '../../../../../server/plugins/engine/i18n/types.js';
6
6
  import { type FormState } from '../../../../../server/plugins/engine/types.js';
7
7
  export type Component = InstanceType<(typeof Components)[keyof typeof Components]>;
8
- export type Field = InstanceType<typeof Components.AutocompleteField | typeof Components.RadiosField | typeof Components.YesNoField | typeof Components.CheckboxesField | typeof Components.DatePartsField | typeof Components.DeclarationField | typeof Components.EastingNorthingField | typeof Components.EmailAddressField | typeof Components.LatLongField | typeof Components.MonthYearField | typeof Components.MultilineTextField | typeof Components.NationalGridFieldNumberField | typeof Components.NumberField | typeof Components.OsGridRefField | typeof Components.SelectField | typeof Components.TelephoneNumberField | typeof Components.TextField | typeof Components.UkAddressField | typeof Components.FileUploadField | typeof Components.HiddenField | typeof Components.PaymentField | typeof Components.GeospatialField>;
8
+ export type Field = InstanceType<typeof Components.AutocompleteField | typeof Components.RadiosField | typeof Components.YesNoField | typeof Components.CheckboxesField | typeof Components.DatePartsField | typeof Components.DeclarationField | typeof Components.EastingNorthingField | typeof Components.EmailAddressField | typeof Components.LatLongField | typeof Components.MonthYearField | typeof Components.MultilineTextField | typeof Components.NationalGridFieldNumberField | typeof Components.NumberField | typeof Components.OsGridRefField | typeof Components.SelectField | typeof Components.TelephoneNumberField | typeof Components.TextField | typeof Components.UkAddressField | typeof Components.FileUploadField | typeof Components.HiddenField | typeof Components.TrackingField | typeof Components.PaymentField | typeof Components.GeospatialField>;
9
9
  export type Guidance = InstanceType<typeof Components.Details> | InstanceType<typeof Components.Html> | InstanceType<typeof Components.Markdown> | InstanceType<typeof Components.InsetText> | InstanceType<typeof Components.List> | InstanceType<typeof Components.NotificationBanner>;
10
10
  export type ListField = InstanceType<typeof Components.AutocompleteField | typeof Components.CheckboxesField | typeof Components.RadiosField | typeof Components.SelectField | typeof Components.YesNoField>;
11
11
  export declare const designerUrl: string;
@@ -114,6 +114,9 @@ export function createComponent(def, options) {
114
114
  case ComponentType.HiddenField:
115
115
  component = new Components.HiddenField(def, options);
116
116
  break;
117
+ case ComponentType.TrackingField:
118
+ component = new Components.TrackingField(def, options);
119
+ break;
117
120
  case ComponentType.PaymentField:
118
121
  component = new Components.PaymentField(def, options);
119
122
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"components.js","names":["ComponentType","config","ListFormComponent","escapeMarkdown","Components","markdown","UnknownComponentTypeError","designerUrl","get","hasListFormField","field","type","undefined","isListFieldType","allowedTypes","AutocompleteField","CheckboxesField","RadiosField","SelectField","YesNoField","includes","createComponent","def","options","component","DatePartsField","DeclarationField","Details","EmailAddressField","Html","InsetText","List","Markdown","NotificationBanner","MultilineTextField","NumberField","TelephoneNumberField","TextField","UkAddressField","MonthYearField","FileUploadField","EastingNorthingField","OsGridRefField","NationalGridFieldNumberField","LatLongField","HiddenField","PaymentField","GeospatialField","getAnswer","state","translator","format","getAnswerMarkdown","context","getContextValueFromState","toString","parse","async","trim","getDisplayStringFromState","answer","answerEscaped","files","getFormValueFromState","length","map","status","file","form","filename","fileId","join","values","flat","items","filter","value","item","resolvedText","tListItem","text","label","line","toLowerCase","split","concat","features","getDisplayStringFromFormValue"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/components.ts"],"sourcesContent":["import { ComponentType, type ComponentDef } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport { markdown } from '~/src/server/plugins/engine/components/markdownParser.js'\nimport { UnknownComponentTypeError } from '~/src/server/plugins/engine/errors.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormState } from '~/src/server/plugins/engine/types.js'\n\n// All component instances\nexport type Component = InstanceType<\n (typeof Components)[keyof typeof Components]\n>\n\n// Field component instances only\nexport type Field = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.RadiosField\n | typeof Components.YesNoField\n | typeof Components.CheckboxesField\n | typeof Components.DatePartsField\n | typeof Components.DeclarationField\n | typeof Components.EastingNorthingField\n | typeof Components.EmailAddressField\n | typeof Components.LatLongField\n | typeof Components.MonthYearField\n | typeof Components.MultilineTextField\n | typeof Components.NationalGridFieldNumberField\n | typeof Components.NumberField\n | typeof Components.OsGridRefField\n | typeof Components.SelectField\n | typeof Components.TelephoneNumberField\n | typeof Components.TextField\n | typeof Components.UkAddressField\n | typeof Components.FileUploadField\n | typeof Components.HiddenField\n | typeof Components.PaymentField\n | typeof Components.GeospatialField\n>\n\n// Guidance component instances only\nexport type Guidance =\n | InstanceType<typeof Components.Details>\n | InstanceType<typeof Components.Html>\n | InstanceType<typeof Components.Markdown>\n | InstanceType<typeof Components.InsetText>\n | InstanceType<typeof Components.List>\n | InstanceType<typeof Components.NotificationBanner>\n\n// List component instances only\nexport type ListField = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.CheckboxesField\n | typeof Components.RadiosField\n | typeof Components.SelectField\n | typeof Components.YesNoField\n>\n\nexport const designerUrl = config.get('designerUrl')\n\n// Re-export markdown from its own module to avoid circular dependencies\nexport { markdown } from '~/src/server/plugins/engine/components/markdownParser.js'\n\n/**\n * Filter known components with lists\n */\nexport function hasListFormField(\n field?: Partial<Component>\n): field is ListFormComponent {\n return !!field && field.type !== undefined && isListFieldType(field.type)\n}\n\nexport function isListFieldType(\n type?: ComponentType\n): type is ListField['type'] {\n const allowedTypes = [\n ComponentType.AutocompleteField,\n ComponentType.CheckboxesField,\n ComponentType.RadiosField,\n ComponentType.SelectField,\n ComponentType.YesNoField\n ]\n\n return !!type && allowedTypes.includes(type)\n}\n\n/**\n * Create field instance for each {@link ComponentDef} type\n */\nexport function createComponent(\n def: ComponentDef,\n options: ConstructorParameters<typeof ComponentBase>[1]\n): Component {\n let component: Component | undefined\n\n switch (def.type) {\n case ComponentType.AutocompleteField:\n component = new Components.AutocompleteField(def, options)\n break\n\n case ComponentType.CheckboxesField:\n component = new Components.CheckboxesField(def, options)\n break\n\n case ComponentType.DatePartsField:\n component = new Components.DatePartsField(def, options)\n break\n\n case ComponentType.DeclarationField:\n component = new Components.DeclarationField(def, options)\n break\n\n case ComponentType.Details:\n component = new Components.Details(def, options)\n break\n\n case ComponentType.EmailAddressField:\n component = new Components.EmailAddressField(def, options)\n break\n\n case ComponentType.Html:\n component = new Components.Html(def, options)\n break\n\n case ComponentType.InsetText:\n component = new Components.InsetText(def, options)\n break\n\n case ComponentType.List:\n component = new Components.List(def, options)\n break\n\n case ComponentType.Markdown:\n component = new Components.Markdown(def, options)\n break\n\n case ComponentType.NotificationBanner:\n component = new Components.NotificationBanner(def, options)\n break\n\n case ComponentType.MultilineTextField:\n component = new Components.MultilineTextField(def, options)\n break\n\n case ComponentType.NumberField:\n component = new Components.NumberField(def, options)\n break\n\n case ComponentType.RadiosField:\n component = new Components.RadiosField(def, options)\n break\n\n case ComponentType.SelectField:\n component = new Components.SelectField(def, options)\n break\n\n case ComponentType.TelephoneNumberField:\n component = new Components.TelephoneNumberField(def, options)\n break\n\n case ComponentType.TextField:\n component = new Components.TextField(def, options)\n break\n\n case ComponentType.UkAddressField:\n component = new Components.UkAddressField(def, options)\n break\n\n case ComponentType.YesNoField:\n component = new Components.YesNoField(def, options)\n break\n\n case ComponentType.MonthYearField:\n component = new Components.MonthYearField(def, options)\n break\n\n case ComponentType.FileUploadField:\n component = new Components.FileUploadField(def, options)\n break\n\n case ComponentType.EastingNorthingField:\n component = new Components.EastingNorthingField(def, options)\n break\n\n case ComponentType.OsGridRefField:\n component = new Components.OsGridRefField(def, options)\n break\n\n case ComponentType.NationalGridFieldNumberField:\n component = new Components.NationalGridFieldNumberField(def, options)\n break\n\n case ComponentType.LatLongField:\n component = new Components.LatLongField(def, options)\n break\n\n case ComponentType.HiddenField:\n component = new Components.HiddenField(def, options)\n break\n\n case ComponentType.PaymentField:\n component = new Components.PaymentField(def, options)\n break\n\n case ComponentType.GeospatialField:\n component = new Components.GeospatialField(def, options)\n break\n }\n\n if (typeof component === 'undefined') {\n throw new UnknownComponentTypeError(def.type)\n }\n\n return component\n}\n\n/**\n * Get formatted answer for a field\n */\nexport function getAnswer(\n field: Field,\n state: FormState,\n translator: Translator,\n options: {\n format:\n | 'data' // Submission data\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n // Use escaped display text for GOV.UK Notify emails\n if (options.format === 'email') {\n return getAnswerMarkdown(field, state, translator, { format: 'email' })\n }\n\n // Use context value for submission data\n if (options.format === 'data') {\n const context = field.getContextValueFromState(state)\n return context?.toString() ?? ''\n }\n\n // Use display HTML for check answers summary (multi line)\n if (\n field instanceof ListFormComponent ||\n field instanceof Components.MultilineTextField ||\n field instanceof Components.UkAddressField ||\n field instanceof Components.EastingNorthingField ||\n field instanceof Components.LatLongField\n ) {\n return markdown\n .parse(\n getAnswerMarkdown(field, state, translator, { format: 'summary' }),\n { async: false }\n )\n .trim()\n }\n\n // Use display text for check answers summary (single line)\n return field.getDisplayStringFromState(state, translator)\n}\n\n/**\n * Get formatted answer for a field (Markdown only)\n */\nexport function getAnswerMarkdown(\n field: Field,\n state: FormState,\n translator: Translator,\n options: {\n format:\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n const answer = field.getDisplayStringFromState(state, translator)\n\n // Use escaped display text\n let answerEscaped = `${escapeMarkdown(answer)}\\n`\n\n if (field instanceof Components.FileUploadField) {\n const files = field.getFormValueFromState(state)\n\n // Skip empty files\n if (!files?.length) {\n return answerEscaped\n }\n\n answerEscaped = `${escapeMarkdown(answer)}:\\n\\n`\n\n // Append bullet points\n answerEscaped += files\n .map(({ status }) => {\n const { file } = status.form\n const filename = escapeMarkdown(file.filename)\n return `* [${filename}](${designerUrl}/file-download/${file.fileId})\\n`\n })\n .join('')\n } else if (field instanceof ListFormComponent) {\n const values = [field.getContextValueFromState(state)].flat()\n const items = field.items.filter(({ value }) => values.includes(value))\n\n // Skip empty values\n if (!items.length) {\n return answerEscaped\n }\n\n answerEscaped = ''\n\n // Append bullet points\n answerEscaped += items\n .map((item) => {\n const resolvedText = translator.tListItem(item, 'text') || item.text\n const label = escapeMarkdown(resolvedText)\n const value = escapeMarkdown(`(${item.value})`)\n\n let line = label\n\n // Prepend bullet points for checkboxes only\n if (field instanceof Components.CheckboxesField) {\n line = `* ${line}`\n }\n\n // Append raw values in parentheses\n // e.g. `* None of the above (false)`\n return options.format === 'email' &&\n `${item.value}`.toLowerCase() !== item.text.toLowerCase()\n ? `${line} ${value}\\n`\n : `${line}\\n`\n })\n .join('')\n } else if (field instanceof Components.MultilineTextField) {\n // Preserve Multiline text new lines\n answerEscaped = answer\n .split(/(?:\\r?\\n)+/)\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (field instanceof Components.UkAddressField) {\n // Format UK addresses into new lines\n answerEscaped = (field.getContextValueFromState(state) ?? [])\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (\n field instanceof Components.EastingNorthingField ||\n field instanceof Components.LatLongField\n ) {\n answerEscaped = answer ? `${answer}\\n` : ''\n } else if (field instanceof Components.GeospatialField) {\n const features = field.getFormValueFromState(state)\n const value = field.getDisplayStringFromFormValue(features, translator)\n\n answerEscaped = value ? `${value}\\n` : ''\n }\n\n return answerEscaped\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"components.js","names":["ComponentType","config","ListFormComponent","escapeMarkdown","Components","markdown","UnknownComponentTypeError","designerUrl","get","hasListFormField","field","type","undefined","isListFieldType","allowedTypes","AutocompleteField","CheckboxesField","RadiosField","SelectField","YesNoField","includes","createComponent","def","options","component","DatePartsField","DeclarationField","Details","EmailAddressField","Html","InsetText","List","Markdown","NotificationBanner","MultilineTextField","NumberField","TelephoneNumberField","TextField","UkAddressField","MonthYearField","FileUploadField","EastingNorthingField","OsGridRefField","NationalGridFieldNumberField","LatLongField","HiddenField","TrackingField","PaymentField","GeospatialField","getAnswer","state","translator","format","getAnswerMarkdown","context","getContextValueFromState","toString","parse","async","trim","getDisplayStringFromState","answer","answerEscaped","files","getFormValueFromState","length","map","status","file","form","filename","fileId","join","values","flat","items","filter","value","item","resolvedText","tListItem","text","label","line","toLowerCase","split","concat","features","getDisplayStringFromFormValue"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/components.ts"],"sourcesContent":["import { ComponentType, type ComponentDef } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport { markdown } from '~/src/server/plugins/engine/components/markdownParser.js'\nimport { UnknownComponentTypeError } from '~/src/server/plugins/engine/errors.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormState } from '~/src/server/plugins/engine/types.js'\n\n// All component instances\nexport type Component = InstanceType<\n (typeof Components)[keyof typeof Components]\n>\n\n// Field component instances only\nexport type Field = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.RadiosField\n | typeof Components.YesNoField\n | typeof Components.CheckboxesField\n | typeof Components.DatePartsField\n | typeof Components.DeclarationField\n | typeof Components.EastingNorthingField\n | typeof Components.EmailAddressField\n | typeof Components.LatLongField\n | typeof Components.MonthYearField\n | typeof Components.MultilineTextField\n | typeof Components.NationalGridFieldNumberField\n | typeof Components.NumberField\n | typeof Components.OsGridRefField\n | typeof Components.SelectField\n | typeof Components.TelephoneNumberField\n | typeof Components.TextField\n | typeof Components.UkAddressField\n | typeof Components.FileUploadField\n | typeof Components.HiddenField\n | typeof Components.TrackingField\n | typeof Components.PaymentField\n | typeof Components.GeospatialField\n>\n\n// Guidance component instances only\nexport type Guidance =\n | InstanceType<typeof Components.Details>\n | InstanceType<typeof Components.Html>\n | InstanceType<typeof Components.Markdown>\n | InstanceType<typeof Components.InsetText>\n | InstanceType<typeof Components.List>\n | InstanceType<typeof Components.NotificationBanner>\n\n// List component instances only\nexport type ListField = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.CheckboxesField\n | typeof Components.RadiosField\n | typeof Components.SelectField\n | typeof Components.YesNoField\n>\n\nexport const designerUrl = config.get('designerUrl')\n\n// Re-export markdown from its own module to avoid circular dependencies\nexport { markdown } from '~/src/server/plugins/engine/components/markdownParser.js'\n\n/**\n * Filter known components with lists\n */\nexport function hasListFormField(\n field?: Partial<Component>\n): field is ListFormComponent {\n return !!field && field.type !== undefined && isListFieldType(field.type)\n}\n\nexport function isListFieldType(\n type?: ComponentType\n): type is ListField['type'] {\n const allowedTypes = [\n ComponentType.AutocompleteField,\n ComponentType.CheckboxesField,\n ComponentType.RadiosField,\n ComponentType.SelectField,\n ComponentType.YesNoField\n ]\n\n return !!type && allowedTypes.includes(type)\n}\n\n/**\n * Create field instance for each {@link ComponentDef} type\n */\nexport function createComponent(\n def: ComponentDef,\n options: ConstructorParameters<typeof ComponentBase>[1]\n): Component {\n let component: Component | undefined\n\n switch (def.type) {\n case ComponentType.AutocompleteField:\n component = new Components.AutocompleteField(def, options)\n break\n\n case ComponentType.CheckboxesField:\n component = new Components.CheckboxesField(def, options)\n break\n\n case ComponentType.DatePartsField:\n component = new Components.DatePartsField(def, options)\n break\n\n case ComponentType.DeclarationField:\n component = new Components.DeclarationField(def, options)\n break\n\n case ComponentType.Details:\n component = new Components.Details(def, options)\n break\n\n case ComponentType.EmailAddressField:\n component = new Components.EmailAddressField(def, options)\n break\n\n case ComponentType.Html:\n component = new Components.Html(def, options)\n break\n\n case ComponentType.InsetText:\n component = new Components.InsetText(def, options)\n break\n\n case ComponentType.List:\n component = new Components.List(def, options)\n break\n\n case ComponentType.Markdown:\n component = new Components.Markdown(def, options)\n break\n\n case ComponentType.NotificationBanner:\n component = new Components.NotificationBanner(def, options)\n break\n\n case ComponentType.MultilineTextField:\n component = new Components.MultilineTextField(def, options)\n break\n\n case ComponentType.NumberField:\n component = new Components.NumberField(def, options)\n break\n\n case ComponentType.RadiosField:\n component = new Components.RadiosField(def, options)\n break\n\n case ComponentType.SelectField:\n component = new Components.SelectField(def, options)\n break\n\n case ComponentType.TelephoneNumberField:\n component = new Components.TelephoneNumberField(def, options)\n break\n\n case ComponentType.TextField:\n component = new Components.TextField(def, options)\n break\n\n case ComponentType.UkAddressField:\n component = new Components.UkAddressField(def, options)\n break\n\n case ComponentType.YesNoField:\n component = new Components.YesNoField(def, options)\n break\n\n case ComponentType.MonthYearField:\n component = new Components.MonthYearField(def, options)\n break\n\n case ComponentType.FileUploadField:\n component = new Components.FileUploadField(def, options)\n break\n\n case ComponentType.EastingNorthingField:\n component = new Components.EastingNorthingField(def, options)\n break\n\n case ComponentType.OsGridRefField:\n component = new Components.OsGridRefField(def, options)\n break\n\n case ComponentType.NationalGridFieldNumberField:\n component = new Components.NationalGridFieldNumberField(def, options)\n break\n\n case ComponentType.LatLongField:\n component = new Components.LatLongField(def, options)\n break\n\n case ComponentType.HiddenField:\n component = new Components.HiddenField(def, options)\n break\n\n case ComponentType.TrackingField:\n component = new Components.TrackingField(def, options)\n break\n\n case ComponentType.PaymentField:\n component = new Components.PaymentField(def, options)\n break\n\n case ComponentType.GeospatialField:\n component = new Components.GeospatialField(def, options)\n break\n }\n\n if (typeof component === 'undefined') {\n throw new UnknownComponentTypeError(def.type)\n }\n\n return component\n}\n\n/**\n * Get formatted answer for a field\n */\nexport function getAnswer(\n field: Field,\n state: FormState,\n translator: Translator,\n options: {\n format:\n | 'data' // Submission data\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n // Use escaped display text for GOV.UK Notify emails\n if (options.format === 'email') {\n return getAnswerMarkdown(field, state, translator, { format: 'email' })\n }\n\n // Use context value for submission data\n if (options.format === 'data') {\n const context = field.getContextValueFromState(state)\n return context?.toString() ?? ''\n }\n\n // Use display HTML for check answers summary (multi line)\n if (\n field instanceof ListFormComponent ||\n field instanceof Components.MultilineTextField ||\n field instanceof Components.UkAddressField ||\n field instanceof Components.EastingNorthingField ||\n field instanceof Components.LatLongField\n ) {\n return markdown\n .parse(\n getAnswerMarkdown(field, state, translator, { format: 'summary' }),\n { async: false }\n )\n .trim()\n }\n\n // Use display text for check answers summary (single line)\n return field.getDisplayStringFromState(state, translator)\n}\n\n/**\n * Get formatted answer for a field (Markdown only)\n */\nexport function getAnswerMarkdown(\n field: Field,\n state: FormState,\n translator: Translator,\n options: {\n format:\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n const answer = field.getDisplayStringFromState(state, translator)\n\n // Use escaped display text\n let answerEscaped = `${escapeMarkdown(answer)}\\n`\n\n if (field instanceof Components.FileUploadField) {\n const files = field.getFormValueFromState(state)\n\n // Skip empty files\n if (!files?.length) {\n return answerEscaped\n }\n\n answerEscaped = `${escapeMarkdown(answer)}:\\n\\n`\n\n // Append bullet points\n answerEscaped += files\n .map(({ status }) => {\n const { file } = status.form\n const filename = escapeMarkdown(file.filename)\n return `* [${filename}](${designerUrl}/file-download/${file.fileId})\\n`\n })\n .join('')\n } else if (field instanceof ListFormComponent) {\n const values = [field.getContextValueFromState(state)].flat()\n const items = field.items.filter(({ value }) => values.includes(value))\n\n // Skip empty values\n if (!items.length) {\n return answerEscaped\n }\n\n answerEscaped = ''\n\n // Append bullet points\n answerEscaped += items\n .map((item) => {\n const resolvedText = translator.tListItem(item, 'text') || item.text\n const label = escapeMarkdown(resolvedText)\n const value = escapeMarkdown(`(${item.value})`)\n\n let line = label\n\n // Prepend bullet points for checkboxes only\n if (field instanceof Components.CheckboxesField) {\n line = `* ${line}`\n }\n\n // Append raw values in parentheses\n // e.g. `* None of the above (false)`\n return options.format === 'email' &&\n `${item.value}`.toLowerCase() !== item.text.toLowerCase()\n ? `${line} ${value}\\n`\n : `${line}\\n`\n })\n .join('')\n } else if (field instanceof Components.MultilineTextField) {\n // Preserve Multiline text new lines\n answerEscaped = answer\n .split(/(?:\\r?\\n)+/)\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (field instanceof Components.UkAddressField) {\n // Format UK addresses into new lines\n answerEscaped = (field.getContextValueFromState(state) ?? [])\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (\n field instanceof Components.EastingNorthingField ||\n field instanceof Components.LatLongField\n ) {\n answerEscaped = answer ? `${answer}\\n` : ''\n } else if (field instanceof Components.GeospatialField) {\n const features = field.getFormValueFromState(state)\n const value = field.getDisplayStringFromFormValue(features, translator)\n\n answerEscaped = value ? `${value}\\n` : ''\n }\n\n return answerEscaped\n}\n"],"mappings":"AAAA,SAASA,aAAa,QAA2B,oBAAoB;AAErE,SAASC,MAAM;AAEf,SAASC,iBAAiB;AAC1B,SAASC,cAAc;AACvB,OAAO,KAAKC,UAAU;AACtB,SAASC,QAAQ;AACjB,SAASC,yBAAyB;;AAIlC;;AAKA;;AA2BA;;AASA;;AASA,OAAO,MAAMC,WAAW,GAAGN,MAAM,CAACO,GAAG,CAAC,aAAa,CAAC;;AAEpD;AACA,SAASH,QAAQ;;AAEjB;AACA;AACA;AACA,OAAO,SAASI,gBAAgBA,CAC9BC,KAA0B,EACE;EAC5B,OAAO,CAAC,CAACA,KAAK,IAAIA,KAAK,CAACC,IAAI,KAAKC,SAAS,IAAIC,eAAe,CAACH,KAAK,CAACC,IAAI,CAAC;AAC3E;AAEA,OAAO,SAASE,eAAeA,CAC7BF,IAAoB,EACO;EAC3B,MAAMG,YAAY,GAAG,CACnBd,aAAa,CAACe,iBAAiB,EAC/Bf,aAAa,CAACgB,eAAe,EAC7BhB,aAAa,CAACiB,WAAW,EACzBjB,aAAa,CAACkB,WAAW,EACzBlB,aAAa,CAACmB,UAAU,CACzB;EAED,OAAO,CAAC,CAACR,IAAI,IAAIG,YAAY,CAACM,QAAQ,CAACT,IAAI,CAAC;AAC9C;;AAEA;AACA;AACA;AACA,OAAO,SAASU,eAAeA,CAC7BC,GAAiB,EACjBC,OAAuD,EAC5C;EACX,IAAIC,SAAgC;EAEpC,QAAQF,GAAG,CAACX,IAAI;IACd,KAAKX,aAAa,CAACe,iBAAiB;MAClCS,SAAS,GAAG,IAAIpB,UAAU,CAACW,iBAAiB,CAACO,GAAG,EAAEC,OAAO,CAAC;MAC1D;IAEF,KAAKvB,aAAa,CAACgB,eAAe;MAChCQ,SAAS,GAAG,IAAIpB,UAAU,CAACY,eAAe,CAACM,GAAG,EAAEC,OAAO,CAAC;MACxD;IAEF,KAAKvB,aAAa,CAACyB,cAAc;MAC/BD,SAAS,GAAG,IAAIpB,UAAU,CAACqB,cAAc,CAACH,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKvB,aAAa,CAAC0B,gBAAgB;MACjCF,SAAS,GAAG,IAAIpB,UAAU,CAACsB,gBAAgB,CAACJ,GAAG,EAAEC,OAAO,CAAC;MACzD;IAEF,KAAKvB,aAAa,CAAC2B,OAAO;MACxBH,SAAS,GAAG,IAAIpB,UAAU,CAACuB,OAAO,CAACL,GAAG,EAAEC,OAAO,CAAC;MAChD;IAEF,KAAKvB,aAAa,CAAC4B,iBAAiB;MAClCJ,SAAS,GAAG,IAAIpB,UAAU,CAACwB,iBAAiB,CAACN,GAAG,EAAEC,OAAO,CAAC;MAC1D;IAEF,KAAKvB,aAAa,CAAC6B,IAAI;MACrBL,SAAS,GAAG,IAAIpB,UAAU,CAACyB,IAAI,CAACP,GAAG,EAAEC,OAAO,CAAC;MAC7C;IAEF,KAAKvB,aAAa,CAAC8B,SAAS;MAC1BN,SAAS,GAAG,IAAIpB,UAAU,CAAC0B,SAAS,CAACR,GAAG,EAAEC,OAAO,CAAC;MAClD;IAEF,KAAKvB,aAAa,CAAC+B,IAAI;MACrBP,SAAS,GAAG,IAAIpB,UAAU,CAAC2B,IAAI,CAACT,GAAG,EAAEC,OAAO,CAAC;MAC7C;IAEF,KAAKvB,aAAa,CAACgC,QAAQ;MACzBR,SAAS,GAAG,IAAIpB,UAAU,CAAC4B,QAAQ,CAACV,GAAG,EAAEC,OAAO,CAAC;MACjD;IAEF,KAAKvB,aAAa,CAACiC,kBAAkB;MACnCT,SAAS,GAAG,IAAIpB,UAAU,CAAC6B,kBAAkB,CAACX,GAAG,EAAEC,OAAO,CAAC;MAC3D;IAEF,KAAKvB,aAAa,CAACkC,kBAAkB;MACnCV,SAAS,GAAG,IAAIpB,UAAU,CAAC8B,kBAAkB,CAACZ,GAAG,EAAEC,OAAO,CAAC;MAC3D;IAEF,KAAKvB,aAAa,CAACmC,WAAW;MAC5BX,SAAS,GAAG,IAAIpB,UAAU,CAAC+B,WAAW,CAACb,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKvB,aAAa,CAACiB,WAAW;MAC5BO,SAAS,GAAG,IAAIpB,UAAU,CAACa,WAAW,CAACK,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKvB,aAAa,CAACkB,WAAW;MAC5BM,SAAS,GAAG,IAAIpB,UAAU,CAACc,WAAW,CAACI,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKvB,aAAa,CAACoC,oBAAoB;MACrCZ,SAAS,GAAG,IAAIpB,UAAU,CAACgC,oBAAoB,CAACd,GAAG,EAAEC,OAAO,CAAC;MAC7D;IAEF,KAAKvB,aAAa,CAACqC,SAAS;MAC1Bb,SAAS,GAAG,IAAIpB,UAAU,CAACiC,SAAS,CAACf,GAAG,EAAEC,OAAO,CAAC;MAClD;IAEF,KAAKvB,aAAa,CAACsC,cAAc;MAC/Bd,SAAS,GAAG,IAAIpB,UAAU,CAACkC,cAAc,CAAChB,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKvB,aAAa,CAACmB,UAAU;MAC3BK,SAAS,GAAG,IAAIpB,UAAU,CAACe,UAAU,CAACG,GAAG,EAAEC,OAAO,CAAC;MACnD;IAEF,KAAKvB,aAAa,CAACuC,cAAc;MAC/Bf,SAAS,GAAG,IAAIpB,UAAU,CAACmC,cAAc,CAACjB,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKvB,aAAa,CAACwC,eAAe;MAChChB,SAAS,GAAG,IAAIpB,UAAU,CAACoC,eAAe,CAAClB,GAAG,EAAEC,OAAO,CAAC;MACxD;IAEF,KAAKvB,aAAa,CAACyC,oBAAoB;MACrCjB,SAAS,GAAG,IAAIpB,UAAU,CAACqC,oBAAoB,CAACnB,GAAG,EAAEC,OAAO,CAAC;MAC7D;IAEF,KAAKvB,aAAa,CAAC0C,cAAc;MAC/BlB,SAAS,GAAG,IAAIpB,UAAU,CAACsC,cAAc,CAACpB,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKvB,aAAa,CAAC2C,4BAA4B;MAC7CnB,SAAS,GAAG,IAAIpB,UAAU,CAACuC,4BAA4B,CAACrB,GAAG,EAAEC,OAAO,CAAC;MACrE;IAEF,KAAKvB,aAAa,CAAC4C,YAAY;MAC7BpB,SAAS,GAAG,IAAIpB,UAAU,CAACwC,YAAY,CAACtB,GAAG,EAAEC,OAAO,CAAC;MACrD;IAEF,KAAKvB,aAAa,CAAC6C,WAAW;MAC5BrB,SAAS,GAAG,IAAIpB,UAAU,CAACyC,WAAW,CAACvB,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKvB,aAAa,CAAC8C,aAAa;MAC9BtB,SAAS,GAAG,IAAIpB,UAAU,CAAC0C,aAAa,CAACxB,GAAG,EAAEC,OAAO,CAAC;MACtD;IAEF,KAAKvB,aAAa,CAAC+C,YAAY;MAC7BvB,SAAS,GAAG,IAAIpB,UAAU,CAAC2C,YAAY,CAACzB,GAAG,EAAEC,OAAO,CAAC;MACrD;IAEF,KAAKvB,aAAa,CAACgD,eAAe;MAChCxB,SAAS,GAAG,IAAIpB,UAAU,CAAC4C,eAAe,CAAC1B,GAAG,EAAEC,OAAO,CAAC;MACxD;EACJ;EAEA,IAAI,OAAOC,SAAS,KAAK,WAAW,EAAE;IACpC,MAAM,IAAIlB,yBAAyB,CAACgB,GAAG,CAACX,IAAI,CAAC;EAC/C;EAEA,OAAOa,SAAS;AAClB;;AAEA;AACA;AACA;AACA,OAAO,SAASyB,SAASA,CACvBvC,KAAY,EACZwC,KAAgB,EAChBC,UAAsB,EACtB5B,OAKC,GAAG;EAAE6B,MAAM,EAAE;AAAU,CAAC,EACzB;EACA;EACA,IAAI7B,OAAO,CAAC6B,MAAM,KAAK,OAAO,EAAE;IAC9B,OAAOC,iBAAiB,CAAC3C,KAAK,EAAEwC,KAAK,EAAEC,UAAU,EAAE;MAAEC,MAAM,EAAE;IAAQ,CAAC,CAAC;EACzE;;EAEA;EACA,IAAI7B,OAAO,CAAC6B,MAAM,KAAK,MAAM,EAAE;IAC7B,MAAME,OAAO,GAAG5C,KAAK,CAAC6C,wBAAwB,CAACL,KAAK,CAAC;IACrD,OAAOI,OAAO,EAAEE,QAAQ,CAAC,CAAC,IAAI,EAAE;EAClC;;EAEA;EACA,IACE9C,KAAK,YAAYR,iBAAiB,IAClCQ,KAAK,YAAYN,UAAU,CAAC8B,kBAAkB,IAC9CxB,KAAK,YAAYN,UAAU,CAACkC,cAAc,IAC1C5B,KAAK,YAAYN,UAAU,CAACqC,oBAAoB,IAChD/B,KAAK,YAAYN,UAAU,CAACwC,YAAY,EACxC;IACA,OAAOvC,QAAQ,CACZoD,KAAK,CACJJ,iBAAiB,CAAC3C,KAAK,EAAEwC,KAAK,EAAEC,UAAU,EAAE;MAAEC,MAAM,EAAE;IAAU,CAAC,CAAC,EAClE;MAAEM,KAAK,EAAE;IAAM,CACjB,CAAC,CACAC,IAAI,CAAC,CAAC;EACX;;EAEA;EACA,OAAOjD,KAAK,CAACkD,yBAAyB,CAACV,KAAK,EAAEC,UAAU,CAAC;AAC3D;;AAEA;AACA;AACA;AACA,OAAO,SAASE,iBAAiBA,CAC/B3C,KAAY,EACZwC,KAAgB,EAChBC,UAAsB,EACtB5B,OAIC,GAAG;EAAE6B,MAAM,EAAE;AAAU,CAAC,EACzB;EACA,MAAMS,MAAM,GAAGnD,KAAK,CAACkD,yBAAyB,CAACV,KAAK,EAAEC,UAAU,CAAC;;EAEjE;EACA,IAAIW,aAAa,GAAG,GAAG3D,cAAc,CAAC0D,MAAM,CAAC,IAAI;EAEjD,IAAInD,KAAK,YAAYN,UAAU,CAACoC,eAAe,EAAE;IAC/C,MAAMuB,KAAK,GAAGrD,KAAK,CAACsD,qBAAqB,CAACd,KAAK,CAAC;;IAEhD;IACA,IAAI,CAACa,KAAK,EAAEE,MAAM,EAAE;MAClB,OAAOH,aAAa;IACtB;IAEAA,aAAa,GAAG,GAAG3D,cAAc,CAAC0D,MAAM,CAAC,OAAO;;IAEhD;IACAC,aAAa,IAAIC,KAAK,CACnBG,GAAG,CAAC,CAAC;MAAEC;IAAO,CAAC,KAAK;MACnB,MAAM;QAAEC;MAAK,CAAC,GAAGD,MAAM,CAACE,IAAI;MAC5B,MAAMC,QAAQ,GAAGnE,cAAc,CAACiE,IAAI,CAACE,QAAQ,CAAC;MAC9C,OAAO,MAAMA,QAAQ,KAAK/D,WAAW,kBAAkB6D,IAAI,CAACG,MAAM,KAAK;IACzE,CAAC,CAAC,CACDC,IAAI,CAAC,EAAE,CAAC;EACb,CAAC,MAAM,IAAI9D,KAAK,YAAYR,iBAAiB,EAAE;IAC7C,MAAMuE,MAAM,GAAG,CAAC/D,KAAK,CAAC6C,wBAAwB,CAACL,KAAK,CAAC,CAAC,CAACwB,IAAI,CAAC,CAAC;IAC7D,MAAMC,KAAK,GAAGjE,KAAK,CAACiE,KAAK,CAACC,MAAM,CAAC,CAAC;MAAEC;IAAM,CAAC,KAAKJ,MAAM,CAACrD,QAAQ,CAACyD,KAAK,CAAC,CAAC;;IAEvE;IACA,IAAI,CAACF,KAAK,CAACV,MAAM,EAAE;MACjB,OAAOH,aAAa;IACtB;IAEAA,aAAa,GAAG,EAAE;;IAElB;IACAA,aAAa,IAAIa,KAAK,CACnBT,GAAG,CAAEY,IAAI,IAAK;MACb,MAAMC,YAAY,GAAG5B,UAAU,CAAC6B,SAAS,CAACF,IAAI,EAAE,MAAM,CAAC,IAAIA,IAAI,CAACG,IAAI;MACpE,MAAMC,KAAK,GAAG/E,cAAc,CAAC4E,YAAY,CAAC;MAC1C,MAAMF,KAAK,GAAG1E,cAAc,CAAC,IAAI2E,IAAI,CAACD,KAAK,GAAG,CAAC;MAE/C,IAAIM,IAAI,GAAGD,KAAK;;MAEhB;MACA,IAAIxE,KAAK,YAAYN,UAAU,CAACY,eAAe,EAAE;QAC/CmE,IAAI,GAAG,KAAKA,IAAI,EAAE;MACpB;;MAEA;MACA;MACA,OAAO5D,OAAO,CAAC6B,MAAM,KAAK,OAAO,IAC/B,GAAG0B,IAAI,CAACD,KAAK,EAAE,CAACO,WAAW,CAAC,CAAC,KAAKN,IAAI,CAACG,IAAI,CAACG,WAAW,CAAC,CAAC,GACvD,GAAGD,IAAI,IAAIN,KAAK,IAAI,GACpB,GAAGM,IAAI,IAAI;IACjB,CAAC,CAAC,CACDX,IAAI,CAAC,EAAE,CAAC;EACb,CAAC,MAAM,IAAI9D,KAAK,YAAYN,UAAU,CAAC8B,kBAAkB,EAAE;IACzD;IACA4B,aAAa,GAAGD,MAAM,CACnBwB,KAAK,CAAC,YAAY,CAAC,CACnBnB,GAAG,CAAC/D,cAAc,CAAC,CACnBqE,IAAI,CAAC,IAAI,CAAC,CACVc,MAAM,CAAC,IAAI,CAAC;EACjB,CAAC,MAAM,IAAI5E,KAAK,YAAYN,UAAU,CAACkC,cAAc,EAAE;IACrD;IACAwB,aAAa,GAAG,CAACpD,KAAK,CAAC6C,wBAAwB,CAACL,KAAK,CAAC,IAAI,EAAE,EACzDgB,GAAG,CAAC/D,cAAc,CAAC,CACnBqE,IAAI,CAAC,IAAI,CAAC,CACVc,MAAM,CAAC,IAAI,CAAC;EACjB,CAAC,MAAM,IACL5E,KAAK,YAAYN,UAAU,CAACqC,oBAAoB,IAChD/B,KAAK,YAAYN,UAAU,CAACwC,YAAY,EACxC;IACAkB,aAAa,GAAGD,MAAM,GAAG,GAAGA,MAAM,IAAI,GAAG,EAAE;EAC7C,CAAC,MAAM,IAAInD,KAAK,YAAYN,UAAU,CAAC4C,eAAe,EAAE;IACtD,MAAMuC,QAAQ,GAAG7E,KAAK,CAACsD,qBAAqB,CAACd,KAAK,CAAC;IACnD,MAAM2B,KAAK,GAAGnE,KAAK,CAAC8E,6BAA6B,CAACD,QAAQ,EAAEpC,UAAU,CAAC;IAEvEW,aAAa,GAAGe,KAAK,GAAG,GAAGA,KAAK,IAAI,GAAG,EAAE;EAC3C;EAEA,OAAOf,aAAa;AACtB","ignoreList":[]}
@@ -29,5 +29,6 @@ export { OsGridRefField } from '../../../../server/plugins/engine/components/OsG
29
29
  export { NationalGridFieldNumberField } from '../../../../server/plugins/engine/components/NationalGridFieldNumberField.js';
30
30
  export { LatLongField } from '../../../../server/plugins/engine/components/LatLongField.js';
31
31
  export { HiddenField } from '../../../../server/plugins/engine/components/HiddenField.js';
32
+ export { TrackingField } from '../../../../server/plugins/engine/components/TrackingField.js';
32
33
  export { PaymentField } from '../../../../server/plugins/engine/components/PaymentField.js';
33
34
  export { GeospatialField } from '../../../../server/plugins/engine/components/GeospatialField.js';
@@ -30,6 +30,7 @@ export { OsGridRefField } from "./OsGridRefField.js";
30
30
  export { NationalGridFieldNumberField } from "./NationalGridFieldNumberField.js";
31
31
  export { LatLongField } from "./LatLongField.js";
32
32
  export { HiddenField } from "./HiddenField.js";
33
+ export { TrackingField } from "./TrackingField.js";
33
34
  export { PaymentField } from "./PaymentField.js";
34
35
  export { GeospatialField } from "./GeospatialField.js";
35
36
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["AutocompleteField","CheckboxesField","DatePartsField","DeclarationField","Details","EmailAddressField","FileUploadField","Html","InsetText","List","Markdown","NotificationBanner","MonthYearField","MultilineTextField","NumberField","RadiosField","SelectField","TelephoneNumberField","TextField","UkAddressField","YesNoField","EastingNorthingField","OsGridRefField","NationalGridFieldNumberField","LatLongField","HiddenField","PaymentField","GeospatialField"],"sources":["../../../../../src/server/plugins/engine/components/index.ts"],"sourcesContent":["/**\n * IMPORTANT: Exported Components must follow the naming convention implemented in {@link @defra/forms-model#ComponentType}\n * In the Form JSON, components have a type property which is the name of the components, e.g. DatePartsField.\n * Components are loaded in the ComponentsCollection constructor.\n */\n\nexport { AutocompleteField } from '~/src/server/plugins/engine/components/AutocompleteField.js'\nexport { CheckboxesField } from '~/src/server/plugins/engine/components/CheckboxesField.js'\nexport { DatePartsField } from '~/src/server/plugins/engine/components/DatePartsField.js'\nexport { DeclarationField } from '~/src/server/plugins/engine/components/DeclarationField.js'\nexport { Details } from '~/src/server/plugins/engine/components/Details.js'\nexport { EmailAddressField } from '~/src/server/plugins/engine/components/EmailAddressField.js'\nexport { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'\nexport { Html } from '~/src/server/plugins/engine/components/Html.js'\nexport { InsetText } from '~/src/server/plugins/engine/components/InsetText.js'\nexport { List } from '~/src/server/plugins/engine/components/List.js'\nexport { Markdown } from '~/src/server/plugins/engine/components/Markdown.js'\nexport { NotificationBanner } from '~/src/server/plugins/engine/components/NotificationBanner.js'\nexport { MonthYearField } from '~/src/server/plugins/engine/components/MonthYearField.js'\nexport { MultilineTextField } from '~/src/server/plugins/engine/components/MultilineTextField.js'\nexport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nexport { RadiosField } from '~/src/server/plugins/engine/components/RadiosField.js'\nexport { SelectField } from '~/src/server/plugins/engine/components/SelectField.js'\nexport { TelephoneNumberField } from '~/src/server/plugins/engine/components/TelephoneNumberField.js'\nexport { TextField } from '~/src/server/plugins/engine/components/TextField.js'\nexport { UkAddressField } from '~/src/server/plugins/engine/components/UkAddressField.js'\nexport { YesNoField } from '~/src/server/plugins/engine/components/YesNoField.js'\nexport { EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nexport { OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'\nexport { NationalGridFieldNumberField } from '~/src/server/plugins/engine/components/NationalGridFieldNumberField.js'\nexport { LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'\nexport { HiddenField } from '~/src/server/plugins/engine/components/HiddenField.js'\nexport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nexport { GeospatialField } from '~/src/server/plugins/engine/components/GeospatialField.js'\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA,SAASA,iBAAiB;AAC1B,SAASC,eAAe;AACxB,SAASC,cAAc;AACvB,SAASC,gBAAgB;AACzB,SAASC,OAAO;AAChB,SAASC,iBAAiB;AAC1B,SAASC,eAAe;AACxB,SAASC,IAAI;AACb,SAASC,SAAS;AAClB,SAASC,IAAI;AACb,SAASC,QAAQ;AACjB,SAASC,kBAAkB;AAC3B,SAASC,cAAc;AACvB,SAASC,kBAAkB;AAC3B,SAASC,WAAW;AACpB,SAASC,WAAW;AACpB,SAASC,WAAW;AACpB,SAASC,oBAAoB;AAC7B,SAASC,SAAS;AAClB,SAASC,cAAc;AACvB,SAASC,UAAU;AACnB,SAASC,oBAAoB;AAC7B,SAASC,cAAc;AACvB,SAASC,4BAA4B;AACrC,SAASC,YAAY;AACrB,SAASC,WAAW;AACpB,SAASC,YAAY;AACrB,SAASC,eAAe","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["AutocompleteField","CheckboxesField","DatePartsField","DeclarationField","Details","EmailAddressField","FileUploadField","Html","InsetText","List","Markdown","NotificationBanner","MonthYearField","MultilineTextField","NumberField","RadiosField","SelectField","TelephoneNumberField","TextField","UkAddressField","YesNoField","EastingNorthingField","OsGridRefField","NationalGridFieldNumberField","LatLongField","HiddenField","TrackingField","PaymentField","GeospatialField"],"sources":["../../../../../src/server/plugins/engine/components/index.ts"],"sourcesContent":["/**\n * IMPORTANT: Exported Components must follow the naming convention implemented in {@link @defra/forms-model#ComponentType}\n * In the Form JSON, components have a type property which is the name of the components, e.g. DatePartsField.\n * Components are loaded in the ComponentsCollection constructor.\n */\n\nexport { AutocompleteField } from '~/src/server/plugins/engine/components/AutocompleteField.js'\nexport { CheckboxesField } from '~/src/server/plugins/engine/components/CheckboxesField.js'\nexport { DatePartsField } from '~/src/server/plugins/engine/components/DatePartsField.js'\nexport { DeclarationField } from '~/src/server/plugins/engine/components/DeclarationField.js'\nexport { Details } from '~/src/server/plugins/engine/components/Details.js'\nexport { EmailAddressField } from '~/src/server/plugins/engine/components/EmailAddressField.js'\nexport { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'\nexport { Html } from '~/src/server/plugins/engine/components/Html.js'\nexport { InsetText } from '~/src/server/plugins/engine/components/InsetText.js'\nexport { List } from '~/src/server/plugins/engine/components/List.js'\nexport { Markdown } from '~/src/server/plugins/engine/components/Markdown.js'\nexport { NotificationBanner } from '~/src/server/plugins/engine/components/NotificationBanner.js'\nexport { MonthYearField } from '~/src/server/plugins/engine/components/MonthYearField.js'\nexport { MultilineTextField } from '~/src/server/plugins/engine/components/MultilineTextField.js'\nexport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nexport { RadiosField } from '~/src/server/plugins/engine/components/RadiosField.js'\nexport { SelectField } from '~/src/server/plugins/engine/components/SelectField.js'\nexport { TelephoneNumberField } from '~/src/server/plugins/engine/components/TelephoneNumberField.js'\nexport { TextField } from '~/src/server/plugins/engine/components/TextField.js'\nexport { UkAddressField } from '~/src/server/plugins/engine/components/UkAddressField.js'\nexport { YesNoField } from '~/src/server/plugins/engine/components/YesNoField.js'\nexport { EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nexport { OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'\nexport { NationalGridFieldNumberField } from '~/src/server/plugins/engine/components/NationalGridFieldNumberField.js'\nexport { LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'\nexport { HiddenField } from '~/src/server/plugins/engine/components/HiddenField.js'\nexport { TrackingField } from '~/src/server/plugins/engine/components/TrackingField.js'\nexport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nexport { GeospatialField } from '~/src/server/plugins/engine/components/GeospatialField.js'\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA,SAASA,iBAAiB;AAC1B,SAASC,eAAe;AACxB,SAASC,cAAc;AACvB,SAASC,gBAAgB;AACzB,SAASC,OAAO;AAChB,SAASC,iBAAiB;AAC1B,SAASC,eAAe;AACxB,SAASC,IAAI;AACb,SAASC,SAAS;AAClB,SAASC,IAAI;AACb,SAASC,QAAQ;AACjB,SAASC,kBAAkB;AAC3B,SAASC,cAAc;AACvB,SAASC,kBAAkB;AAC3B,SAASC,WAAW;AACpB,SAASC,WAAW;AACpB,SAASC,WAAW;AACpB,SAASC,oBAAoB;AAC7B,SAASC,SAAS;AAClB,SAASC,cAAc;AACvB,SAASC,UAAU;AACnB,SAASC,oBAAoB;AAC7B,SAASC,cAAc;AACvB,SAASC,4BAA4B;AACrC,SAASC,YAAY;AACrB,SAASC,WAAW;AACpB,SAASC,aAAa;AACtB,SAASC,YAAY;AACrB,SAASC,eAAe","ignoreList":[]}
@@ -70,6 +70,7 @@ export declare function getPluginOptions(server: Server): {
70
70
  getLanguage?: import("../../../server/plugins/engine/types.js").PluginOptions["getLanguage"];
71
71
  baseUrl: string;
72
72
  services: import("../../../server/plugins/engine/types.js").PluginOptions["services"];
73
+ generateReferenceNumber: import("../../../server/plugins/engine/types.js").GenerateReferenceNumberHandler;
73
74
  };
74
75
  /**
75
76
  * Handles logging and issuing a permanent redirect for legacy routes.
@@ -21,7 +21,8 @@ const pluginRegistrationOptionsSchema = Joi.object({
21
21
  saveAndExit: Joi.function().optional(),
22
22
  ordnanceSurveyApiKey: Joi.string().optional(),
23
23
  ordnanceSurveyApiSecret: Joi.string().optional(),
24
- getLanguage: Joi.function().optional()
24
+ getLanguage: Joi.function().optional(),
25
+ generateReferenceNumber: Joi.function().optional()
25
26
  });
26
27
 
27
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","names":["getErrorMessage","Joi","logger","CacheService","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cache","alternatives","try","instance","globals","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","preparePageEventRequestOptions","onRequest","baseUrl","uri","saveAndExit","ordnanceSurveyApiKey","ordnanceSurveyApiSecret","getLanguage","validatePluginOptions","options","result","validate","abortEarly","error","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import { getErrorMessage } from '@defra/forms-model'\nimport Joi from 'joi'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nconst pluginRegistrationOptionsSchema = Joi.object({\n model: Joi.object().optional(),\n services: Joi.object().optional(),\n controllers: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n cache: Joi.alternatives().try(\n Joi.object().instance(CacheService),\n Joi.string()\n ),\n globals: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n filters: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n pluginPath: Joi.string().optional(),\n nunjucks: Joi.object({\n baseLayoutPath: Joi.string().required(),\n paths: Joi.array().items(Joi.string()).required()\n }).required(),\n viewContext: Joi.function().required(),\n preparePageEventRequestOptions: Joi.function().optional(),\n onRequest: Joi.function().optional(),\n baseUrl: Joi.string().uri().required(),\n saveAndExit: Joi.function().optional(),\n ordnanceSurveyApiKey: Joi.string().optional(),\n ordnanceSurveyApiSecret: Joi.string().optional(),\n getLanguage: Joi.function().optional()\n})\n\n/**\n * Validates the plugin options against the schema and returns the validated value.\n * @param {PluginOptions} options\n * @returns {PluginOptions}\n */\nexport function validatePluginOptions(options) {\n const result = pluginRegistrationOptionsSchema.validate(options, {\n abortEarly: false\n })\n\n if (result.error) {\n logger.error(\n result.error,\n `Missing required properties in plugin options: ${getErrorMessage(result.error)}`\n )\n throw new Error('Invalid plugin options', result.error)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return result.value\n}\n\n/**\n * @import { PluginOptions } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"options.js","names":["getErrorMessage","Joi","logger","CacheService","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cache","alternatives","try","instance","globals","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","preparePageEventRequestOptions","onRequest","baseUrl","uri","saveAndExit","ordnanceSurveyApiKey","ordnanceSurveyApiSecret","getLanguage","generateReferenceNumber","validatePluginOptions","options","result","validate","abortEarly","error","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import { getErrorMessage } from '@defra/forms-model'\nimport Joi from 'joi'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nconst pluginRegistrationOptionsSchema = Joi.object({\n model: Joi.object().optional(),\n services: Joi.object().optional(),\n controllers: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n cache: Joi.alternatives().try(\n Joi.object().instance(CacheService),\n Joi.string()\n ),\n globals: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n filters: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n pluginPath: Joi.string().optional(),\n nunjucks: Joi.object({\n baseLayoutPath: Joi.string().required(),\n paths: Joi.array().items(Joi.string()).required()\n }).required(),\n viewContext: Joi.function().required(),\n preparePageEventRequestOptions: Joi.function().optional(),\n onRequest: Joi.function().optional(),\n baseUrl: Joi.string().uri().required(),\n saveAndExit: Joi.function().optional(),\n ordnanceSurveyApiKey: Joi.string().optional(),\n ordnanceSurveyApiSecret: Joi.string().optional(),\n getLanguage: Joi.function().optional(),\n generateReferenceNumber: Joi.function().optional()\n})\n\n/**\n * Validates the plugin options against the schema and returns the validated value.\n * @param {PluginOptions} options\n * @returns {PluginOptions}\n */\nexport function validatePluginOptions(options) {\n const result = pluginRegistrationOptionsSchema.validate(options, {\n abortEarly: false\n })\n\n if (result.error) {\n logger.error(\n result.error,\n `Missing required properties in plugin options: ${getErrorMessage(result.error)}`\n )\n throw new Error('Invalid plugin options', result.error)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return result.value\n}\n\n/**\n * @import { PluginOptions } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,5 @@
1
1
  import { validatePluginOptions } from "./options.js";
2
+ import { generateUniqueReference } from "./referenceNumbers.js";
2
3
  import { getRoutes as getFileUploadStatusRoutes } from "./routes/file-upload.js";
3
4
  import { makeLoadFormPreHandler } from "./routes/index.js";
4
5
  import { getRoutes as getPaymentRoutes } from "./routes/payment.js";
@@ -21,6 +22,7 @@ export const plugin = {
21
22
  cache,
22
23
  saveAndExit,
23
24
  getLanguage,
25
+ generateReferenceNumber,
24
26
  nunjucks: nunjucksOptions,
25
27
  viewContext,
26
28
  preparePageEventRequestOptions,
@@ -61,6 +63,7 @@ export const plugin = {
61
63
  server.expose('cacheService', cacheService);
62
64
  server.expose('saveAndExit', saveAndExit);
63
65
  server.expose('getLanguage', getLanguage);
66
+ server.expose('generateReferenceNumber', generateReferenceNumber ?? generateUniqueReference);
64
67
  server.expose('baseUrl', baseUrl);
65
68
  server.expose('services', services);
66
69
  server.app.model = model;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["validatePluginOptions","getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getPaymentRoutes","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerUnavailableResponse","registerVision","mapPlugin","postcodeLookupPlugin","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cache","saveAndExit","getLanguage","nunjucks","nunjucksOptions","viewContext","preparePageEventRequestOptions","onRequest","ordnanceSurveyApiKey","baseUrl","ordnanceSurveyApiSecret","services","cacheService","cacheName","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getPaymentRoutes } from '~/src/server/plugins/engine/routes/payment.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerUnavailableResponse } from '~/src/server/plugins/engine/unavailable-response.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport { mapPlugin } from '~/src/server/plugins/map/index.js'\nimport { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n options = validatePluginOptions(options)\n\n const {\n model,\n cache,\n saveAndExit,\n getLanguage,\n nunjucks: nunjucksOptions,\n viewContext,\n preparePageEventRequestOptions,\n onRequest,\n ordnanceSurveyApiKey,\n baseUrl,\n ordnanceSurveyApiSecret,\n services\n } = options\n\n const cacheService =\n typeof cache === 'string'\n ? new CacheService({ server, cacheName: cache })\n : cache\n\n await registerVision(server, options)\n\n // Register the postcode lookup plugin only if we have an OS api key\n if (ordnanceSurveyApiKey) {\n await server.register({\n plugin: postcodeLookupPlugin,\n options: {\n ordnanceSurveyApiKey\n }\n })\n }\n\n // Register the maps plugin only if we have an OS api key & secret\n if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {\n await server.register({\n plugin: mapPlugin,\n options: {\n ordnanceSurveyApiKey,\n ordnanceSurveyApiSecret\n }\n })\n }\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n server.expose('saveAndExit', saveAndExit)\n server.expose('getLanguage', getLanguage)\n server.expose('baseUrl', baseUrl)\n server.expose('services', services)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getPaymentRoutes(),\n ...getFileUploadStatusRoutes(),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions, onRequest),\n ...getRepeaterItemDeleteRoutes(\n getRouteOptions,\n postRouteOptions,\n onRequest\n ),\n\n ...getQuestionRoutes(\n getRouteOptions,\n postRouteOptions,\n preparePageEventRequestOptions,\n onRequest\n )\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n\n // Registration order is important: must be registered after the engine's\n // routes so it sees their responses, but before any global error-page\n // handler that would re-shape Boom errors.\n registerUnavailableResponse(server)\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"plugin.js","names":["validatePluginOptions","generateUniqueReference","getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getPaymentRoutes","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerUnavailableResponse","registerVision","mapPlugin","postcodeLookupPlugin","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cache","saveAndExit","getLanguage","generateReferenceNumber","nunjucks","nunjucksOptions","viewContext","preparePageEventRequestOptions","onRequest","ordnanceSurveyApiKey","baseUrl","ordnanceSurveyApiSecret","services","cacheService","cacheName","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\nimport { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getPaymentRoutes } from '~/src/server/plugins/engine/routes/payment.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerUnavailableResponse } from '~/src/server/plugins/engine/unavailable-response.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport { mapPlugin } from '~/src/server/plugins/map/index.js'\nimport { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n options = validatePluginOptions(options)\n\n const {\n model,\n cache,\n saveAndExit,\n getLanguage,\n generateReferenceNumber,\n nunjucks: nunjucksOptions,\n viewContext,\n preparePageEventRequestOptions,\n onRequest,\n ordnanceSurveyApiKey,\n baseUrl,\n ordnanceSurveyApiSecret,\n services\n } = options\n\n const cacheService =\n typeof cache === 'string'\n ? new CacheService({ server, cacheName: cache })\n : cache\n\n await registerVision(server, options)\n\n // Register the postcode lookup plugin only if we have an OS api key\n if (ordnanceSurveyApiKey) {\n await server.register({\n plugin: postcodeLookupPlugin,\n options: {\n ordnanceSurveyApiKey\n }\n })\n }\n\n // Register the maps plugin only if we have an OS api key & secret\n if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {\n await server.register({\n plugin: mapPlugin,\n options: {\n ordnanceSurveyApiKey,\n ordnanceSurveyApiSecret\n }\n })\n }\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n server.expose('saveAndExit', saveAndExit)\n server.expose('getLanguage', getLanguage)\n server.expose(\n 'generateReferenceNumber',\n generateReferenceNumber ?? generateUniqueReference\n )\n server.expose('baseUrl', baseUrl)\n server.expose('services', services)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getPaymentRoutes(),\n ...getFileUploadStatusRoutes(),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions, onRequest),\n ...getRepeaterItemDeleteRoutes(\n getRouteOptions,\n postRouteOptions,\n onRequest\n ),\n\n ...getQuestionRoutes(\n getRouteOptions,\n postRouteOptions,\n preparePageEventRequestOptions,\n onRequest\n )\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n\n // Registration order is important: must be registered after the engine's\n // routes so it sees their responses, but before any global error-page\n // handler that would re-shape Boom errors.\n registerUnavailableResponse(server)\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"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","ignoreList":[]}
@@ -1,3 +1,4 @@
1
+ export declare const DEFAULT_REFERENCE_NUMBER = "XXX-XXX-XXX";
1
2
  /**
2
3
  * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.
3
4
  * @param strCodes - array of binary input values
@@ -1,5 +1,6 @@
1
1
  import { randomBytes } from 'node:crypto';
2
2
  import { RegExpMatcher, englishDataset, englishRecommendedTransformers } from 'obscenity';
3
+ export const DEFAULT_REFERENCE_NUMBER = 'XXX-XXX-XXX';
3
4
 
4
5
  /**
5
6
  * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.
@@ -1 +1 @@
1
- {"version":3,"file":"referenceNumbers.js","names":["randomBytes","RegExpMatcher","englishDataset","englishRecommendedTransformers","convertToDecAlpha","strCodes","validChars","strLen","length","outArray","forEach","code","pos","push","charAt","join","generateUniqueReference","prefix","segmentLength","segmentCount","profanityMatcher","build","referenceNumber","segments","Array","from","slice","toUpperCase","hasMatch","replaceAll"],"sources":["../../../../src/server/plugins/engine/referenceNumbers.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\n\nimport {\n RegExpMatcher,\n englishDataset,\n englishRecommendedTransformers\n} from 'obscenity'\n\n/**\n * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.\n * @param strCodes - array of binary input values\n */\nexport function convertToDecAlpha(strCodes: number[]) {\n const validChars = 'ABCDEFHJKLMNPRSTUVWXYZ23456789'\n const strLen = validChars.length\n const outArray = [] as string[]\n\n strCodes.forEach((code) => {\n const pos = (code / 256) * strLen\n outArray.push(validChars.charAt(pos))\n })\n\n return outArray.join('')\n}\n\n/**\n * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.\n * Provides no guarantee on uniqueness.\n * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed\n * (see https://gunkies.org/wiki/DEC_alphabet )\n */\nexport function generateUniqueReference(prefix?: string) {\n const segmentLength = 3\n const segmentCount = prefix ? 2 : 3\n prefix = prefix ? `${prefix}-` : ''\n\n const profanityMatcher = new RegExpMatcher({\n ...englishDataset.build(),\n ...englishRecommendedTransformers\n })\n\n let referenceNumber\n\n do {\n const segments = Array.from({ length: segmentCount }, () =>\n convertToDecAlpha([...randomBytes(segmentLength)]).slice(\n 0,\n segmentLength * 2\n )\n )\n\n referenceNumber = `${prefix}${segments.join('-')}`.toUpperCase()\n } while (profanityMatcher.hasMatch(referenceNumber.replaceAll('-', '')))\n\n return referenceNumber\n}\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,aAAa;AAEzC,SACEC,aAAa,EACbC,cAAc,EACdC,8BAA8B,QACzB,WAAW;;AAElB;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAACC,QAAkB,EAAE;EACpD,MAAMC,UAAU,GAAG,gCAAgC;EACnD,MAAMC,MAAM,GAAGD,UAAU,CAACE,MAAM;EAChC,MAAMC,QAAQ,GAAG,EAAc;EAE/BJ,QAAQ,CAACK,OAAO,CAAEC,IAAI,IAAK;IACzB,MAAMC,GAAG,GAAID,IAAI,GAAG,GAAG,GAAIJ,MAAM;IACjCE,QAAQ,CAACI,IAAI,CAACP,UAAU,CAACQ,MAAM,CAACF,GAAG,CAAC,CAAC;EACvC,CAAC,CAAC;EAEF,OAAOH,QAAQ,CAACM,IAAI,CAAC,EAAE,CAAC;AAC1B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CAACC,MAAe,EAAE;EACvD,MAAMC,aAAa,GAAG,CAAC;EACvB,MAAMC,YAAY,GAAGF,MAAM,GAAG,CAAC,GAAG,CAAC;EACnCA,MAAM,GAAGA,MAAM,GAAG,GAAGA,MAAM,GAAG,GAAG,EAAE;EAEnC,MAAMG,gBAAgB,GAAG,IAAInB,aAAa,CAAC;IACzC,GAAGC,cAAc,CAACmB,KAAK,CAAC,CAAC;IACzB,GAAGlB;EACL,CAAC,CAAC;EAEF,IAAImB,eAAe;EAEnB,GAAG;IACD,MAAMC,QAAQ,GAAGC,KAAK,CAACC,IAAI,CAAC;MAAEjB,MAAM,EAAEW;IAAa,CAAC,EAAE,MACpDf,iBAAiB,CAAC,CAAC,GAAGJ,WAAW,CAACkB,aAAa,CAAC,CAAC,CAAC,CAACQ,KAAK,CACtD,CAAC,EACDR,aAAa,GAAG,CAClB,CACF,CAAC;IAEDI,eAAe,GAAG,GAAGL,MAAM,GAAGM,QAAQ,CAACR,IAAI,CAAC,GAAG,CAAC,EAAE,CAACY,WAAW,CAAC,CAAC;EAClE,CAAC,QAAQP,gBAAgB,CAACQ,QAAQ,CAACN,eAAe,CAACO,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;EAEvE,OAAOP,eAAe;AACxB","ignoreList":[]}
1
+ {"version":3,"file":"referenceNumbers.js","names":["randomBytes","RegExpMatcher","englishDataset","englishRecommendedTransformers","DEFAULT_REFERENCE_NUMBER","convertToDecAlpha","strCodes","validChars","strLen","length","outArray","forEach","code","pos","push","charAt","join","generateUniqueReference","prefix","segmentLength","segmentCount","profanityMatcher","build","referenceNumber","segments","Array","from","slice","toUpperCase","hasMatch","replaceAll"],"sources":["../../../../src/server/plugins/engine/referenceNumbers.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\n\nimport {\n RegExpMatcher,\n englishDataset,\n englishRecommendedTransformers\n} from 'obscenity'\n\nexport const DEFAULT_REFERENCE_NUMBER = 'XXX-XXX-XXX'\n\n/**\n * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.\n * @param strCodes - array of binary input values\n */\nexport function convertToDecAlpha(strCodes: number[]) {\n const validChars = 'ABCDEFHJKLMNPRSTUVWXYZ23456789'\n const strLen = validChars.length\n const outArray = [] as string[]\n\n strCodes.forEach((code) => {\n const pos = (code / 256) * strLen\n outArray.push(validChars.charAt(pos))\n })\n\n return outArray.join('')\n}\n\n/**\n * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.\n * Provides no guarantee on uniqueness.\n * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed\n * (see https://gunkies.org/wiki/DEC_alphabet )\n */\nexport function generateUniqueReference(prefix?: string) {\n const segmentLength = 3\n const segmentCount = prefix ? 2 : 3\n prefix = prefix ? `${prefix}-` : ''\n\n const profanityMatcher = new RegExpMatcher({\n ...englishDataset.build(),\n ...englishRecommendedTransformers\n })\n\n let referenceNumber\n\n do {\n const segments = Array.from({ length: segmentCount }, () =>\n convertToDecAlpha([...randomBytes(segmentLength)]).slice(\n 0,\n segmentLength * 2\n )\n )\n\n referenceNumber = `${prefix}${segments.join('-')}`.toUpperCase()\n } while (profanityMatcher.hasMatch(referenceNumber.replaceAll('-', '')))\n\n return referenceNumber\n}\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,aAAa;AAEzC,SACEC,aAAa,EACbC,cAAc,EACdC,8BAA8B,QACzB,WAAW;AAElB,OAAO,MAAMC,wBAAwB,GAAG,aAAa;;AAErD;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAACC,QAAkB,EAAE;EACpD,MAAMC,UAAU,GAAG,gCAAgC;EACnD,MAAMC,MAAM,GAAGD,UAAU,CAACE,MAAM;EAChC,MAAMC,QAAQ,GAAG,EAAc;EAE/BJ,QAAQ,CAACK,OAAO,CAAEC,IAAI,IAAK;IACzB,MAAMC,GAAG,GAAID,IAAI,GAAG,GAAG,GAAIJ,MAAM;IACjCE,QAAQ,CAACI,IAAI,CAACP,UAAU,CAACQ,MAAM,CAACF,GAAG,CAAC,CAAC;EACvC,CAAC,CAAC;EAEF,OAAOH,QAAQ,CAACM,IAAI,CAAC,EAAE,CAAC;AAC1B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CAACC,MAAe,EAAE;EACvD,MAAMC,aAAa,GAAG,CAAC;EACvB,MAAMC,YAAY,GAAGF,MAAM,GAAG,CAAC,GAAG,CAAC;EACnCA,MAAM,GAAGA,MAAM,GAAG,GAAGA,MAAM,GAAG,GAAG,EAAE;EAEnC,MAAMG,gBAAgB,GAAG,IAAIpB,aAAa,CAAC;IACzC,GAAGC,cAAc,CAACoB,KAAK,CAAC,CAAC;IACzB,GAAGnB;EACL,CAAC,CAAC;EAEF,IAAIoB,eAAe;EAEnB,GAAG;IACD,MAAMC,QAAQ,GAAGC,KAAK,CAACC,IAAI,CAAC;MAAEjB,MAAM,EAAEW;IAAa,CAAC,EAAE,MACpDf,iBAAiB,CAAC,CAAC,GAAGL,WAAW,CAACmB,aAAa,CAAC,CAAC,CAAC,CAACQ,KAAK,CACtD,CAAC,EACDR,aAAa,GAAG,CAClB,CACF,CAAC;IAEDI,eAAe,GAAG,GAAGL,MAAM,GAAGM,QAAQ,CAACR,IAAI,CAAC,GAAG,CAAC,EAAE,CAACY,WAAW,CAAC,CAAC;EAClE,CAAC,QAAQP,gBAAgB,CAACQ,QAAQ,CAACN,eAAe,CAACO,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;EAEvE,OAAOP,eAAe;AACxB","ignoreList":[]}
@@ -3,15 +3,16 @@ import Boom from '@hapi/boom';
3
3
  import { EXTERNAL_STATE_APPENDAGE, EXTERNAL_STATE_PAYLOAD } from "../../../constants.js";
4
4
  import { FormComponent, isFormState } from "../components/FormComponent.js";
5
5
  import { resolveFormModel } from "../form-context.js";
6
- import { checkFormStatus, findPage, getCacheService, getPage, getStartPath, proceed } from "../helpers.js";
6
+ import { checkFormStatus, findPage, getCacheService, getPage, getPluginOptions, getStartPath, proceed } from "../helpers.js";
7
7
  import { checkSaveAndExitRepeater, copyNotYetValidatedState } from "../pageControllers/helpers/state.js";
8
- import { generateUniqueReference } from "../referenceNumbers.js";
8
+ import { DEFAULT_REFERENCE_NUMBER } from "../referenceNumbers.js";
9
9
  import * as defaultServices from "../services/index.js";
10
10
  import { resolveLanguage } from "../../../utils/utils.js";
11
11
  export async function redirectOrMakeHandler(request, h, onRequest, makeHandler) {
12
12
  const {
13
13
  app,
14
- params
14
+ params,
15
+ query
15
16
  } = request;
16
17
  const {
17
18
  model
@@ -27,7 +28,11 @@ export async function redirectOrMakeHandler(request, h, onRequest, makeHandler)
27
28
  if (typeof prefix !== 'string') {
28
29
  throw Boom.badImplementation('Reference number prefix must be a string or undefined');
29
30
  }
30
- const referenceNumber = generateUniqueReference(prefix);
31
+ const isForce = 'force' in query;
32
+ const {
33
+ generateReferenceNumber
34
+ } = getPluginOptions(request.server);
35
+ const referenceNumber = isForce ? DEFAULT_REFERENCE_NUMBER : await generateReferenceNumber(prefix);
31
36
  state = await page.mergeState(request, state, {
32
37
  $$__referenceNumber: referenceNumber
33
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["isPaymentPage","Boom","EXTERNAL_STATE_APPENDAGE","EXTERNAL_STATE_PAYLOAD","FormComponent","isFormState","resolveFormModel","checkFormStatus","findPage","getCacheService","getPage","getStartPath","proceed","checkSaveAndExitRepeater","copyNotYetValidatedState","generateUniqueReference","defaultServices","resolveLanguage","redirectOrMakeHandler","request","h","onRequest","makeHandler","app","params","model","notFound","path","cacheService","server","page","state","getState","$$__referenceNumber","prefix","def","metadata","referenceNumberPrefix","badImplementation","referenceNumber","mergeState","importExternalComponentState","flash","getFlash","language","translator","createTranslator","context","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","result","continue","resumeInRepeaterUrl","startsWith","pageDef","isForceAccess","redirectTo","next","length","query","returnUrl","getHref","externalComponentData","yar","Array","isArray","typedStateAppendage","componentName","component","stateAppendage","data","componentMap","get","Error","TypeError","isStateValid","isState","componentState","isAppendageStateSingleObject","Object","fromEntries","entries","map","key","value","savedState","payload","stashedPayload","localState","getStateFromValidForm","makeLoadFormPreHandler","options","realm","modifiers","route","services","controllers","ordnanceSurveyApiKey","handler","slug","isPreview","formState","routePrefix","dispatchHandler","servicePath","basePath"],"sources":["../../../../../src/server/plugins/engine/routes/index.ts"],"sourcesContent":["import { isPaymentPage } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseObject,\n type ResponseToolkit,\n type Server\n} from '@hapi/hapi'\n\nimport {\n EXTERNAL_STATE_APPENDAGE,\n EXTERNAL_STATE_PAYLOAD\n} from '~/src/server/constants.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { resolveFormModel } from '~/src/server/plugins/engine/form-context.js'\nimport {\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getStartPath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport {\n checkSaveAndExitRepeater,\n copyNotYetValidatedState\n} from '~/src/server/plugins/engine/pageControllers/helpers/state.js'\nimport { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyFormRequest,\n type ExternalStateAppendage,\n type FormContext,\n type FormPayload,\n type FormSubmissionState,\n type OnRequestCallback,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport async function redirectOrMakeHandler(\n request: AnyFormRequest,\n h: FormResponseToolkit,\n onRequest: OnRequestCallback | undefined,\n makeHandler: (\n page: PageControllerClass,\n context: FormContext\n ) => ResponseObject | Promise<ResponseObject>\n) {\n const { app, params } = request\n const { model } = app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n const cacheService = getCacheService(request.server)\n const page = getPage(model, request)\n let state = await page.getState(request)\n\n if (!state.$$__referenceNumber) {\n const prefix = model.def.metadata?.referenceNumberPrefix ?? ''\n\n if (typeof prefix !== 'string') {\n throw Boom.badImplementation(\n 'Reference number prefix must be a string or undefined'\n )\n }\n\n const referenceNumber = generateUniqueReference(prefix)\n state = await page.mergeState(request, state, {\n $$__referenceNumber: referenceNumber\n })\n }\n\n state = await importExternalComponentState(request, page, state)\n\n const flash = cacheService.getFlash(request)\n const language = resolveLanguage(request)\n const translator = model.createTranslator(language)\n const context = model.getFormContext(\n request,\n state,\n flash?.errors,\n translator\n )\n\n await copyNotYetValidatedState(request, context)\n\n const relevantPath = page.getRelevantPath(request, context)\n const summaryPath = page.getSummaryPath()\n\n // Call the onRequest callback if it has been supplied\n if (onRequest) {\n const result = await onRequest(request, h, context)\n if (result !== h.continue) {\n return result\n }\n }\n\n // Check whether save-and-exit should resume from within a repeater\n const resumeInRepeaterUrl = checkSaveAndExitRepeater(context, model)\n if (resumeInRepeaterUrl) {\n return proceed(request, h, resumeInRepeaterUrl)\n }\n\n // Return handler for relevant pages, payment pages, or preview URL direct access.\n // Payment pages are skipped in the normal page walk but must render when the user\n // is redirected there from CYA \"Pay and submit\".\n if (\n relevantPath.startsWith(page.path) ||\n isPaymentPage(page.pageDef) ||\n context.isForceAccess\n ) {\n return makeHandler(page, context)\n }\n\n // Redirect back to last relevant page\n const redirectTo = findPage(model, relevantPath)\n // Set the return URL unless an exit page\n if (redirectTo?.next.length) {\n request.query.returnUrl = page.getHref(summaryPath)\n }\n\n return proceed(request, h, page.getHref(relevantPath))\n}\n\nasync function importExternalComponentState(\n request: AnyFormRequest,\n page: PageControllerClass,\n state: FormSubmissionState\n): Promise<FormSubmissionState> {\n const externalComponentData = request.yar.flash(EXTERNAL_STATE_APPENDAGE)\n\n if (Array.isArray(externalComponentData)) {\n return state\n }\n\n const typedStateAppendage = externalComponentData as ExternalStateAppendage\n const componentName = typedStateAppendage.component\n const stateAppendage = typedStateAppendage.data\n\n const component = request.app.model?.componentMap.get(componentName)\n\n if (!component) {\n throw new Error(`Component ${componentName} not found in form`)\n }\n\n if (!(component instanceof FormComponent)) {\n throw new TypeError(\n `Component ${componentName} is not a FormComponent and does not support isState`\n )\n }\n\n const isStateValid = component.isState(stateAppendage)\n\n if (!isStateValid) {\n throw new Error(`State for component ${componentName} is invalid`)\n }\n\n // Create state structure from appendage state\n // Some components use a record structure with properties of the format of '<compName>__<fieldName>'\n // e.g. UKAddressField\n // Some components use a single object structure e.g. PaymentField\n const componentState =\n isFormState(stateAppendage) && !component.isAppendageStateSingleObject\n ? Object.fromEntries(\n Object.entries(stateAppendage).map(([key, value]) => [\n `${componentName}__${key}`,\n value\n ])\n )\n : { [componentName]: stateAppendage }\n\n // Save the external component state directly (already has correct key format)\n const savedState = await page.mergeState(request, state, componentState)\n\n // Merge any stashed payload into the local state\n const payload = request.yar.flash(EXTERNAL_STATE_PAYLOAD)\n const stashedPayload = Array.isArray(payload) ? {} : (payload as FormPayload)\n\n const localState = page.getStateFromValidForm(request, savedState, {\n ...stashedPayload,\n ...componentState\n } as FormPayload)\n\n return { ...savedState, ...localState }\n}\n\nexport function makeLoadFormPreHandler(server: Server, options: PluginOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- hapi types are wrong\n const prefix = server.realm.modifiers.route.prefix ?? ''\n\n const {\n services = defaultServices,\n controllers,\n ordnanceSurveyApiKey\n } = options\n\n async function handler(request: AnyFormRequest, h: ResponseToolkit) {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(params)\n\n const model = await resolveFormModel(server, slug, formState, {\n services,\n controllers,\n ordnanceSurveyApiKey,\n routePrefix: prefix,\n isPreview\n })\n\n request.app.model = model\n\n return h.continue\n }\n\n return handler\n}\n\nexport function dispatchHandler(request: FormRequest, h: FormResponseToolkit) {\n const { model } = request.app\n\n const servicePath = model ? `/${model.basePath}` : ''\n return proceed(request, h, `${servicePath}${getStartPath(model)}`)\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["isPaymentPage","Boom","EXTERNAL_STATE_APPENDAGE","EXTERNAL_STATE_PAYLOAD","FormComponent","isFormState","resolveFormModel","checkFormStatus","findPage","getCacheService","getPage","getPluginOptions","getStartPath","proceed","checkSaveAndExitRepeater","copyNotYetValidatedState","DEFAULT_REFERENCE_NUMBER","defaultServices","resolveLanguage","redirectOrMakeHandler","request","h","onRequest","makeHandler","app","params","query","model","notFound","path","cacheService","server","page","state","getState","$$__referenceNumber","prefix","def","metadata","referenceNumberPrefix","badImplementation","isForce","generateReferenceNumber","referenceNumber","mergeState","importExternalComponentState","flash","getFlash","language","translator","createTranslator","context","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","result","continue","resumeInRepeaterUrl","startsWith","pageDef","isForceAccess","redirectTo","next","length","returnUrl","getHref","externalComponentData","yar","Array","isArray","typedStateAppendage","componentName","component","stateAppendage","data","componentMap","get","Error","TypeError","isStateValid","isState","componentState","isAppendageStateSingleObject","Object","fromEntries","entries","map","key","value","savedState","payload","stashedPayload","localState","getStateFromValidForm","makeLoadFormPreHandler","options","realm","modifiers","route","services","controllers","ordnanceSurveyApiKey","handler","slug","isPreview","formState","routePrefix","dispatchHandler","servicePath","basePath"],"sources":["../../../../../src/server/plugins/engine/routes/index.ts"],"sourcesContent":["import { isPaymentPage } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseObject,\n type ResponseToolkit,\n type Server\n} from '@hapi/hapi'\n\nimport {\n EXTERNAL_STATE_APPENDAGE,\n EXTERNAL_STATE_PAYLOAD\n} from '~/src/server/constants.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { resolveFormModel } from '~/src/server/plugins/engine/form-context.js'\nimport {\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getPluginOptions,\n getStartPath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport {\n checkSaveAndExitRepeater,\n copyNotYetValidatedState\n} from '~/src/server/plugins/engine/pageControllers/helpers/state.js'\nimport { DEFAULT_REFERENCE_NUMBER } from '~/src/server/plugins/engine/referenceNumbers.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyFormRequest,\n type ExternalStateAppendage,\n type FormContext,\n type FormPayload,\n type FormSubmissionState,\n type OnRequestCallback,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport async function redirectOrMakeHandler(\n request: AnyFormRequest,\n h: FormResponseToolkit,\n onRequest: OnRequestCallback | undefined,\n makeHandler: (\n page: PageControllerClass,\n context: FormContext\n ) => ResponseObject | Promise<ResponseObject>\n) {\n const { app, params, query } = request\n const { model } = app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n const cacheService = getCacheService(request.server)\n const page = getPage(model, request)\n let state = await page.getState(request)\n\n if (!state.$$__referenceNumber) {\n const prefix = model.def.metadata?.referenceNumberPrefix ?? ''\n\n if (typeof prefix !== 'string') {\n throw Boom.badImplementation(\n 'Reference number prefix must be a string or undefined'\n )\n }\n\n const isForce = 'force' in query\n const { generateReferenceNumber } = getPluginOptions(request.server)\n const referenceNumber = isForce\n ? DEFAULT_REFERENCE_NUMBER\n : await generateReferenceNumber(prefix)\n\n state = await page.mergeState(request, state, {\n $$__referenceNumber: referenceNumber\n })\n }\n\n state = await importExternalComponentState(request, page, state)\n\n const flash = cacheService.getFlash(request)\n const language = resolveLanguage(request)\n const translator = model.createTranslator(language)\n const context = model.getFormContext(\n request,\n state,\n flash?.errors,\n translator\n )\n\n await copyNotYetValidatedState(request, context)\n\n const relevantPath = page.getRelevantPath(request, context)\n const summaryPath = page.getSummaryPath()\n\n // Call the onRequest callback if it has been supplied\n if (onRequest) {\n const result = await onRequest(request, h, context)\n if (result !== h.continue) {\n return result\n }\n }\n\n // Check whether save-and-exit should resume from within a repeater\n const resumeInRepeaterUrl = checkSaveAndExitRepeater(context, model)\n if (resumeInRepeaterUrl) {\n return proceed(request, h, resumeInRepeaterUrl)\n }\n\n // Return handler for relevant pages, payment pages, or preview URL direct access.\n // Payment pages are skipped in the normal page walk but must render when the user\n // is redirected there from CYA \"Pay and submit\".\n if (\n relevantPath.startsWith(page.path) ||\n isPaymentPage(page.pageDef) ||\n context.isForceAccess\n ) {\n return makeHandler(page, context)\n }\n\n // Redirect back to last relevant page\n const redirectTo = findPage(model, relevantPath)\n // Set the return URL unless an exit page\n if (redirectTo?.next.length) {\n request.query.returnUrl = page.getHref(summaryPath)\n }\n\n return proceed(request, h, page.getHref(relevantPath))\n}\n\nasync function importExternalComponentState(\n request: AnyFormRequest,\n page: PageControllerClass,\n state: FormSubmissionState\n): Promise<FormSubmissionState> {\n const externalComponentData = request.yar.flash(EXTERNAL_STATE_APPENDAGE)\n\n if (Array.isArray(externalComponentData)) {\n return state\n }\n\n const typedStateAppendage = externalComponentData as ExternalStateAppendage\n const componentName = typedStateAppendage.component\n const stateAppendage = typedStateAppendage.data\n\n const component = request.app.model?.componentMap.get(componentName)\n\n if (!component) {\n throw new Error(`Component ${componentName} not found in form`)\n }\n\n if (!(component instanceof FormComponent)) {\n throw new TypeError(\n `Component ${componentName} is not a FormComponent and does not support isState`\n )\n }\n\n const isStateValid = component.isState(stateAppendage)\n\n if (!isStateValid) {\n throw new Error(`State for component ${componentName} is invalid`)\n }\n\n // Create state structure from appendage state\n // Some components use a record structure with properties of the format of '<compName>__<fieldName>'\n // e.g. UKAddressField\n // Some components use a single object structure e.g. PaymentField\n const componentState =\n isFormState(stateAppendage) && !component.isAppendageStateSingleObject\n ? Object.fromEntries(\n Object.entries(stateAppendage).map(([key, value]) => [\n `${componentName}__${key}`,\n value\n ])\n )\n : { [componentName]: stateAppendage }\n\n // Save the external component state directly (already has correct key format)\n const savedState = await page.mergeState(request, state, componentState)\n\n // Merge any stashed payload into the local state\n const payload = request.yar.flash(EXTERNAL_STATE_PAYLOAD)\n const stashedPayload = Array.isArray(payload) ? {} : (payload as FormPayload)\n\n const localState = page.getStateFromValidForm(request, savedState, {\n ...stashedPayload,\n ...componentState\n } as FormPayload)\n\n return { ...savedState, ...localState }\n}\n\nexport function makeLoadFormPreHandler(server: Server, options: PluginOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- hapi types are wrong\n const prefix = server.realm.modifiers.route.prefix ?? ''\n\n const {\n services = defaultServices,\n controllers,\n ordnanceSurveyApiKey\n } = options\n\n async function handler(request: AnyFormRequest, h: ResponseToolkit) {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(params)\n\n const model = await resolveFormModel(server, slug, formState, {\n services,\n controllers,\n ordnanceSurveyApiKey,\n routePrefix: prefix,\n isPreview\n })\n\n request.app.model = model\n\n return h.continue\n }\n\n return handler\n}\n\nexport function dispatchHandler(request: FormRequest, h: FormResponseToolkit) {\n const { model } = request.app\n\n const servicePath = model ? `/${model.basePath}` : ''\n return proceed(request, h, `${servicePath}${getStartPath(model)}`)\n}\n"],"mappings":"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","ignoreList":[]}
@@ -353,6 +353,7 @@ export type PreparePageEventRequestOptions = (options: RequestOptions, event: Ev
353
353
  export type OnRequestCallback = (request: AnyFormRequest, h: FormResponseToolkit, context: FormContext) => ResponseObject | FormResponseToolkit['continue'] | Promise<ResponseObject | FormResponseToolkit['continue']>;
354
354
  export type SaveAndExitHandler = (request: FormRequestPayload, h: FormResponseToolkit, context: FormContext) => ResponseObject;
355
355
  export type GetLanguageHandler = (query?: RequestQuery, yar?: Yar) => string;
356
+ export type GenerateReferenceNumberHandler = (prefix?: string) => Promise<string>;
356
357
  export interface ExternalArgs {
357
358
  component: ComponentDef;
358
359
  controller: QuestionPageController;
@@ -391,6 +392,7 @@ export interface PluginOptions {
391
392
  preparePageEventRequestOptions?: PreparePageEventRequestOptions;
392
393
  onRequest?: OnRequestCallback;
393
394
  getLanguage?: GetLanguageHandler;
395
+ generateReferenceNumber?: GenerateReferenceNumberHandler;
394
396
  baseUrl: string;
395
397
  ordnanceSurveyApiKey?: string;
396
398
  ordnanceSurveyApiSecret?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type PaymentFieldComponent,\n type SubmitConditionEvaluation,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type RequestQuery,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type Yar } from '@hapi/yar'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type FileUploadField,\n type PaymentField\n} from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\nexport interface CacheRequest {\n yar: { id: string | undefined }\n params: { slug?: string; state?: string }\n}\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError extends Pick<\n ValidationErrorItem,\n 'context' | 'path'\n> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | GeospatialState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n languages: { name: string; code: string }[]\n translator: Translator | undefined\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorCode?: string\n errorMessage?: string\n errorParams?: Record<string, number | string | string[]>\n }\n | {\n fileStatus: FileStatus.complete\n errorCode?: string\n errorMessage?: undefined\n errorParams?: Record<string, number | string | string[]>\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\n/**\n * A longitude/latitude coordinate pair in WGS84 format\n * Format: [longitude, latitude]\n */\nexport type Coordinates = [longitude: number, latitude: number]\n\n/**\n * GeoJSON Point geometry\n */\nexport interface PointGeometry {\n type: 'Point'\n coordinates: Coordinates\n}\n\n/**\n * GeoJSON LineString geometry\n */\nexport interface LineStringGeometry {\n type: 'LineString'\n coordinates: Coordinates[]\n}\n\n/**\n * GeoJSON Polygon geometry\n */\nexport interface PolygonGeometry {\n type: 'Polygon'\n coordinates: Coordinates[][]\n}\n\n/**\n * Supported geometry types\n */\nexport type Geometry = PointGeometry | LineStringGeometry | PolygonGeometry\n\n/**\n * Feature metadata\n */\nexport interface FeatureProperties {\n /**\n * Human-readable description of the feature\n */\n description: string\n /**\n * The OS grid reference of the first coordinate of the feature\n */\n coordinateGridReference?: string\n /**\n * The OS grid reference of the centroid of the feature\n */\n centroidGridReference?: string\n}\n\n/**\n * A single GeoJSON Feature\n */\nexport interface Feature {\n id: string\n type: 'Feature'\n properties: FeatureProperties\n geometry: Geometry\n}\n\n/**\n * A GeoJSON FeatureCollection\n */\nexport type FeatureCollection = Feature[]\n\nexport type GeospatialState = FeatureCollection\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n t?: (key: string, opts?: Record<string, unknown>) => string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n showSubmitButton?: boolean\n showPaymentExpiredNotification?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport type GetLanguageHandler = (query?: RequestQuery, yar?: Yar) => string\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs?: Record<string, string>\n isLive: boolean\n isPreview: boolean\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface PaymentExternalArgs extends ExternalArgs {\n component: PaymentFieldComponent\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n getLanguage?: GetLanguageHandler\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: (typeof FormAdapterSubmissionSchemaVersion)[keyof typeof FormAdapterSubmissionSchemaVersion]\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n language?: string\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterPayment {\n paymentId: string\n reference: string\n amount: number\n description: string\n descriptionInEng?: string\n createdAt: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\n/**\n * A detail item specifically for payments\n */\nexport type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {\n field: PaymentField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n | GeospatialState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n payment?: FormAdapterPayment\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n\n /**\n * The outcome of every condition in the form definition, evaluated against\n * the final answers at the point of submission.\n *\n * Always emitted, empty when there is nothing to report - a V1-engine form\n * has no stable condition ids to record against. Optional only for\n * backwards compatibility: a message published before this existed carries\n * no evaluations at all, and consumers fall back to resolving the recipients\n * from the form definition themselves.\n */\n conditionEvaluations?: SubmitConditionEvaluation[]\n}\n\nexport interface FormAdapterSubmissionMessage extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAmEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AA2BA;AACA;AACA;AACA;;AAwGA,SACEA,UAAU,EACVC,YAAY;;AAoEd;AACA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAGA;AACA;AACA;;AAgBA;AACA;AACA;;AAQA;AACA;AACA;;AA+MA;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type PaymentFieldComponent,\n type SubmitConditionEvaluation,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type RequestQuery,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type Yar } from '@hapi/yar'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type FileUploadField,\n type PaymentField\n} from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\nexport interface CacheRequest {\n yar: { id: string | undefined }\n params: { slug?: string; state?: string }\n}\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError extends Pick<\n ValidationErrorItem,\n 'context' | 'path'\n> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | GeospatialState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n languages: { name: string; code: string }[]\n translator: Translator | undefined\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorCode?: string\n errorMessage?: string\n errorParams?: Record<string, number | string | string[]>\n }\n | {\n fileStatus: FileStatus.complete\n errorCode?: string\n errorMessage?: undefined\n errorParams?: Record<string, number | string | string[]>\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\n/**\n * A longitude/latitude coordinate pair in WGS84 format\n * Format: [longitude, latitude]\n */\nexport type Coordinates = [longitude: number, latitude: number]\n\n/**\n * GeoJSON Point geometry\n */\nexport interface PointGeometry {\n type: 'Point'\n coordinates: Coordinates\n}\n\n/**\n * GeoJSON LineString geometry\n */\nexport interface LineStringGeometry {\n type: 'LineString'\n coordinates: Coordinates[]\n}\n\n/**\n * GeoJSON Polygon geometry\n */\nexport interface PolygonGeometry {\n type: 'Polygon'\n coordinates: Coordinates[][]\n}\n\n/**\n * Supported geometry types\n */\nexport type Geometry = PointGeometry | LineStringGeometry | PolygonGeometry\n\n/**\n * Feature metadata\n */\nexport interface FeatureProperties {\n /**\n * Human-readable description of the feature\n */\n description: string\n /**\n * The OS grid reference of the first coordinate of the feature\n */\n coordinateGridReference?: string\n /**\n * The OS grid reference of the centroid of the feature\n */\n centroidGridReference?: string\n}\n\n/**\n * A single GeoJSON Feature\n */\nexport interface Feature {\n id: string\n type: 'Feature'\n properties: FeatureProperties\n geometry: Geometry\n}\n\n/**\n * A GeoJSON FeatureCollection\n */\nexport type FeatureCollection = Feature[]\n\nexport type GeospatialState = FeatureCollection\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n t?: (key: string, opts?: Record<string, unknown>) => string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n showSubmitButton?: boolean\n showPaymentExpiredNotification?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport type GetLanguageHandler = (query?: RequestQuery, yar?: Yar) => string\n\nexport type GenerateReferenceNumberHandler = (\n prefix?: string\n) => Promise<string>\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs?: Record<string, string>\n isLive: boolean\n isPreview: boolean\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface PaymentExternalArgs extends ExternalArgs {\n component: PaymentFieldComponent\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n getLanguage?: GetLanguageHandler\n generateReferenceNumber?: GenerateReferenceNumberHandler\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: (typeof FormAdapterSubmissionSchemaVersion)[keyof typeof FormAdapterSubmissionSchemaVersion]\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n language?: string\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterPayment {\n paymentId: string\n reference: string\n amount: number\n description: string\n descriptionInEng?: string\n createdAt: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\n/**\n * A detail item specifically for payments\n */\nexport type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {\n field: PaymentField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n | GeospatialState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n payment?: FormAdapterPayment\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n\n /**\n * The outcome of every condition in the form definition, evaluated against\n * the final answers at the point of submission.\n *\n * Always emitted, empty when there is nothing to report - a V1-engine form\n * has no stable condition ids to record against. Optional only for\n * backwards compatibility: a message published before this existed carries\n * no evaluations at all, and consumers fall back to resolving the recipients\n * from the form definition themselves.\n */\n conditionEvaluations?: SubmitConditionEvaluation[]\n}\n\nexport interface FormAdapterSubmissionMessage extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAmEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AA2BA;AACA;AACA;AACA;;AAwGA,SACEA,UAAU,EACVC,YAAY;;AAoEd;AACA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAGA;AACA;AACA;;AAgBA;AACA;AACA;;AAQA;AACA;AACA;;AAoNA;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ {% macro TrackingField(component) %}
2
+ <input type="hidden" id="{{ component.model.name }}" name="{{ component.model.name }}" value="true" />
3
+ {% endmacro %}
@@ -3,5 +3,5 @@
3
3
  * @this {NunjucksContext}
4
4
  * @param {string} name - The name of the component
5
5
  */
6
- export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/PaymentField.js").PaymentField | import("../../engine/components/GeospatialField.js").GeospatialField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | import("../../engine/components/NotificationBanner.js").NotificationBanner | undefined;
6
+ export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/TrackingField.js").TrackingField | import("../../engine/components/PaymentField.js").PaymentField | import("../../engine/components/GeospatialField.js").GeospatialField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | import("../../engine/components/NotificationBanner.js").NotificationBanner | undefined;
7
7
  import type { NunjucksContext } from '../../../../server/plugins/nunjucks/types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/hapi/index.d.ts"],"sourcesContent":["import { type Plugin } from '@hapi/hapi'\nimport { type ServerYar, type Yar } from '@hapi/yar'\nimport { type Logger } from 'pino'\n\nimport {\n type COMPONENT_STATE_ERROR,\n type EXTERNAL_STATE_APPENDAGE,\n type EXTERNAL_STATE_PAYLOAD\n} from '~/src/server/constants.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type AnyFormRequest,\n type FormSubmissionError,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.ts'\nimport { type CacheService } from '~/src/server/services/index.js'\n\ndeclare module '@hapi/yar' {\n interface YarFlashes {\n [EXTERNAL_STATE_APPENDAGE]: object\n [EXTERNAL_STATE_PAYLOAD]: object\n [COMPONENT_STATE_ERROR]: string\n [key: string]: { errors: FormSubmissionError[] }\n }\n}\n\ndeclare module '@hapi/hapi' {\n // Here we are decorating Hapi interface types with\n // props from plugins which doesn't export @types\n interface PluginProperties {\n crumb: {\n generate?: (request: AnyRequest) => string\n }\n 'forms-engine-plugin': {\n baseLayoutPath: string\n cacheService: CacheService\n viewContext?: (\n request: AnyFormRequest | null\n ) => Record<string, unknown> | Promise<Record<string, unknown>>\n saveAndExit?: PluginOptions['saveAndExit']\n getLanguage?: PluginOptions['getLanguage']\n baseUrl: string\n services: PluginOptions['services']\n }\n }\n\n interface Request {\n logger: Logger\n yar: Yar\n }\n\n interface RequestApplicationState {\n model?: FormModel\n }\n\n interface Server {\n logger: Logger\n yar: ServerYar\n }\n\n interface ServerApplicationState {\n model?: FormModel\n models?: Map<string, { model: FormModel; updatedAt: Date }>\n }\n}\n\ndeclare module 'blankie' {\n declare const blankie: {\n plugin: Plugin<Record<string, boolean | string | string[]>>\n }\n\n export = blankie\n}\n\ndeclare module 'blipp' {\n declare const blipp: {\n plugin: Plugin\n }\n\n export = blipp\n}\n\ndeclare module 'hapi-pulse' {\n declare const hapiPulse: {\n plugin: Plugin<{\n timeout: number\n }>\n }\n\n export = hapiPulse\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/hapi/index.d.ts"],"sourcesContent":["import { type Plugin } from '@hapi/hapi'\nimport { type ServerYar, type Yar } from '@hapi/yar'\nimport { type Logger } from 'pino'\n\nimport {\n type COMPONENT_STATE_ERROR,\n type EXTERNAL_STATE_APPENDAGE,\n type EXTERNAL_STATE_PAYLOAD\n} from '~/src/server/constants.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type AnyFormRequest,\n type FormSubmissionError,\n type GenerateReferenceNumberHandler,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.ts'\nimport { type CacheService } from '~/src/server/services/index.js'\n\ndeclare module '@hapi/yar' {\n interface YarFlashes {\n [EXTERNAL_STATE_APPENDAGE]: object\n [EXTERNAL_STATE_PAYLOAD]: object\n [COMPONENT_STATE_ERROR]: string\n [key: string]: { errors: FormSubmissionError[] }\n }\n}\n\ndeclare module '@hapi/hapi' {\n // Here we are decorating Hapi interface types with\n // props from plugins which doesn't export @types\n interface PluginProperties {\n crumb: {\n generate?: (request: AnyRequest) => string\n }\n 'forms-engine-plugin': {\n baseLayoutPath: string\n cacheService: CacheService\n viewContext?: (\n request: AnyFormRequest | null\n ) => Record<string, unknown> | Promise<Record<string, unknown>>\n saveAndExit?: PluginOptions['saveAndExit']\n getLanguage?: PluginOptions['getLanguage']\n baseUrl: string\n services: PluginOptions['services']\n generateReferenceNumber: GenerateReferenceNumberHandler\n }\n }\n\n interface Request {\n logger: Logger\n yar: Yar\n }\n\n interface RequestApplicationState {\n model?: FormModel\n }\n\n interface Server {\n logger: Logger\n yar: ServerYar\n }\n\n interface ServerApplicationState {\n model?: FormModel\n models?: Map<string, { model: FormModel; updatedAt: Date }>\n }\n}\n\ndeclare module 'blankie' {\n declare const blankie: {\n plugin: Plugin<Record<string, boolean | string | string[]>>\n }\n\n export = blankie\n}\n\ndeclare module 'blipp' {\n declare const blipp: {\n plugin: Plugin\n }\n\n export = blipp\n}\n\ndeclare module 'hapi-pulse' {\n declare const hapiPulse: {\n plugin: Plugin<{\n timeout: number\n }>\n }\n\n export = hapiPulse\n}\n"],"mappings":"","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "5.0.0-alpha.21",
3
+ "version": "5.0.0-alpha.23",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -92,7 +92,7 @@
92
92
  },
93
93
  "license": "SEE LICENSE IN LICENSE",
94
94
  "dependencies": {
95
- "@defra/forms-model": "^3.0.704",
95
+ "@defra/forms-model": "^3.0.708",
96
96
  "@defra/hapi-tracing": "^1.29.0",
97
97
  "@defra/interactive-map": "0.0.33-alpha",
98
98
  "@elastic/ecs-pino-format": "^1.5.0",
@@ -197,7 +197,7 @@
197
197
  "core-js": "^3.46.0",
198
198
  "cssnano": "^7.1.2",
199
199
  "cssnano-preset-default": "^7.0.10",
200
- "editorconfig-checker": "^6.1.1",
200
+ "editorconfig-checker": "^6.2.0",
201
201
  "eslint": "^9.39.3",
202
202
  "eslint-config-prettier": "^10.1.8",
203
203
  "eslint-plugin-jest": "^28.14.0",
@@ -6,6 +6,14 @@
6
6
  "path": "/all-components",
7
7
  "title": "All Components",
8
8
  "components": [
9
+ {
10
+ "type": "TrackingField",
11
+ "name": "trackingField",
12
+ "title": "Tracking field",
13
+ "hint": "Help text",
14
+ "options": {},
15
+ "schema": {}
16
+ },
9
17
  {
10
18
  "type": "TextField",
11
19
  "name": "textField",
@@ -5,6 +5,10 @@ pages:
5
5
  - path: '/whats-your-name'
6
6
  title: What's your name?
7
7
  components:
8
+ - type: TrackingField
9
+ name: hasSeenNamePage
10
+ title: Tracks if the user has seen the name page
11
+ options: {}
8
12
  - type: TextField
9
13
  name: textField
10
14
  title: Name
@@ -0,0 +1,64 @@
1
+ import { type TrackingFieldComponent } from '@defra/forms-model'
2
+ import joi, { type BooleanSchema } from 'joi'
3
+
4
+ import {
5
+ FormComponent,
6
+ isFormValue
7
+ } from './FormComponent.js'
8
+ import { messageTemplate } from '../pageControllers/validationOptions.js'
9
+ import {
10
+ type ErrorMessageTemplateList,
11
+ type FormState,
12
+ type FormStateValue,
13
+ type FormSubmissionState
14
+ } from '../types.js'
15
+
16
+ export class TrackingField extends FormComponent {
17
+ declare formSchema: BooleanSchema
18
+ declare stateSchema: BooleanSchema
19
+ declare options: TrackingFieldComponent['options']
20
+
21
+ constructor(
22
+ def: TrackingFieldComponent,
23
+ props: ConstructorParameters<typeof FormComponent>[1]
24
+ ) {
25
+ super(def, props)
26
+
27
+ const formSchema = joi.boolean().valid(true).label(this.label).required()
28
+
29
+ this.formSchema = formSchema.default(false)
30
+ this.stateSchema = formSchema.default(null).allow(null)
31
+ this.schema = {}
32
+ this.options = {}
33
+ }
34
+
35
+ getFormValueFromState(state: FormSubmissionState) {
36
+ const { name } = this
37
+ return this.getFormValue(state[name])
38
+ }
39
+
40
+ isValue(value?: FormStateValue | FormState): value is true {
41
+ return TrackingField.isTracking(value)
42
+ }
43
+
44
+ /**
45
+ * For error preview page that shows all possible errors on a component
46
+ */
47
+ getAllPossibleErrors(): ErrorMessageTemplateList {
48
+ return TrackingField.getAllPossibleErrors()
49
+ }
50
+
51
+ static isTracking(value?: FormStateValue | FormState): value is true {
52
+ return isFormValue(value) && typeof value === 'boolean'
53
+ }
54
+
55
+ /**
56
+ * Static version of getAllPossibleErrors that doesn't require a component instance.
57
+ */
58
+ static getAllPossibleErrors(): ErrorMessageTemplateList {
59
+ return {
60
+ baseErrors: [{ type: 'required', template: messageTemplate.required }],
61
+ advancedSettingsErrors: []
62
+ }
63
+ }
64
+ }
@@ -37,6 +37,7 @@ export type Field = InstanceType<
37
37
  | typeof Components.UkAddressField
38
38
  | typeof Components.FileUploadField
39
39
  | typeof Components.HiddenField
40
+ | typeof Components.TrackingField
40
41
  | typeof Components.PaymentField
41
42
  | typeof Components.GeospatialField
42
43
  >
@@ -201,6 +202,10 @@ export function createComponent(
201
202
  component = new Components.HiddenField(def, options)
202
203
  break
203
204
 
205
+ case ComponentType.TrackingField:
206
+ component = new Components.TrackingField(def, options)
207
+ break
208
+
204
209
  case ComponentType.PaymentField:
205
210
  component = new Components.PaymentField(def, options)
206
211
  break
@@ -30,5 +30,6 @@ export { OsGridRefField } from './OsGridRefField.js'
30
30
  export { NationalGridFieldNumberField } from './NationalGridFieldNumberField.js'
31
31
  export { LatLongField } from './LatLongField.js'
32
32
  export { HiddenField } from './HiddenField.js'
33
+ export { TrackingField } from './TrackingField.js'
33
34
  export { PaymentField } from './PaymentField.js'
34
35
  export { GeospatialField } from './GeospatialField.js'
@@ -26,7 +26,8 @@ const pluginRegistrationOptionsSchema = Joi.object({
26
26
  saveAndExit: Joi.function().optional(),
27
27
  ordnanceSurveyApiKey: Joi.string().optional(),
28
28
  ordnanceSurveyApiSecret: Joi.string().optional(),
29
- getLanguage: Joi.function().optional()
29
+ getLanguage: Joi.function().optional(),
30
+ generateReferenceNumber: Joi.function().optional()
30
31
  })
31
32
 
32
33
  /**
@@ -8,6 +8,7 @@ import {
8
8
 
9
9
  import { type FormModel } from './models/index.js'
10
10
  import { validatePluginOptions } from './options.js'
11
+ import { generateUniqueReference } from './referenceNumbers.js'
11
12
  import { getRoutes as getFileUploadStatusRoutes } from './routes/file-upload.js'
12
13
  import { makeLoadFormPreHandler } from './routes/index.js'
13
14
  import { getRoutes as getPaymentRoutes } from './routes/payment.js'
@@ -37,6 +38,7 @@ export const plugin = {
37
38
  cache,
38
39
  saveAndExit,
39
40
  getLanguage,
41
+ generateReferenceNumber,
40
42
  nunjucks: nunjucksOptions,
41
43
  viewContext,
42
44
  preparePageEventRequestOptions,
@@ -80,6 +82,10 @@ export const plugin = {
80
82
  server.expose('cacheService', cacheService)
81
83
  server.expose('saveAndExit', saveAndExit)
82
84
  server.expose('getLanguage', getLanguage)
85
+ server.expose(
86
+ 'generateReferenceNumber',
87
+ generateReferenceNumber ?? generateUniqueReference
88
+ )
83
89
  server.expose('baseUrl', baseUrl)
84
90
  server.expose('services', services)
85
91
 
@@ -6,6 +6,8 @@ import {
6
6
  englishRecommendedTransformers
7
7
  } from 'obscenity'
8
8
 
9
+ export const DEFAULT_REFERENCE_NUMBER = 'XXX-XXX-XXX'
10
+
9
11
  /**
10
12
  * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.
11
13
  * @param strCodes - array of binary input values
@@ -20,6 +20,7 @@ import {
20
20
  findPage,
21
21
  getCacheService,
22
22
  getPage,
23
+ getPluginOptions,
23
24
  getStartPath,
24
25
  proceed
25
26
  } from '../helpers.js'
@@ -28,7 +29,7 @@ import {
28
29
  checkSaveAndExitRepeater,
29
30
  copyNotYetValidatedState
30
31
  } from '../pageControllers/helpers/state.js'
31
- import { generateUniqueReference } from '../referenceNumbers.js'
32
+ import { DEFAULT_REFERENCE_NUMBER } from '../referenceNumbers.js'
32
33
  import * as defaultServices from '../services/index.js'
33
34
  import {
34
35
  type AnyFormRequest,
@@ -54,7 +55,7 @@ export async function redirectOrMakeHandler(
54
55
  context: FormContext
55
56
  ) => ResponseObject | Promise<ResponseObject>
56
57
  ) {
57
- const { app, params } = request
58
+ const { app, params, query } = request
58
59
  const { model } = app
59
60
 
60
61
  if (!model) {
@@ -74,7 +75,12 @@ export async function redirectOrMakeHandler(
74
75
  )
75
76
  }
76
77
 
77
- const referenceNumber = generateUniqueReference(prefix)
78
+ const isForce = 'force' in query
79
+ const { generateReferenceNumber } = getPluginOptions(request.server)
80
+ const referenceNumber = isForce
81
+ ? DEFAULT_REFERENCE_NUMBER
82
+ : await generateReferenceNumber(prefix)
83
+
78
84
  state = await page.mergeState(request, state, {
79
85
  $$__referenceNumber: referenceNumber
80
86
  })
@@ -484,6 +484,10 @@ export type SaveAndExitHandler = (
484
484
 
485
485
  export type GetLanguageHandler = (query?: RequestQuery, yar?: Yar) => string
486
486
 
487
+ export type GenerateReferenceNumberHandler = (
488
+ prefix?: string
489
+ ) => Promise<string>
490
+
487
491
  export interface ExternalArgs {
488
492
  component: ComponentDef
489
493
  controller: QuestionPageController
@@ -524,6 +528,7 @@ export interface PluginOptions {
524
528
  preparePageEventRequestOptions?: PreparePageEventRequestOptions
525
529
  onRequest?: OnRequestCallback
526
530
  getLanguage?: GetLanguageHandler
531
+ generateReferenceNumber?: GenerateReferenceNumberHandler
527
532
  baseUrl: string // base URL of the application, protocol and hostname e.g. "https://myapp.com"
528
533
  ordnanceSurveyApiKey?: string
529
534
  ordnanceSurveyApiSecret?: string
@@ -0,0 +1,3 @@
1
+ {% macro TrackingField(component) %}
2
+ <input type="hidden" id="{{ component.model.name }}" name="{{ component.model.name }}" value="true" />
3
+ {% endmacro %}
@@ -11,6 +11,7 @@ import { type FormModel } from '../../server/plugins/engine/models/index.js'
11
11
  import {
12
12
  type AnyFormRequest,
13
13
  type FormSubmissionError,
14
+ type GenerateReferenceNumberHandler,
14
15
  type PluginOptions
15
16
  } from '../../server/plugins/engine/types.ts'
16
17
  import { type CacheService } from '../../server/services/index.js'
@@ -41,6 +42,7 @@ declare module '@hapi/hapi' {
41
42
  getLanguage?: PluginOptions['getLanguage']
42
43
  baseUrl: string
43
44
  services: PluginOptions['services']
45
+ generateReferenceNumber: GenerateReferenceNumberHandler
44
46
  }
45
47
  }
46
48