@defra/forms-engine-plugin 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.server/server/plugins/engine/index.js +1 -1
  2. package/.server/server/plugins/engine/index.js.map +1 -1
  3. package/.server/server/plugins/engine/models/FormModel.js +2 -2
  4. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  5. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +1 -0
  6. package/.server/server/plugins/engine/models/SummaryViewModel.js +1 -0
  7. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  8. package/.server/server/plugins/engine/options.js +4 -1
  9. package/.server/server/plugins/engine/options.js.map +1 -1
  10. package/.server/server/plugins/engine/options.test.js +20 -0
  11. package/.server/server/plugins/engine/options.test.js.map +1 -1
  12. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +5 -0
  13. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +27 -1
  14. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  15. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -0
  16. package/.server/server/plugins/engine/pageControllers/StartPageController.js +3 -0
  17. package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
  18. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +1 -0
  19. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +4 -0
  20. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  21. package/.server/server/plugins/engine/plugin.js +5 -2
  22. package/.server/server/plugins/engine/plugin.js.map +1 -1
  23. package/.server/server/plugins/engine/routes/exit.d.ts +46 -0
  24. package/.server/server/plugins/engine/routes/exit.js +36 -0
  25. package/.server/server/plugins/engine/routes/exit.js.map +1 -0
  26. package/.server/server/plugins/engine/types.d.ts +6 -2
  27. package/.server/server/plugins/engine/types.js.map +1 -1
  28. package/.server/server/plugins/engine/views/exit.html +31 -0
  29. package/.server/server/plugins/engine/views/partials/form.html +17 -6
  30. package/.server/server/routes/types.d.ts +2 -1
  31. package/.server/server/routes/types.js +1 -0
  32. package/.server/server/routes/types.js.map +1 -1
  33. package/.server/server/schemas/index.js +1 -1
  34. package/.server/server/schemas/index.js.map +1 -1
  35. package/.server/server/services/cacheService.d.ts +2 -0
  36. package/.server/server/services/cacheService.js +9 -5
  37. package/.server/server/services/cacheService.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/server/index.test.ts +39 -0
  40. package/src/server/plugins/engine/components/helpers.test.ts +31 -0
  41. package/src/server/plugins/engine/index.ts +1 -3
  42. package/src/server/plugins/engine/models/FormModel.test.ts +85 -11
  43. package/src/server/plugins/engine/models/FormModel.ts +5 -2
  44. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +59 -0
  45. package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -0
  46. package/src/server/plugins/engine/options.js +4 -1
  47. package/src/server/plugins/engine/options.test.js +20 -0
  48. package/src/server/plugins/engine/pageControllers/PageController.test.ts +25 -0
  49. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +178 -1
  50. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +28 -1
  51. package/src/server/plugins/engine/pageControllers/StartPageController.ts +4 -0
  52. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +5 -0
  53. package/src/server/plugins/engine/plugin.ts +5 -1
  54. package/src/server/plugins/engine/routes/exit.ts +47 -0
  55. package/src/server/plugins/engine/types.ts +10 -4
  56. package/src/server/plugins/engine/views/exit.html +31 -0
  57. package/src/server/plugins/engine/views/partials/form.html +17 -6
  58. package/src/server/routes/types.ts +2 -1
  59. package/src/server/schemas/index.ts +2 -1
  60. package/src/server/services/cacheService.test.ts +45 -0
  61. package/src/server/services/cacheService.ts +20 -9
@@ -17,7 +17,7 @@ export const prepareNunjucksEnvironment = function (env, pluginOptions) {
17
17
  for (const [name, nunjucksFilter] of Object.entries(filters)) {
18
18
  env.addFilter(name, nunjucksFilter);
19
19
  }
20
- env.addFilter('markdown', text => markdownToHtml(text, pluginOptions.baseUrl));
20
+ env.addFilter('markdown', text => markdownToHtml(text));
21
21
  for (const [name, nunjucksGlobal] of Object.entries(globals)) {
22
22
  env.addGlobal(name, nunjucksGlobal);
23
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","filters","getPageHref","context","globals","VIEW_PATH","PLUGIN_PATH","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string) =>\n markdownToHtml(text, pluginOptions.baseUrl)\n )\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY;AAEd,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAEhB,MAAMC,OAAO,GAAG;EACdP,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC;AACF,CAAC;AAED,OAAO,MAAMK,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACZ,OAAO,CAAC,EAAE;IAC5DO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAGC,IAAY,IACrCrB,cAAc,CAACqB,IAAI,EAAEN,aAAa,CAACO,OAAO,CAC5C,CAAC;EAED,KAAK,MAAM,CAACN,IAAI,EAAEO,cAAc,CAAC,IAAIL,MAAM,CAACC,OAAO,CAACT,OAAO,CAAC,EAAE;IAC5DI,GAAG,CAACU,SAAS,CAACR,IAAI,EAAEO,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIR,aAAa,CAACR,OAAO,EAAE;IACzB,KAAK,MAAM,CAACS,IAAI,EAAES,MAAM,CAAC,IAAIP,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACR,OAAO,CAAC,EAAE;MAClEO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAES,MAAM,CAAC;MAC3BxB,MAAM,CAACyB,cAAc,CAACV,IAAI,EAAES,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIV,aAAa,CAACL,OAAO,EAAE;IACzBQ,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACL,OAAO,CAAC,CAACiB,OAAO,CAAC,CAAC,CAACX,IAAI,EAAEY,EAAE,CAAC,KAAK;MAC5Dd,GAAG,CAACU,SAAS,CAACR,IAAI,EAAEY,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAe1B,MAAM","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","filters","getPageHref","context","globals","VIEW_PATH","PLUGIN_PATH","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string) => markdownToHtml(text))\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY;AAEd,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAEhB,MAAMC,OAAO,GAAG;EACdP,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC;AACF,CAAC;AAED,OAAO,MAAMK,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACZ,OAAO,CAAC,EAAE;IAC5DO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAGC,IAAY,IAAKrB,cAAc,CAACqB,IAAI,CAAC,CAAC;EAEjE,KAAK,MAAM,CAACL,IAAI,EAAEM,cAAc,CAAC,IAAIJ,MAAM,CAACC,OAAO,CAACT,OAAO,CAAC,EAAE;IAC5DI,GAAG,CAACS,SAAS,CAACP,IAAI,EAAEM,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIP,aAAa,CAACR,OAAO,EAAE;IACzB,KAAK,MAAM,CAACS,IAAI,EAAEQ,MAAM,CAAC,IAAIN,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACR,OAAO,CAAC,EAAE;MAClEO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEQ,MAAM,CAAC;MAC3BvB,MAAM,CAACwB,cAAc,CAACT,IAAI,EAAEQ,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIT,aAAa,CAACL,OAAO,EAAE;IACzBQ,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACL,OAAO,CAAC,CAACgB,OAAO,CAAC,CAAC,CAACV,IAAI,EAAEW,EAAE,CAAC,KAAK;MAC5Db,GAAG,CAACS,SAAS,CAACP,IAAI,EAAEW,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAezB,MAAM","ignoreList":[]}
@@ -173,8 +173,8 @@ export class FormModel {
173
173
  ...evaluationState
174
174
  };
175
175
  for (const conditionId in conditions) {
176
- const alias = generateConditionAlias(conditionId);
177
- Object.defineProperty(context, alias, {
176
+ const propertyName = this.schemaVersion === SchemaVersion.V2 ? generateConditionAlias(conditionId) : conditionId;
177
+ Object.defineProperty(context, propertyName, {
178
178
  get() {
179
179
  return conditions[conditionId]?.fn(evaluationState);
180
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","Engine","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V2","result","validate","abortEarly","error","structuredClone","value","push","id","V1","title","type","items","text","Map","map","page","path","list","filter","flatMap","components","component","forEach","conditionDef","condition","makeCondition","pageDef","some","controller","Status","collection","makeFilteredSchema","relevantPages","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","conditionId","alias","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","referenceNumber","getReferenceNumber","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","pageStateIsInvalid","getNextPath","validateFormState","assignPaths","key","keys","getContextValueFromState","listFields","fields","field","undefined","YesNoField","hasOptionalItems","item","length","fieldStateIsInvalid","validValues","fieldState","getFormValueFromState","isInvalid","isArray","Array","every","includes","href","getComponentById","componentId","getListById","listId","getConditionById","action","getFormParams","Validate","update","CheckboxesField","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details","$$__referenceNumber","Error"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n Engine,\n SchemaVersion,\n convertConditionWrapperFromV2,\n formDefinitionSchema,\n formDefinitionV2Schema,\n generateConditionAlias,\n hasComponents,\n hasRepeater,\n isConditionWrapperV2,\n yesNoListId,\n yesNoListName,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionWrapperV2,\n type ConditionsModelData,\n type DateUnits,\n type FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\nimport { type ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport {} from '~/src/server/plugins/engine/components/YesNoField.js'\nimport {\n hasListFormField,\n type Component\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { todayAsDateOnly } from '~/src/server/plugins/engine/date-helper.js'\nimport {\n findPage,\n getError,\n getPage,\n setPageTitles\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n schemaVersion: SchemaVersion\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n values: FormDefinition\n basePath: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n\n listDefMap: Map<string, List>\n listDefIdMap: Map<string, List>\n\n componentDefMap: Map<string, ComponentDef>\n componentDefIdMap: Map<string, ComponentDef>\n\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionSchema\n\n if (def.schema === SchemaVersion.V2) {\n schema = formDefinitionV2Schema\n }\n\n const result = schema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n id: def.schema === SchemaVersion.V1 ? yesNoListName : yesNoListId,\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n id: '02900d42-83d1-4c72-a719-c4e8228952fa',\n text: 'Yes',\n value: true\n },\n {\n id: 'f39000eb-c51b-4019-8f82-bbda0423f04d',\n text: 'No',\n value: false\n }\n ]\n })\n\n // Fix up page titles\n setPageTitles(def)\n\n this.engine = def.engine\n this.schemaVersion = def.schema ?? SchemaVersion.V1\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.listDefIdMap = new Map(\n def.lists\n .filter((list) => list.id) // Skip lists without an ID\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n .map((list) => [list.id as string, list])\n )\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n this.componentDefIdMap = new Map(\n def.pages.filter(hasComponents).flatMap((page) =>\n page.components\n .filter((component) => component.id) // Skip components without an ID\n .map((component) => {\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return [component.id as string, component]\n })\n )\n )\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(\n isConditionWrapperV2(conditionDef)\n ? convertConditionWrapperFromV2(conditionDef, this)\n : conditionDef\n )\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n // The time element must be stripped (hence using startOfDay() which has no time element),\n // then formatted as YYYY-MM-DD otherwise we can hit time element and BST issues giving the\n // wrong date to compare against.\n // Do not use .toISOString() to format the date as that introduces BST errors.\n return format(\n add(todayAsDateOnly(), { [timeUnit]: timePeriod }),\n 'yyyy-MM-dd'\n )\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const conditionId in conditions) {\n const alias = generateConditionAlias(conditionId)\n\n Object.defineProperty(context, alias, {\n get() {\n return conditions[conditionId]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(nameOrId: string): List | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.lists.find((list) => list.name === nameOrId)\n : this.lists.find((list) => list.id === nameOrId)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap,\n referenceNumber: getReferenceNumber(state)\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (\n this.pageStateIsInvalid(context, nextPage) ||\n nextPage.path === currentPath\n ) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\n }\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private pageStateIsInvalid(context: FormContext, page: PageControllerClass) {\n // Get any list-bound fields on the page\n const listFields = page.collection.fields.filter(hasListFormField)\n\n // For each list field that is bound to a list that contains any conditional items,\n // we need to check any answers are still valid. Do this by evaluating the conditions\n // and ensuring any current answers are all included in the set of valid answers\n for (const field of listFields) {\n const list = field.list\n\n // Filter out YesNo as they can't be conditional\n if (list !== undefined && field.type !== ComponentType.YesNoField) {\n const hasOptionalItems =\n list.items.filter((item) => item.condition).length > 0\n\n if (hasOptionalItems) {\n return this.fieldStateIsInvalid(context, field, list)\n }\n }\n }\n }\n\n private fieldStateIsInvalid(\n context: FormContext,\n field: ListFormComponent,\n list: List\n ) {\n const { evaluationState, state } = context\n\n const validValues = list.items\n .filter((item) =>\n item.condition\n ? this.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n .map((item) => item.value)\n\n // Get the field state\n const fieldState = field.getFormValueFromState(state)\n\n if (fieldState !== undefined) {\n let isInvalid = false\n const isArray = Array.isArray(fieldState)\n\n // Check if any saved state value(s) are still valid\n // and return true if any are invalid\n if (isArray) {\n isInvalid = !fieldState.every((item) => validValues.includes(item))\n } else {\n isInvalid = !validValues.includes(fieldState)\n }\n\n if (isInvalid) {\n context.errors ??= []\n\n const text =\n 'Options are different because you changed a previous answer'\n\n context.errors.push({\n text,\n name: field.name,\n href: `#${field.name}`,\n path: [`#${field.name}`]\n })\n }\n\n return isInvalid\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n\n getComponentById(componentId: string): ComponentDef | undefined {\n return this.componentDefIdMap.get(componentId)\n }\n\n getListById(listId: string): List | undefined {\n return this.listDefIdMap.get(listId)\n }\n\n /**\n * Returns a condition by its ID. O(n) lookup time.\n * @param conditionId\n * @returns\n */\n getConditionById(conditionId: string): ConditionWrapperV2 | undefined {\n return this.def.conditions\n .filter(isConditionWrapperV2)\n .find((condition) => condition.id === conditionId)\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (!request.payload || action !== FormAction.Validate) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n\nfunction getReferenceNumber(state: FormState): string {\n if (\n !state.$$__referenceNumber ||\n typeof state.$$__referenceNumber !== 'string'\n ) {\n throw Error('Reference number not found in form state')\n }\n\n return state.$$__referenceNumber\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","Engine","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V2","result","validate","abortEarly","error","structuredClone","value","push","id","V1","title","type","items","text","Map","map","page","path","list","filter","flatMap","components","component","forEach","conditionDef","condition","makeCondition","pageDef","some","controller","Status","collection","makeFilteredSchema","relevantPages","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","conditionId","propertyName","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","referenceNumber","getReferenceNumber","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","pageStateIsInvalid","getNextPath","validateFormState","assignPaths","key","keys","getContextValueFromState","listFields","fields","field","undefined","YesNoField","hasOptionalItems","item","length","fieldStateIsInvalid","validValues","fieldState","getFormValueFromState","isInvalid","isArray","Array","every","includes","href","getComponentById","componentId","getListById","listId","getConditionById","action","getFormParams","Validate","update","CheckboxesField","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details","$$__referenceNumber","Error"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n Engine,\n SchemaVersion,\n convertConditionWrapperFromV2,\n formDefinitionSchema,\n formDefinitionV2Schema,\n generateConditionAlias,\n hasComponents,\n hasRepeater,\n isConditionWrapperV2,\n yesNoListId,\n yesNoListName,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionWrapperV2,\n type ConditionsModelData,\n type DateUnits,\n type FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\nimport { type ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport {} from '~/src/server/plugins/engine/components/YesNoField.js'\nimport {\n hasListFormField,\n type Component\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { todayAsDateOnly } from '~/src/server/plugins/engine/date-helper.js'\nimport {\n findPage,\n getError,\n getPage,\n setPageTitles\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n schemaVersion: SchemaVersion\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n values: FormDefinition\n basePath: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n\n listDefMap: Map<string, List>\n listDefIdMap: Map<string, List>\n\n componentDefMap: Map<string, ComponentDef>\n componentDefIdMap: Map<string, ComponentDef>\n\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionSchema\n\n if (def.schema === SchemaVersion.V2) {\n schema = formDefinitionV2Schema\n }\n\n const result = schema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n id: def.schema === SchemaVersion.V1 ? yesNoListName : yesNoListId,\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n id: '02900d42-83d1-4c72-a719-c4e8228952fa',\n text: 'Yes',\n value: true\n },\n {\n id: 'f39000eb-c51b-4019-8f82-bbda0423f04d',\n text: 'No',\n value: false\n }\n ]\n })\n\n // Fix up page titles\n setPageTitles(def)\n\n this.engine = def.engine\n this.schemaVersion = def.schema ?? SchemaVersion.V1\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.listDefIdMap = new Map(\n def.lists\n .filter((list) => list.id) // Skip lists without an ID\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n .map((list) => [list.id as string, list])\n )\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n this.componentDefIdMap = new Map(\n def.pages.filter(hasComponents).flatMap((page) =>\n page.components\n .filter((component) => component.id) // Skip components without an ID\n .map((component) => {\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return [component.id as string, component]\n })\n )\n )\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(\n isConditionWrapperV2(conditionDef)\n ? convertConditionWrapperFromV2(conditionDef, this)\n : conditionDef\n )\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n // The time element must be stripped (hence using startOfDay() which has no time element),\n // then formatted as YYYY-MM-DD otherwise we can hit time element and BST issues giving the\n // wrong date to compare against.\n // Do not use .toISOString() to format the date as that introduces BST errors.\n return format(\n add(todayAsDateOnly(), { [timeUnit]: timePeriod }),\n 'yyyy-MM-dd'\n )\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const conditionId in conditions) {\n const propertyName =\n this.schemaVersion === SchemaVersion.V2\n ? generateConditionAlias(conditionId)\n : conditionId\n\n Object.defineProperty(context, propertyName, {\n get() {\n return conditions[conditionId]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(nameOrId: string): List | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.lists.find((list) => list.name === nameOrId)\n : this.lists.find((list) => list.id === nameOrId)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap,\n referenceNumber: getReferenceNumber(state)\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (\n this.pageStateIsInvalid(context, nextPage) ||\n nextPage.path === currentPath\n ) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\n }\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private pageStateIsInvalid(context: FormContext, page: PageControllerClass) {\n // Get any list-bound fields on the page\n const listFields = page.collection.fields.filter(hasListFormField)\n\n // For each list field that is bound to a list that contains any conditional items,\n // we need to check any answers are still valid. Do this by evaluating the conditions\n // and ensuring any current answers are all included in the set of valid answers\n for (const field of listFields) {\n const list = field.list\n\n // Filter out YesNo as they can't be conditional\n if (list !== undefined && field.type !== ComponentType.YesNoField) {\n const hasOptionalItems =\n list.items.filter((item) => item.condition).length > 0\n\n if (hasOptionalItems) {\n return this.fieldStateIsInvalid(context, field, list)\n }\n }\n }\n }\n\n private fieldStateIsInvalid(\n context: FormContext,\n field: ListFormComponent,\n list: List\n ) {\n const { evaluationState, state } = context\n\n const validValues = list.items\n .filter((item) =>\n item.condition\n ? this.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n .map((item) => item.value)\n\n // Get the field state\n const fieldState = field.getFormValueFromState(state)\n\n if (fieldState !== undefined) {\n let isInvalid = false\n const isArray = Array.isArray(fieldState)\n\n // Check if any saved state value(s) are still valid\n // and return true if any are invalid\n if (isArray) {\n isInvalid = !fieldState.every((item) => validValues.includes(item))\n } else {\n isInvalid = !validValues.includes(fieldState)\n }\n\n if (isInvalid) {\n context.errors ??= []\n\n const text =\n 'Options are different because you changed a previous answer'\n\n context.errors.push({\n text,\n name: field.name,\n href: `#${field.name}`,\n path: [`#${field.name}`]\n })\n }\n\n return isInvalid\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n\n getComponentById(componentId: string): ComponentDef | undefined {\n return this.componentDefIdMap.get(componentId)\n }\n\n getListById(listId: string): List | undefined {\n return this.listDefIdMap.get(listId)\n }\n\n /**\n * Returns a condition by its ID. O(n) lookup time.\n * @param conditionId\n * @returns\n */\n getConditionById(conditionId: string): ConditionWrapperV2 | undefined {\n return this.def.conditions\n .filter(isConditionWrapperV2)\n .find((condition) => condition.id === conditionId)\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (!request.payload || action !== FormAction.Validate) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n\nfunction getReferenceNumber(state: FormState): string {\n if (\n !state.$$__referenceNumber ||\n typeof state.$$__referenceNumber !== 'string'\n ) {\n throw Error('Reference number not found in form state')\n }\n\n return state.$$__referenceNumber\n}\n"],"mappings":"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","ignoreList":[]}
@@ -20,6 +20,7 @@ export declare class SummaryViewModel {
20
20
  serviceUrl: string;
21
21
  hasMissingNotificationEmail?: boolean;
22
22
  components?: ComponentViewModel[];
23
+ allowSaveAndReturn?: boolean;
23
24
  constructor(request: FormContextRequest, page: PageControllerClass, context: FormContext);
24
25
  private summaryDetails;
25
26
  }
@@ -21,6 +21,7 @@ export class SummaryViewModel {
21
21
  serviceUrl;
22
22
  hasMissingNotificationEmail;
23
23
  components;
24
+ allowSaveAndReturn;
24
25
  constructor(request, page, context) {
25
26
  const {
26
27
  model
@@ -1 +1 @@
1
- {"version":3,"file":"SummaryViewModel.js","names":["getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","constructor","request","model","basePath","def","sections","isForceAccess","title","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { type Section } from '@defra/forms-model'\n\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not supplied'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[undefined, ...sections].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? title : `${title}s`\n\n return {\n name,\n label: title,\n title: values.length ? `${unit} added` : unit,\n value: values.length ? `You added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nfunction ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title: field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"SummaryViewModel.js","names":["getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","allowSaveAndReturn","constructor","request","model","basePath","def","sections","isForceAccess","title","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { type Section } from '@defra/forms-model'\n\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n allowSaveAndReturn?: boolean\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not supplied'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[undefined, ...sections].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? title : `${title}s`\n\n return {\n name,\n label: title,\n title: values.length ? `${unit} added` : unit,\n value: values.length ? `You added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nfunction ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title: field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -16,7 +16,10 @@ const pluginRegistrationOptionsSchema = Joi.object({
16
16
  viewContext: Joi.function().required(),
17
17
  preparePageEventRequestOptions: Joi.function().optional(),
18
18
  onRequest: Joi.function().optional(),
19
- baseUrl: Joi.string().uri().required()
19
+ baseUrl: Joi.string().uri().required(),
20
+ keyGenerator: Joi.function().optional(),
21
+ sessionHydrator: Joi.function().optional(),
22
+ sessionPersister: Joi.function().optional()
20
23
  });
21
24
 
22
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","names":["Joi","createLogger","logger","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cacheName","globals","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","preparePageEventRequestOptions","onRequest","baseUrl","uri","validatePluginOptions","options","result","validate","abortEarly","error","message","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import Joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\n\nconst logger = createLogger()\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 cacheName: Joi.string().optional(),\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})\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 `Missing required properties in plugin options: ${result.error.message}`\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":"AAAA,OAAOA,GAAG,MAAM,KAAK;AAErB,SAASC,YAAY;AAErB,MAAMC,MAAM,GAAGD,YAAY,CAAC,CAAC;AAE7B,MAAME,+BAA+B,GAAGH,GAAG,CAACI,MAAM,CAAC;EACjDC,KAAK,EAAEL,GAAG,CAACI,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EAC9BC,QAAQ,EAAEP,GAAG,CAACI,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EACjCE,WAAW,EAAER,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACrEM,SAAS,EAAEZ,GAAG,CAACU,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EAClCO,OAAO,EAAEb,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjEQ,OAAO,EAAEd,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjES,UAAU,EAAEf,GAAG,CAACU,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EACnCU,QAAQ,EAAEhB,GAAG,CAACI,MAAM,CAAC;IACnBa,cAAc,EAAEjB,GAAG,CAACU,MAAM,CAAC,CAAC,CAACQ,QAAQ,CAAC,CAAC;IACvCC,KAAK,EAAEnB,GAAG,CAACoB,KAAK,CAAC,CAAC,CAACC,KAAK,CAACrB,GAAG,CAACU,MAAM,CAAC,CAAC,CAAC,CAACQ,QAAQ,CAAC;EAClD,CAAC,CAAC,CAACA,QAAQ,CAAC,CAAC;EACbI,WAAW,EAAEtB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACtCM,8BAA8B,EAAExB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EACzDmB,SAAS,EAAEzB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EACpCoB,OAAO,EAAE1B,GAAG,CAACU,MAAM,CAAC,CAAC,CAACiB,GAAG,CAAC,CAAC,CAACT,QAAQ,CAAC;AACvC,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA,OAAO,SAASU,qBAAqBA,CAACC,OAAO,EAAE;EAC7C,MAAMC,MAAM,GAAG3B,+BAA+B,CAAC4B,QAAQ,CAACF,OAAO,EAAE;IAC/DG,UAAU,EAAE;EACd,CAAC,CAAC;EAEF,IAAIF,MAAM,CAACG,KAAK,EAAE;IAChB/B,MAAM,CAAC+B,KAAK,CACV,kDAAkDH,MAAM,CAACG,KAAK,CAACC,OAAO,EACxE,CAAC;IACD,MAAM,IAAIC,KAAK,CAAC,wBAAwB,EAAEL,MAAM,CAACG,KAAK,CAAC;EACzD;;EAEA;EACA,OAAOH,MAAM,CAACM,KAAK;AACrB;;AAEA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"options.js","names":["Joi","createLogger","logger","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cacheName","globals","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","preparePageEventRequestOptions","onRequest","baseUrl","uri","keyGenerator","sessionHydrator","sessionPersister","validatePluginOptions","options","result","validate","abortEarly","error","message","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import Joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\n\nconst logger = createLogger()\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 cacheName: Joi.string().optional(),\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 keyGenerator: Joi.function().optional(),\n sessionHydrator: Joi.function().optional(),\n sessionPersister: 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 `Missing required properties in plugin options: ${result.error.message}`\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":"AAAA,OAAOA,GAAG,MAAM,KAAK;AAErB,SAASC,YAAY;AAErB,MAAMC,MAAM,GAAGD,YAAY,CAAC,CAAC;AAE7B,MAAME,+BAA+B,GAAGH,GAAG,CAACI,MAAM,CAAC;EACjDC,KAAK,EAAEL,GAAG,CAACI,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EAC9BC,QAAQ,EAAEP,GAAG,CAACI,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EACjCE,WAAW,EAAER,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACrEM,SAAS,EAAEZ,GAAG,CAACU,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EAClCO,OAAO,EAAEb,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjEQ,OAAO,EAAEd,GAAG,CAACI,MAAM,CAAC,CAAC,CAACK,OAAO,CAACT,GAAG,CAACU,MAAM,CAAC,CAAC,EAAEV,GAAG,CAACW,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjES,UAAU,EAAEf,GAAG,CAACU,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EACnCU,QAAQ,EAAEhB,GAAG,CAACI,MAAM,CAAC;IACnBa,cAAc,EAAEjB,GAAG,CAACU,MAAM,CAAC,CAAC,CAACQ,QAAQ,CAAC,CAAC;IACvCC,KAAK,EAAEnB,GAAG,CAACoB,KAAK,CAAC,CAAC,CAACC,KAAK,CAACrB,GAAG,CAACU,MAAM,CAAC,CAAC,CAAC,CAACQ,QAAQ,CAAC;EAClD,CAAC,CAAC,CAACA,QAAQ,CAAC,CAAC;EACbI,WAAW,EAAEtB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACtCM,8BAA8B,EAAExB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EACzDmB,SAAS,EAAEzB,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EACpCoB,OAAO,EAAE1B,GAAG,CAACU,MAAM,CAAC,CAAC,CAACiB,GAAG,CAAC,CAAC,CAACT,QAAQ,CAAC,CAAC;EACtCU,YAAY,EAAE5B,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EACvCuB,eAAe,EAAE7B,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC,CAAC;EAC1CwB,gBAAgB,EAAE9B,GAAG,CAACuB,QAAQ,CAAC,CAAC,CAACjB,QAAQ,CAAC;AAC5C,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA,OAAO,SAASyB,qBAAqBA,CAACC,OAAO,EAAE;EAC7C,MAAMC,MAAM,GAAG9B,+BAA+B,CAAC+B,QAAQ,CAACF,OAAO,EAAE;IAC/DG,UAAU,EAAE;EACd,CAAC,CAAC;EAEF,IAAIF,MAAM,CAACG,KAAK,EAAE;IAChBlC,MAAM,CAACkC,KAAK,CACV,kDAAkDH,MAAM,CAACG,KAAK,CAACC,OAAO,EACxE,CAAC;IACD,MAAM,IAAIC,KAAK,CAAC,wBAAwB,EAAEL,MAAM,CAACG,KAAK,CAAC;EACzD;;EAEA;EACA,OAAOH,MAAM,CAACM,KAAK;AACrB;;AAEA;AACA;AACA","ignoreList":[]}
@@ -15,6 +15,26 @@ describe('validatePluginOptions', () => {
15
15
  };
16
16
  expect(validatePluginOptions(validOptions)).toEqual(validOptions);
17
17
  });
18
+ it('accepts optional properties keyGenerator, sessionHydrator, and sessionPersister', () => {
19
+ const validOptionsWithOptionals = {
20
+ nunjucks: {
21
+ baseLayoutPath: 'dxt-devtool-baselayout.html',
22
+ paths: ['src/server/devserver']
23
+ },
24
+ viewContext: () => {
25
+ return {
26
+ hello: 'world'
27
+ };
28
+ },
29
+ baseUrl: 'http://localhost:3009',
30
+ keyGenerator: () => 'test-key',
31
+ sessionHydrator: () => Promise.resolve({
32
+ someState: 'value'
33
+ }),
34
+ sessionPersister: () => Promise.resolve(undefined)
35
+ };
36
+ expect(validatePluginOptions(validOptionsWithOptionals)).toEqual(validOptionsWithOptionals);
37
+ });
18
38
 
19
39
  /**
20
40
  * tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test
@@ -1 +1 @@
1
- {"version":3,"file":"options.test.js","names":["validatePluginOptions","describe","it","validOptions","nunjucks","baseLayoutPath","paths","viewContext","hello","baseUrl","expect","toEqual","invalidOptions","toThrow"],"sources":["../../../../src/server/plugins/engine/options.test.js"],"sourcesContent":["import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\n\ndescribe('validatePluginOptions', () => {\n it('returns the validated value for valid options', () => {\n const validOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n },\n viewContext: () => {\n return { hello: 'world' }\n },\n baseUrl: 'http://localhost:3009'\n }\n\n expect(validatePluginOptions(validOptions)).toEqual(validOptions)\n })\n\n /**\n * tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test\n */\n it('fails if a required attribute is missing', () => {\n // viewContext is missing\n const invalidOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n }\n }\n\n // @ts-expect-error -- add a test for JS users\n expect(() => validatePluginOptions(invalidOptions)).toThrow(\n 'Invalid plugin options'\n )\n })\n})\n"],"mappings":"AAAA,SAASA,qBAAqB;AAE9BC,QAAQ,CAAC,uBAAuB,EAAE,MAAM;EACtCC,EAAE,CAAC,+CAA+C,EAAE,MAAM;IACxD,MAAMC,YAAY,GAAG;MACnBC,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC,CAAC;MACDC,WAAW,EAAEA,CAAA,KAAM;QACjB,OAAO;UAAEC,KAAK,EAAE;QAAQ,CAAC;MAC3B,CAAC;MACDC,OAAO,EAAE;IACX,CAAC;IAEDC,MAAM,CAACV,qBAAqB,CAACG,YAAY,CAAC,CAAC,CAACQ,OAAO,CAACR,YAAY,CAAC;EACnE,CAAC,CAAC;;EAEF;AACF;AACA;EACED,EAAE,CAAC,0CAA0C,EAAE,MAAM;IACnD;IACA,MAAMU,cAAc,GAAG;MACrBR,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC;IACF,CAAC;;IAED;IACAI,MAAM,CAAC,MAAMV,qBAAqB,CAACY,cAAc,CAAC,CAAC,CAACC,OAAO,CACzD,wBACF,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"options.test.js","names":["validatePluginOptions","describe","it","validOptions","nunjucks","baseLayoutPath","paths","viewContext","hello","baseUrl","expect","toEqual","validOptionsWithOptionals","keyGenerator","sessionHydrator","Promise","resolve","someState","sessionPersister","undefined","invalidOptions","toThrow"],"sources":["../../../../src/server/plugins/engine/options.test.js"],"sourcesContent":["import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\n\ndescribe('validatePluginOptions', () => {\n it('returns the validated value for valid options', () => {\n const validOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n },\n viewContext: () => {\n return { hello: 'world' }\n },\n baseUrl: 'http://localhost:3009'\n }\n\n expect(validatePluginOptions(validOptions)).toEqual(validOptions)\n })\n\n it('accepts optional properties keyGenerator, sessionHydrator, and sessionPersister', () => {\n const validOptionsWithOptionals = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver']\n },\n viewContext: () => {\n return { hello: 'world' }\n },\n baseUrl: 'http://localhost:3009',\n keyGenerator: () => 'test-key',\n sessionHydrator: () => Promise.resolve({ someState: 'value' }),\n sessionPersister: () => Promise.resolve(undefined)\n }\n\n expect(validatePluginOptions(validOptionsWithOptionals)).toEqual(\n validOptionsWithOptionals\n )\n })\n\n /**\n * tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test\n */\n it('fails if a required attribute is missing', () => {\n // viewContext is missing\n const invalidOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n }\n }\n\n // @ts-expect-error -- add a test for JS users\n expect(() => validatePluginOptions(invalidOptions)).toThrow(\n 'Invalid plugin options'\n )\n })\n})\n"],"mappings":"AAAA,SAASA,qBAAqB;AAE9BC,QAAQ,CAAC,uBAAuB,EAAE,MAAM;EACtCC,EAAE,CAAC,+CAA+C,EAAE,MAAM;IACxD,MAAMC,YAAY,GAAG;MACnBC,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC,CAAC;MACDC,WAAW,EAAEA,CAAA,KAAM;QACjB,OAAO;UAAEC,KAAK,EAAE;QAAQ,CAAC;MAC3B,CAAC;MACDC,OAAO,EAAE;IACX,CAAC;IAEDC,MAAM,CAACV,qBAAqB,CAACG,YAAY,CAAC,CAAC,CAACQ,OAAO,CAACR,YAAY,CAAC;EACnE,CAAC,CAAC;EAEFD,EAAE,CAAC,iFAAiF,EAAE,MAAM;IAC1F,MAAMU,yBAAyB,GAAG;MAChCR,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB;MAChC,CAAC;MACDC,WAAW,EAAEA,CAAA,KAAM;QACjB,OAAO;UAAEC,KAAK,EAAE;QAAQ,CAAC;MAC3B,CAAC;MACDC,OAAO,EAAE,uBAAuB;MAChCI,YAAY,EAAEA,CAAA,KAAM,UAAU;MAC9BC,eAAe,EAAEA,CAAA,KAAMC,OAAO,CAACC,OAAO,CAAC;QAAEC,SAAS,EAAE;MAAQ,CAAC,CAAC;MAC9DC,gBAAgB,EAAEA,CAAA,KAAMH,OAAO,CAACC,OAAO,CAACG,SAAS;IACnD,CAAC;IAEDT,MAAM,CAACV,qBAAqB,CAACY,yBAAyB,CAAC,CAAC,CAACD,OAAO,CAC9DC,yBACF,CAAC;EACH,CAAC,CAAC;;EAEF;AACF;AACA;EACEV,EAAE,CAAC,0CAA0C,EAAE,MAAM;IACnD;IACA,MAAMkB,cAAc,GAAG;MACrBhB,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC;IACF,CAAC;;IAED;IACAI,MAAM,CAAC,MAAMV,qBAAqB,CAACoB,cAAc,CAAC,CAAC,CAACC,OAAO,CACzD,wBACF,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -47,6 +47,11 @@ export declare class QuestionPageController extends PageController {
47
47
  protected getBackLink(request: FormContextRequest, context: FormContext): BackLink | undefined;
48
48
  makePostRouteHandler(): (request: FormRequestPayload, context: FormContext, h: Pick<ResponseToolkit, "redirect" | "view">) => Promise<import("@hapi/hapi").ResponseObject>;
49
49
  proceed(request: FormContextRequest, h: Pick<ResponseToolkit, 'redirect' | 'view'>, nextPath?: string): import("@hapi/hapi").ResponseObject;
50
+ shouldShowSaveAndReturn(): boolean;
51
+ /**
52
+ * Handle save-and-return action by processing form data and redirecting to exit page
53
+ */
54
+ handleSaveAndReturn(request: FormRequestPayload, context: FormContext, h: Pick<ResponseToolkit, 'redirect' | 'view'>): Promise<import("@hapi/hapi").ResponseObject>;
50
55
  /**
51
56
  * {@link https://hapi.dev/api/?v=20.1.2#route-options}
52
57
  */
@@ -3,6 +3,7 @@ import { ComponentCollection } from "../components/ComponentCollection.js";
3
3
  import { optionalText } from "../components/constants.js";
4
4
  import { getCacheService, getErrors, normalisePath, proceed } from "../helpers.js";
5
5
  import { PageController } from "./PageController.js";
6
+ import { FormAction } from "../../../routes/types.js";
6
7
  import { actionSchema, crumbSchema, paramsSchema } from "../../../schemas/index.js";
7
8
  import { merge } from "../../../services/cacheService.js";
8
9
  export class QuestionPageController extends PageController {
@@ -132,7 +133,8 @@ export class QuestionPageController extends PageController {
132
133
  context,
133
134
  showTitle,
134
135
  components,
135
- errors
136
+ errors,
137
+ allowSaveAndReturn: this.shouldShowSaveAndReturn()
136
138
  };
137
139
  }
138
140
  getRelevantPath(request, context) {
@@ -426,6 +428,14 @@ export class QuestionPageController extends PageController {
426
428
  return h.view(viewName, viewModel);
427
429
  }
428
430
 
431
+ // Check if this is a save-and-return action
432
+ const {
433
+ action
434
+ } = request.payload;
435
+ if (action === FormAction.SaveAndReturn) {
436
+ return this.handleSaveAndReturn(request, context, h);
437
+ }
438
+
429
439
  // Save and proceed
430
440
  await this.setState(request, state);
431
441
  return this.proceed(request, h, this.getNextPath(context));
@@ -437,6 +447,22 @@ export class QuestionPageController extends PageController {
437
447
 
438
448
  return proceed(request, h, nextUrl);
439
449
  }
450
+ shouldShowSaveAndReturn() {
451
+ return true;
452
+ }
453
+
454
+ /**
455
+ * Handle save-and-return action by processing form data and redirecting to exit page
456
+ */
457
+ async handleSaveAndReturn(request, context, h) {
458
+ const {
459
+ state
460
+ } = context;
461
+
462
+ // Save the current state and redirect to exit page
463
+ await this.setState(request, state);
464
+ return h.redirect(this.getHref('/exit'));
465
+ }
440
466
 
441
467
  /**
442
468
  * {@link https://hapi.dev/api/?v=20.1.2#route-options}
@@ -1 +1 @@
1
- {"version":3,"file":"QuestionPageController.js","names":["ComponentType","ControllerType","Engine","hasComponents","hasNext","hasRepeater","ComponentCollection","optionalText","getCacheService","getErrors","normalisePath","proceed","PageController","actionSchema","crumbSchema","paramsSchema","merge","QuestionPageController","collection","errorSummaryTitle","constructor","model","pageDef","components","page","formSchema","keys","crumb","action","next","def","filter","path","linkPath","pages","some","pagePath","allowContinue","engine","V2","controller","Terminal","length","getItemId","request","itemId","getFormParams","params","getViewModel","context","viewModel","query","payload","errors","pageTitle","showTitle","formComponents","isFormComponent","fieldset","label","isPageHeading","labelOrLegend","legend","size","classes","isOptional","fields","at","options","required","text","backLink","getBackLink","getRelevantPath","paths","startPath","getStartPath","relevantPath","getNextPath","evaluationState","summaryPath","getSummaryPath","statusPath","getStatusPath","defaultPath","undefined","pageIndex","indexOf","nextPage","slice","find","condition","conditionResult","fn","nextLink","link","conditions","getFormDataFromState","state","result","validate","abortEarly","stripUnknown","value","getStateFromValidForm","details","getState","cacheService","server","setState","mergeState","update","updated","filterConditionalComponents","filtered","component","content","type","Details","map","evaluatedComponent","Array","isArray","item","items","makeGetRouteHandler","h","viewName","getViewErrors","hasMissingNotificationEmail","view","isForceAccess","formsService","services","getFormMetadata","includes","notificationEmail","slug","returnUrl","href","backPath","endsWith","getHref","makePostRouteHandler","nextPath","nextUrl","getRouteOptions","ext","onPostHandler","method","_request","continue","postRouteOptions","parse","maxBytes","Number","MAX_SAFE_INTEGER","failAction"],"sources":["../../../../../src/server/plugins/engine/pageControllers/QuestionPageController.ts"],"sourcesContent":["import {\n ComponentType,\n ControllerType,\n Engine,\n hasComponents,\n hasNext,\n hasRepeater,\n type Link,\n type Page\n} from '@defra/forms-model'\nimport { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'\nimport { type ValidationErrorItem } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { optionalText } from '~/src/server/plugins/engine/components/constants.js'\nimport { type BackLink } from '~/src/server/plugins/engine/components/types.js'\nimport {\n getCacheService,\n getErrors,\n normalisePath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormPayloadParams,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n paramsSchema\n} from '~/src/server/schemas/index.js'\nimport { merge } from '~/src/server/services/cacheService.js'\n\nexport class QuestionPageController extends PageController {\n collection: ComponentCollection\n errorSummaryTitle = 'There is a problem'\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponents(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n\n this.collection.formSchema = this.collection.formSchema.keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n }\n\n get next(): Link[] {\n const { def, pageDef } = this\n\n if (!hasNext(pageDef)) {\n return []\n }\n\n // Remove stale links\n return pageDef.next.filter(({ path }) => {\n const linkPath = normalisePath(path)\n\n return def.pages.some((page) => {\n const pagePath = normalisePath(page.path)\n return pagePath === linkPath\n })\n })\n }\n\n get allowContinue(): boolean {\n if (this.model.engine === Engine.V2) {\n return this.pageDef.controller !== ControllerType.Terminal\n }\n\n return this.next.length > 0\n }\n\n getItemId(request?: FormContextRequest) {\n const { itemId } = this.getFormParams(request)\n return itemId ?? request?.params.itemId\n }\n\n /**\n * Used for mapping form payloads and errors to govuk-frontend's template api, so a page can be rendered\n * @param request - the hapi request\n * @param context - the form context\n */\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { collection, viewModel } = this\n const { query } = request\n const { payload, errors } = context\n\n let { pageTitle, showTitle } = viewModel\n\n const components = collection.getViewModel(payload, errors, query)\n const formComponents = components.filter(\n ({ isFormComponent }) => isFormComponent\n )\n\n // Single form component? Hide title and customise label or legend instead\n if (formComponents.length === 1) {\n const { model } = formComponents[0]\n const { fieldset, label } = model\n\n // Set as page heading when not following other content\n const isPageHeading = formComponents[0] === components[0]\n\n // Check for legend or label\n const labelOrLegend = fieldset?.legend ?? label\n\n // Use legend or label as page heading\n if (labelOrLegend) {\n const size = isPageHeading ? 'l' : 'm'\n\n labelOrLegend.classes =\n labelOrLegend === label\n ? `govuk-label--${size}`\n : `govuk-fieldset__legend--${size}`\n\n if (isPageHeading) {\n labelOrLegend.isPageHeading = isPageHeading\n\n // Check for optional in label\n const isOptional =\n this.collection.fields.at(0)?.options.required === false\n\n if (pageTitle) {\n labelOrLegend.text = isOptional\n ? `${pageTitle}${optionalText}`\n : pageTitle\n }\n\n pageTitle = pageTitle || labelOrLegend.text\n }\n }\n\n showTitle = !isPageHeading\n } else if (formComponents.length > 1) {\n // When there is more than one form component,\n // adjust the label/legends to give equal prominence\n for (const { model } of formComponents) {\n if (model.fieldset?.legend) {\n model.fieldset.legend.classes = 'govuk-fieldset__legend--m'\n }\n if (model.label) {\n model.label.classes = 'govuk-label--m'\n }\n }\n }\n\n return {\n ...viewModel,\n backLink: this.getBackLink(request, context),\n context,\n showTitle,\n components,\n errors\n }\n }\n\n getRelevantPath(\n request: FormRequest | FormRequestPayload,\n context: FormContext\n ) {\n const { paths } = context\n\n const startPath = this.getStartPath()\n const relevantPath = paths.at(-1) ?? startPath\n\n return !paths.length\n ? startPath // First possible path\n : relevantPath // Last possible path\n }\n\n /**\n * Apply conditions to evaluation state to determine next page path\n */\n getNextPath(context: FormContext) {\n const { model, next, path } = this\n const { evaluationState } = context\n\n const summaryPath = this.getSummaryPath()\n const statusPath = this.getStatusPath()\n\n // Walk from summary page (no next links) to status page\n let defaultPath = path === summaryPath ? statusPath : undefined\n\n if (model.engine === Engine.V2) {\n if (this.pageDef.controller !== ControllerType.Terminal) {\n const { pages } = this.model\n const pageIndex = pages.indexOf(this)\n\n // The \"next\" page is the first found after the current which is\n // either unconditional or has a condition that evaluates to \"true\"\n const nextPage = pages.slice(pageIndex + 1).find((page) => {\n const { condition } = page\n\n if (condition) {\n const conditionResult = condition.fn(evaluationState)\n\n if (!conditionResult) {\n return false\n }\n }\n\n return true\n })\n\n return nextPage?.path ?? defaultPath\n } else {\n return defaultPath\n }\n }\n\n const nextLink = next.find((link) => {\n const { condition } = link\n\n if (condition) {\n return model.conditions[condition]?.fn(evaluationState) ?? false\n }\n\n defaultPath = link.path\n return false\n })\n\n return nextLink?.path ?? defaultPath\n }\n\n /**\n * Gets the form payload (from state) for this page only\n */\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ): FormPayload {\n const { collection } = this\n\n // Form params from request\n const params = this.getFormParams(request)\n\n // Form payload from state\n const payload = collection.getFormDataFromState(state)\n\n return {\n ...params,\n ...payload\n }\n }\n\n /**\n * Gets form params (from payload) for this page only\n */\n getFormParams(request?: FormContextRequest): FormPayloadParams {\n const { payload } = request ?? {}\n\n const result = paramsSchema.validate(payload, {\n abortEarly: false,\n stripUnknown: true\n })\n\n return result.value as FormPayloadParams\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ): FormState {\n return this.collection.getStateFromValidForm(payload)\n }\n\n getErrors(details?: ValidationErrorItem[]) {\n return getErrors(details)\n }\n\n async getState(request: FormRequest | FormRequestPayload) {\n const { query } = request\n\n // Skip get for preview URL direct access\n if ('force' in query) {\n return {}\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.getState(request)\n }\n\n async setState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState\n ) {\n const { query } = request\n\n // Skip set for preview URL direct access\n if ('force' in query) {\n return state\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.setState(request, state)\n }\n\n async mergeState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState,\n update: object\n ) {\n const { query } = request\n\n // Merge state before set\n const updated = merge(state, update)\n\n // Skip set for preview URL direct access\n if ('force' in query) {\n return updated\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.setState(request, updated)\n }\n\n filterConditionalComponents(\n viewModel: FormPageViewModel,\n model: FormModel,\n evaluationState: Partial<Record<string, FormStateValue>>\n ) {\n // Filter our components based on their conditions using our evaluated state\n let filtered = viewModel.components.filter((component) => {\n if (\n (!!component.model.content ||\n component.type === ComponentType.Details) &&\n component.model.condition\n ) {\n const condition = model.conditions[component.model.condition]\n return condition?.fn(evaluationState)\n }\n return true\n })\n\n /**\n * For conditional reveal components (which we no longer support until GDS resolves the related accessibility issues {@link https://github.com/alphagov/govuk-frontend/issues/1991}\n */\n filtered = filtered.map((component) => {\n const evaluatedComponent = component\n const content = evaluatedComponent.model.content\n if (Array.isArray(content)) {\n evaluatedComponent.model.content = content.filter((item) =>\n item.condition\n ? model.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n }\n // apply condition to items for radios, checkboxes etc\n const items = evaluatedComponent.model.items\n\n if (Array.isArray(items)) {\n evaluatedComponent.model.items = items.filter((item) =>\n item.condition\n ? model.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n }\n\n return evaluatedComponent\n })\n\n return filtered\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { collection, model, viewName } = this\n const { evaluationState } = context\n\n const viewModel = this.getViewModel(request, context)\n viewModel.errors = collection.getViewErrors(viewModel.errors)\n\n /**\n * Content components can be hidden based on a condition. If the condition evaluates to true, it is safe to be kept, otherwise discard it\n */\n\n // Filter our components based on their conditions using our evaluated state\n viewModel.components = this.filterConditionalComponents(\n viewModel,\n model,\n evaluationState\n )\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n async hasMissingNotificationEmail(\n request: FormRequest,\n context: FormContext\n ) {\n const { path } = this\n const { params } = request\n const { isForceAccess } = context\n\n const startPath = this.getStartPath()\n const summaryPath = this.getSummaryPath()\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Warn the user if the form has no notification email set only on start page and summary page\n if ([startPath, summaryPath].includes(path) && !isForceAccess) {\n const { notificationEmail } = await getFormMetadata(params.slug)\n return !notificationEmail\n }\n\n return false\n }\n\n /**\n * Get the back link for a given progress.\n */\n protected getBackLink(\n request: FormContextRequest,\n context: FormContext\n ): BackLink | undefined {\n const { pageDef } = this\n const { path, query } = request\n const { returnUrl } = query\n const { paths } = context\n\n const itemId = this.getItemId(request)\n\n // Check answers back link\n if (returnUrl) {\n return {\n text:\n hasRepeater(pageDef) && itemId\n ? 'Go back to add another'\n : 'Go back to check answers',\n href: returnUrl\n }\n }\n\n // Item delete pages etc\n const backPath =\n itemId && !path.endsWith(itemId)\n ? paths.at(-1) // Back to main page\n : paths.at(-2) // Back to previous page\n\n // No back link\n if (!backPath) {\n return\n }\n\n // Default back link\n return {\n text: 'Back',\n href: this.getHref(backPath)\n }\n }\n\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { collection, viewName, model } = this\n const { isForceAccess, state, evaluationState } = context\n\n /**\n * If there are any errors, render the page with the parsed errors\n * @todo Refactor to match POST REDIRECT GET pattern\n */\n if (context.errors || isForceAccess) {\n const viewModel = this.getViewModel(request, context)\n viewModel.errors = collection.getViewErrors(viewModel.errors)\n\n // Filter our components based on their conditions using our evaluated state\n viewModel.components = this.filterConditionalComponents(\n viewModel,\n model,\n evaluationState\n )\n\n return h.view(viewName, viewModel)\n }\n\n // Save and proceed\n await this.setState(request, state)\n return this.proceed(request, h, this.getNextPath(context))\n }\n }\n\n proceed(\n request: FormContextRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>,\n nextPath?: string\n ) {\n const nextUrl = nextPath\n ? this.getHref(nextPath) // Redirect to next page\n : this.href // Redirect to current page (refresh)\n\n return proceed(request, h, nextUrl)\n }\n\n /**\n * {@link https://hapi.dev/api/?v=20.1.2#route-options}\n */\n get getRouteOptions(): RouteOptions<FormRequestRefs> {\n return {\n ext: {\n onPostHandler: {\n method(_request, h) {\n return h.continue\n }\n }\n }\n }\n }\n\n /**\n * {@link https://hapi.dev/api/?v=20.1.2#route-options}\n */\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n payload: {\n parse: true,\n maxBytes: Number.MAX_SAFE_INTEGER,\n failAction: 'ignore'\n },\n ext: {\n onPostHandler: {\n method(_request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n"],"mappings":"AAAA,SACEA,aAAa,EACbC,cAAc,EACdC,MAAM,EACNC,aAAa,EACbC,OAAO,EACPC,WAAW,QAGN,oBAAoB;AAI3B,SAASC,mBAAmB;AAC5B,SAASC,YAAY;AAErB,SACEC,eAAe,EACfC,SAAS,EACTC,aAAa,EACbC,OAAO;AAGT,SAASC,cAAc;AAiBvB,SACEC,YAAY,EACZC,WAAW,EACXC,YAAY;AAEd,SAASC,KAAK;AAEd,OAAO,MAAMC,sBAAsB,SAASL,cAAc,CAAC;EACzDM,UAAU;EACVC,iBAAiB,GAAG,oBAAoB;EAExCC,WAAWA,CAACC,KAAgB,EAAEC,OAAa,EAAE;IAC3C,KAAK,CAACD,KAAK,EAAEC,OAAO,CAAC;;IAErB;IACA,IAAI,CAACJ,UAAU,GAAG,IAAIZ,mBAAmB,CACvCH,aAAa,CAACmB,OAAO,CAAC,GAAGA,OAAO,CAACC,UAAU,GAAG,EAAE,EAChD;MAAEF,KAAK;MAAEG,IAAI,EAAE;IAAK,CACtB,CAAC;IAED,IAAI,CAACN,UAAU,CAACO,UAAU,GAAG,IAAI,CAACP,UAAU,CAACO,UAAU,CAACC,IAAI,CAAC;MAC3DC,KAAK,EAAEb,WAAW;MAClBc,MAAM,EAAEf;IACV,CAAC,CAAC;EACJ;EAEA,IAAIgB,IAAIA,CAAA,EAAW;IACjB,MAAM;MAAEC,GAAG;MAAER;IAAQ,CAAC,GAAG,IAAI;IAE7B,IAAI,CAAClB,OAAO,CAACkB,OAAO,CAAC,EAAE;MACrB,OAAO,EAAE;IACX;;IAEA;IACA,OAAOA,OAAO,CAACO,IAAI,CAACE,MAAM,CAAC,CAAC;MAAEC;IAAK,CAAC,KAAK;MACvC,MAAMC,QAAQ,GAAGvB,aAAa,CAACsB,IAAI,CAAC;MAEpC,OAAOF,GAAG,CAACI,KAAK,CAACC,IAAI,CAAEX,IAAI,IAAK;QAC9B,MAAMY,QAAQ,GAAG1B,aAAa,CAACc,IAAI,CAACQ,IAAI,CAAC;QACzC,OAAOI,QAAQ,KAAKH,QAAQ;MAC9B,CAAC,CAAC;IACJ,CAAC,CAAC;EACJ;EAEA,IAAII,aAAaA,CAAA,EAAY;IAC3B,IAAI,IAAI,CAAChB,KAAK,CAACiB,MAAM,KAAKpC,MAAM,CAACqC,EAAE,EAAE;MACnC,OAAO,IAAI,CAACjB,OAAO,CAACkB,UAAU,KAAKvC,cAAc,CAACwC,QAAQ;IAC5D;IAEA,OAAO,IAAI,CAACZ,IAAI,CAACa,MAAM,GAAG,CAAC;EAC7B;EAEAC,SAASA,CAACC,OAA4B,EAAE;IACtC,MAAM;MAAEC;IAAO,CAAC,GAAG,IAAI,CAACC,aAAa,CAACF,OAAO,CAAC;IAC9C,OAAOC,MAAM,IAAID,OAAO,EAAEG,MAAM,CAACF,MAAM;EACzC;;EAEA;AACF;AACA;AACA;AACA;EACEG,YAAYA,CACVJ,OAA2B,EAC3BK,OAAoB,EACD;IACnB,MAAM;MAAE/B,UAAU;MAAEgC;IAAU,CAAC,GAAG,IAAI;IACtC,MAAM;MAAEC;IAAM,CAAC,GAAGP,OAAO;IACzB,MAAM;MAAEQ,OAAO;MAAEC;IAAO,CAAC,GAAGJ,OAAO;IAEnC,IAAI;MAAEK,SAAS;MAAEC;IAAU,CAAC,GAAGL,SAAS;IAExC,MAAM3B,UAAU,GAAGL,UAAU,CAAC8B,YAAY,CAACI,OAAO,EAAEC,MAAM,EAAEF,KAAK,CAAC;IAClE,MAAMK,cAAc,GAAGjC,UAAU,CAACQ,MAAM,CACtC,CAAC;MAAE0B;IAAgB,CAAC,KAAKA,eAC3B,CAAC;;IAED;IACA,IAAID,cAAc,CAACd,MAAM,KAAK,CAAC,EAAE;MAC/B,MAAM;QAAErB;MAAM,CAAC,GAAGmC,cAAc,CAAC,CAAC,CAAC;MACnC,MAAM;QAAEE,QAAQ;QAAEC;MAAM,CAAC,GAAGtC,KAAK;;MAEjC;MACA,MAAMuC,aAAa,GAAGJ,cAAc,CAAC,CAAC,CAAC,KAAKjC,UAAU,CAAC,CAAC,CAAC;;MAEzD;MACA,MAAMsC,aAAa,GAAGH,QAAQ,EAAEI,MAAM,IAAIH,KAAK;;MAE/C;MACA,IAAIE,aAAa,EAAE;QACjB,MAAME,IAAI,GAAGH,aAAa,GAAG,GAAG,GAAG,GAAG;QAEtCC,aAAa,CAACG,OAAO,GACnBH,aAAa,KAAKF,KAAK,GACnB,gBAAgBI,IAAI,EAAE,GACtB,2BAA2BA,IAAI,EAAE;QAEvC,IAAIH,aAAa,EAAE;UACjBC,aAAa,CAACD,aAAa,GAAGA,aAAa;;UAE3C;UACA,MAAMK,UAAU,GACd,IAAI,CAAC/C,UAAU,CAACgD,MAAM,CAACC,EAAE,CAAC,CAAC,CAAC,EAAEC,OAAO,CAACC,QAAQ,KAAK,KAAK;UAE1D,IAAIf,SAAS,EAAE;YACbO,aAAa,CAACS,IAAI,GAAGL,UAAU,GAC3B,GAAGX,SAAS,GAAG/C,YAAY,EAAE,GAC7B+C,SAAS;UACf;UAEAA,SAAS,GAAGA,SAAS,IAAIO,aAAa,CAACS,IAAI;QAC7C;MACF;MAEAf,SAAS,GAAG,CAACK,aAAa;IAC5B,CAAC,MAAM,IAAIJ,cAAc,CAACd,MAAM,GAAG,CAAC,EAAE;MACpC;MACA;MACA,KAAK,MAAM;QAAErB;MAAM,CAAC,IAAImC,cAAc,EAAE;QACtC,IAAInC,KAAK,CAACqC,QAAQ,EAAEI,MAAM,EAAE;UAC1BzC,KAAK,CAACqC,QAAQ,CAACI,MAAM,CAACE,OAAO,GAAG,2BAA2B;QAC7D;QACA,IAAI3C,KAAK,CAACsC,KAAK,EAAE;UACftC,KAAK,CAACsC,KAAK,CAACK,OAAO,GAAG,gBAAgB;QACxC;MACF;IACF;IAEA,OAAO;MACL,GAAGd,SAAS;MACZqB,QAAQ,EAAE,IAAI,CAACC,WAAW,CAAC5B,OAAO,EAAEK,OAAO,CAAC;MAC5CA,OAAO;MACPM,SAAS;MACThC,UAAU;MACV8B;IACF,CAAC;EACH;EAEAoB,eAAeA,CACb7B,OAAyC,EACzCK,OAAoB,EACpB;IACA,MAAM;MAAEyB;IAAM,CAAC,GAAGzB,OAAO;IAEzB,MAAM0B,SAAS,GAAG,IAAI,CAACC,YAAY,CAAC,CAAC;IACrC,MAAMC,YAAY,GAAGH,KAAK,CAACP,EAAE,CAAC,CAAC,CAAC,CAAC,IAAIQ,SAAS;IAE9C,OAAO,CAACD,KAAK,CAAChC,MAAM,GAChBiC,SAAS,CAAC;IAAA,EACVE,YAAY,EAAC;EACnB;;EAEA;AACF;AACA;EACEC,WAAWA,CAAC7B,OAAoB,EAAE;IAChC,MAAM;MAAE5B,KAAK;MAAEQ,IAAI;MAAEG;IAAK,CAAC,GAAG,IAAI;IAClC,MAAM;MAAE+C;IAAgB,CAAC,GAAG9B,OAAO;IAEnC,MAAM+B,WAAW,GAAG,IAAI,CAACC,cAAc,CAAC,CAAC;IACzC,MAAMC,UAAU,GAAG,IAAI,CAACC,aAAa,CAAC,CAAC;;IAEvC;IACA,IAAIC,WAAW,GAAGpD,IAAI,KAAKgD,WAAW,GAAGE,UAAU,GAAGG,SAAS;IAE/D,IAAIhE,KAAK,CAACiB,MAAM,KAAKpC,MAAM,CAACqC,EAAE,EAAE;MAC9B,IAAI,IAAI,CAACjB,OAAO,CAACkB,UAAU,KAAKvC,cAAc,CAACwC,QAAQ,EAAE;QACvD,MAAM;UAAEP;QAAM,CAAC,GAAG,IAAI,CAACb,KAAK;QAC5B,MAAMiE,SAAS,GAAGpD,KAAK,CAACqD,OAAO,CAAC,IAAI,CAAC;;QAErC;QACA;QACA,MAAMC,QAAQ,GAAGtD,KAAK,CAACuD,KAAK,CAACH,SAAS,GAAG,CAAC,CAAC,CAACI,IAAI,CAAElE,IAAI,IAAK;UACzD,MAAM;YAAEmE;UAAU,CAAC,GAAGnE,IAAI;UAE1B,IAAImE,SAAS,EAAE;YACb,MAAMC,eAAe,GAAGD,SAAS,CAACE,EAAE,CAACd,eAAe,CAAC;YAErD,IAAI,CAACa,eAAe,EAAE;cACpB,OAAO,KAAK;YACd;UACF;UAEA,OAAO,IAAI;QACb,CAAC,CAAC;QAEF,OAAOJ,QAAQ,EAAExD,IAAI,IAAIoD,WAAW;MACtC,CAAC,MAAM;QACL,OAAOA,WAAW;MACpB;IACF;IAEA,MAAMU,QAAQ,GAAGjE,IAAI,CAAC6D,IAAI,CAAEK,IAAI,IAAK;MACnC,MAAM;QAAEJ;MAAU,CAAC,GAAGI,IAAI;MAE1B,IAAIJ,SAAS,EAAE;QACb,OAAOtE,KAAK,CAAC2E,UAAU,CAACL,SAAS,CAAC,EAAEE,EAAE,CAACd,eAAe,CAAC,IAAI,KAAK;MAClE;MAEAK,WAAW,GAAGW,IAAI,CAAC/D,IAAI;MACvB,OAAO,KAAK;IACd,CAAC,CAAC;IAEF,OAAO8D,QAAQ,EAAE9D,IAAI,IAAIoD,WAAW;EACtC;;EAEA;AACF;AACA;EACEa,oBAAoBA,CAClBrD,OAAuC,EACvCsD,KAA0B,EACb;IACb,MAAM;MAAEhF;IAAW,CAAC,GAAG,IAAI;;IAE3B;IACA,MAAM6B,MAAM,GAAG,IAAI,CAACD,aAAa,CAACF,OAAO,CAAC;;IAE1C;IACA,MAAMQ,OAAO,GAAGlC,UAAU,CAAC+E,oBAAoB,CAACC,KAAK,CAAC;IAEtD,OAAO;MACL,GAAGnD,MAAM;MACT,GAAGK;IACL,CAAC;EACH;;EAEA;AACF;AACA;EACEN,aAAaA,CAACF,OAA4B,EAAqB;IAC7D,MAAM;MAAEQ;IAAQ,CAAC,GAAGR,OAAO,IAAI,CAAC,CAAC;IAEjC,MAAMuD,MAAM,GAAGpF,YAAY,CAACqF,QAAQ,CAAChD,OAAO,EAAE;MAC5CiD,UAAU,EAAE,KAAK;MACjBC,YAAY,EAAE;IAChB,CAAC,CAAC;IAEF,OAAOH,MAAM,CAACI,KAAK;EACrB;EAEAC,qBAAqBA,CACnB5D,OAA2B,EAC3BsD,KAA0B,EAC1B9C,OAAoB,EACT;IACX,OAAO,IAAI,CAAClC,UAAU,CAACsF,qBAAqB,CAACpD,OAAO,CAAC;EACvD;EAEA3C,SAASA,CAACgG,OAA+B,EAAE;IACzC,OAAOhG,SAAS,CAACgG,OAAO,CAAC;EAC3B;EAEA,MAAMC,QAAQA,CAAC9D,OAAyC,EAAE;IACxD,MAAM;MAAEO;IAAM,CAAC,GAAGP,OAAO;;IAEzB;IACA,IAAI,OAAO,IAAIO,KAAK,EAAE;MACpB,OAAO,CAAC,CAAC;IACX;IAEA,MAAMwD,YAAY,GAAGnG,eAAe,CAACoC,OAAO,CAACgE,MAAM,CAAC;IAEpD,OAAOD,YAAY,CAACD,QAAQ,CAAC9D,OAAO,CAAC;EACvC;EAEA,MAAMiE,QAAQA,CACZjE,OAAyC,EACzCsD,KAA0B,EAC1B;IACA,MAAM;MAAE/C;IAAM,CAAC,GAAGP,OAAO;;IAEzB;IACA,IAAI,OAAO,IAAIO,KAAK,EAAE;MACpB,OAAO+C,KAAK;IACd;IAEA,MAAMS,YAAY,GAAGnG,eAAe,CAACoC,OAAO,CAACgE,MAAM,CAAC;IAEpD,OAAOD,YAAY,CAACE,QAAQ,CAACjE,OAAO,EAAEsD,KAAK,CAAC;EAC9C;EAEA,MAAMY,UAAUA,CACdlE,OAAyC,EACzCsD,KAA0B,EAC1Ba,MAAc,EACd;IACA,MAAM;MAAE5D;IAAM,CAAC,GAAGP,OAAO;;IAEzB;IACA,MAAMoE,OAAO,GAAGhG,KAAK,CAACkF,KAAK,EAAEa,MAAM,CAAC;;IAEpC;IACA,IAAI,OAAO,IAAI5D,KAAK,EAAE;MACpB,OAAO6D,OAAO;IAChB;IAEA,MAAML,YAAY,GAAGnG,eAAe,CAACoC,OAAO,CAACgE,MAAM,CAAC;IAEpD,OAAOD,YAAY,CAACE,QAAQ,CAACjE,OAAO,EAAEoE,OAAO,CAAC;EAChD;EAEAC,2BAA2BA,CACzB/D,SAA4B,EAC5B7B,KAAgB,EAChB0D,eAAwD,EACxD;IACA;IACA,IAAImC,QAAQ,GAAGhE,SAAS,CAAC3B,UAAU,CAACQ,MAAM,CAAEoF,SAAS,IAAK;MACxD,IACE,CAAC,CAAC,CAACA,SAAS,CAAC9F,KAAK,CAAC+F,OAAO,IACxBD,SAAS,CAACE,IAAI,KAAKrH,aAAa,CAACsH,OAAO,KAC1CH,SAAS,CAAC9F,KAAK,CAACsE,SAAS,EACzB;QACA,MAAMA,SAAS,GAAGtE,KAAK,CAAC2E,UAAU,CAACmB,SAAS,CAAC9F,KAAK,CAACsE,SAAS,CAAC;QAC7D,OAAOA,SAAS,EAAEE,EAAE,CAACd,eAAe,CAAC;MACvC;MACA,OAAO,IAAI;IACb,CAAC,CAAC;;IAEF;AACJ;AACA;IACImC,QAAQ,GAAGA,QAAQ,CAACK,GAAG,CAAEJ,SAAS,IAAK;MACrC,MAAMK,kBAAkB,GAAGL,SAAS;MACpC,MAAMC,OAAO,GAAGI,kBAAkB,CAACnG,KAAK,CAAC+F,OAAO;MAChD,IAAIK,KAAK,CAACC,OAAO,CAACN,OAAO,CAAC,EAAE;QAC1BI,kBAAkB,CAACnG,KAAK,CAAC+F,OAAO,GAAGA,OAAO,CAACrF,MAAM,CAAE4F,IAAI,IACrDA,IAAI,CAAChC,SAAS,GACVtE,KAAK,CAAC2E,UAAU,CAAC2B,IAAI,CAAChC,SAAS,CAAC,EAAEE,EAAE,CAACd,eAAe,CAAC,GACrD,IACN,CAAC;MACH;MACA;MACA,MAAM6C,KAAK,GAAGJ,kBAAkB,CAACnG,KAAK,CAACuG,KAAK;MAE5C,IAAIH,KAAK,CAACC,OAAO,CAACE,KAAK,CAAC,EAAE;QACxBJ,kBAAkB,CAACnG,KAAK,CAACuG,KAAK,GAAGA,KAAK,CAAC7F,MAAM,CAAE4F,IAAI,IACjDA,IAAI,CAAChC,SAAS,GACVtE,KAAK,CAAC2E,UAAU,CAAC2B,IAAI,CAAChC,SAAS,CAAC,EAAEE,EAAE,CAACd,eAAe,CAAC,GACrD,IACN,CAAC;MACH;MAEA,OAAOyC,kBAAkB;IAC3B,CAAC,CAAC;IAEF,OAAON,QAAQ;EACjB;EAEAW,mBAAmBA,CAAA,EAAG;IACpB,OAAO,OACLjF,OAAoB,EACpBK,OAAoB,EACpB6E,CAA6C,KAC1C;MACH,MAAM;QAAE5G,UAAU;QAAEG,KAAK;QAAE0G;MAAS,CAAC,GAAG,IAAI;MAC5C,MAAM;QAAEhD;MAAgB,CAAC,GAAG9B,OAAO;MAEnC,MAAMC,SAAS,GAAG,IAAI,CAACF,YAAY,CAACJ,OAAO,EAAEK,OAAO,CAAC;MACrDC,SAAS,CAACG,MAAM,GAAGnC,UAAU,CAAC8G,aAAa,CAAC9E,SAAS,CAACG,MAAM,CAAC;;MAE7D;AACN;AACA;;MAEM;MACAH,SAAS,CAAC3B,UAAU,GAAG,IAAI,CAAC0F,2BAA2B,CACrD/D,SAAS,EACT7B,KAAK,EACL0D,eACF,CAAC;MAED7B,SAAS,CAAC+E,2BAA2B,GACnC,MAAM,IAAI,CAACA,2BAA2B,CAACrF,OAAO,EAAEK,OAAO,CAAC;MAE1D,OAAO6E,CAAC,CAACI,IAAI,CAACH,QAAQ,EAAE7E,SAAS,CAAC;IACpC,CAAC;EACH;EAEA,MAAM+E,2BAA2BA,CAC/BrF,OAAoB,EACpBK,OAAoB,EACpB;IACA,MAAM;MAAEjB;IAAK,CAAC,GAAG,IAAI;IACrB,MAAM;MAAEe;IAAO,CAAC,GAAGH,OAAO;IAC1B,MAAM;MAAEuF;IAAc,CAAC,GAAGlF,OAAO;IAEjC,MAAM0B,SAAS,GAAG,IAAI,CAACC,YAAY,CAAC,CAAC;IACrC,MAAMI,WAAW,GAAG,IAAI,CAACC,cAAc,CAAC,CAAC;IACzC,MAAM;MAAEmD;IAAa,CAAC,GAAG,IAAI,CAAC/G,KAAK,CAACgH,QAAQ;IAC5C,MAAM;MAAEC;IAAgB,CAAC,GAAGF,YAAY;;IAExC;IACA,IAAI,CAACzD,SAAS,EAAEK,WAAW,CAAC,CAACuD,QAAQ,CAACvG,IAAI,CAAC,IAAI,CAACmG,aAAa,EAAE;MAC7D,MAAM;QAAEK;MAAkB,CAAC,GAAG,MAAMF,eAAe,CAACvF,MAAM,CAAC0F,IAAI,CAAC;MAChE,OAAO,CAACD,iBAAiB;IAC3B;IAEA,OAAO,KAAK;EACd;;EAEA;AACF;AACA;EACYhE,WAAWA,CACnB5B,OAA2B,EAC3BK,OAAoB,EACE;IACtB,MAAM;MAAE3B;IAAQ,CAAC,GAAG,IAAI;IACxB,MAAM;MAAEU,IAAI;MAAEmB;IAAM,CAAC,GAAGP,OAAO;IAC/B,MAAM;MAAE8F;IAAU,CAAC,GAAGvF,KAAK;IAC3B,MAAM;MAAEuB;IAAM,CAAC,GAAGzB,OAAO;IAEzB,MAAMJ,MAAM,GAAG,IAAI,CAACF,SAAS,CAACC,OAAO,CAAC;;IAEtC;IACA,IAAI8F,SAAS,EAAE;MACb,OAAO;QACLpE,IAAI,EACFjE,WAAW,CAACiB,OAAO,CAAC,IAAIuB,MAAM,GAC1B,wBAAwB,GACxB,0BAA0B;QAChC8F,IAAI,EAAED;MACR,CAAC;IACH;;IAEA;IACA,MAAME,QAAQ,GACZ/F,MAAM,IAAI,CAACb,IAAI,CAAC6G,QAAQ,CAAChG,MAAM,CAAC,GAC5B6B,KAAK,CAACP,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,EACbO,KAAK,CAACP,EAAE,CAAC,CAAC,CAAC,CAAC,EAAC;;IAEnB;IACA,IAAI,CAACyE,QAAQ,EAAE;MACb;IACF;;IAEA;IACA,OAAO;MACLtE,IAAI,EAAE,MAAM;MACZqE,IAAI,EAAE,IAAI,CAACG,OAAO,CAACF,QAAQ;IAC7B,CAAC;EACH;EAEAG,oBAAoBA,CAAA,EAAG;IACrB,OAAO,OACLnG,OAA2B,EAC3BK,OAAoB,EACpB6E,CAA6C,KAC1C;MACH,MAAM;QAAE5G,UAAU;QAAE6G,QAAQ;QAAE1G;MAAM,CAAC,GAAG,IAAI;MAC5C,MAAM;QAAE8G,aAAa;QAAEjC,KAAK;QAAEnB;MAAgB,CAAC,GAAG9B,OAAO;;MAEzD;AACN;AACA;AACA;MACM,IAAIA,OAAO,CAACI,MAAM,IAAI8E,aAAa,EAAE;QACnC,MAAMjF,SAAS,GAAG,IAAI,CAACF,YAAY,CAACJ,OAAO,EAAEK,OAAO,CAAC;QACrDC,SAAS,CAACG,MAAM,GAAGnC,UAAU,CAAC8G,aAAa,CAAC9E,SAAS,CAACG,MAAM,CAAC;;QAE7D;QACAH,SAAS,CAAC3B,UAAU,GAAG,IAAI,CAAC0F,2BAA2B,CACrD/D,SAAS,EACT7B,KAAK,EACL0D,eACF,CAAC;QAED,OAAO+C,CAAC,CAACI,IAAI,CAACH,QAAQ,EAAE7E,SAAS,CAAC;MACpC;;MAEA;MACA,MAAM,IAAI,CAAC2D,QAAQ,CAACjE,OAAO,EAAEsD,KAAK,CAAC;MACnC,OAAO,IAAI,CAACvF,OAAO,CAACiC,OAAO,EAAEkF,CAAC,EAAE,IAAI,CAAChD,WAAW,CAAC7B,OAAO,CAAC,CAAC;IAC5D,CAAC;EACH;EAEAtC,OAAOA,CACLiC,OAA2B,EAC3BkF,CAA6C,EAC7CkB,QAAiB,EACjB;IACA,MAAMC,OAAO,GAAGD,QAAQ,GACpB,IAAI,CAACF,OAAO,CAACE,QAAQ,CAAC,CAAC;IAAA,EACvB,IAAI,CAACL,IAAI,EAAC;;IAEd,OAAOhI,OAAO,CAACiC,OAAO,EAAEkF,CAAC,EAAEmB,OAAO,CAAC;EACrC;;EAEA;AACF;AACA;EACE,IAAIC,eAAeA,CAAA,EAAkC;IACnD,OAAO;MACLC,GAAG,EAAE;QACHC,aAAa,EAAE;UACbC,MAAMA,CAACC,QAAQ,EAAExB,CAAC,EAAE;YAClB,OAAOA,CAAC,CAACyB,QAAQ;UACnB;QACF;MACF;IACF,CAAC;EACH;;EAEA;AACF;AACA;EACE,IAAIC,gBAAgBA,CAAA,EAAyC;IAC3D,OAAO;MACLpG,OAAO,EAAE;QACPqG,KAAK,EAAE,IAAI;QACXC,QAAQ,EAAEC,MAAM,CAACC,gBAAgB;QACjCC,UAAU,EAAE;MACd,CAAC;MACDV,GAAG,EAAE;QACHC,aAAa,EAAE;UACbC,MAAMA,CAACC,QAAQ,EAAExB,CAAC,EAAE;YAClB,OAAOA,CAAC,CAACyB,QAAQ;UACnB;QACF;MACF;IACF,CAAC;EACH;AACF","ignoreList":[]}
1
+ {"version":3,"file":"QuestionPageController.js","names":["ComponentType","ControllerType","Engine","hasComponents","hasNext","hasRepeater","ComponentCollection","optionalText","getCacheService","getErrors","normalisePath","proceed","PageController","FormAction","actionSchema","crumbSchema","paramsSchema","merge","QuestionPageController","collection","errorSummaryTitle","constructor","model","pageDef","components","page","formSchema","keys","crumb","action","next","def","filter","path","linkPath","pages","some","pagePath","allowContinue","engine","V2","controller","Terminal","length","getItemId","request","itemId","getFormParams","params","getViewModel","context","viewModel","query","payload","errors","pageTitle","showTitle","formComponents","isFormComponent","fieldset","label","isPageHeading","labelOrLegend","legend","size","classes","isOptional","fields","at","options","required","text","backLink","getBackLink","allowSaveAndReturn","shouldShowSaveAndReturn","getRelevantPath","paths","startPath","getStartPath","relevantPath","getNextPath","evaluationState","summaryPath","getSummaryPath","statusPath","getStatusPath","defaultPath","undefined","pageIndex","indexOf","nextPage","slice","find","condition","conditionResult","fn","nextLink","link","conditions","getFormDataFromState","state","result","validate","abortEarly","stripUnknown","value","getStateFromValidForm","details","getState","cacheService","server","setState","mergeState","update","updated","filterConditionalComponents","filtered","component","content","type","Details","map","evaluatedComponent","Array","isArray","item","items","makeGetRouteHandler","h","viewName","getViewErrors","hasMissingNotificationEmail","view","isForceAccess","formsService","services","getFormMetadata","includes","notificationEmail","slug","returnUrl","href","backPath","endsWith","getHref","makePostRouteHandler","SaveAndReturn","handleSaveAndReturn","nextPath","nextUrl","redirect","getRouteOptions","ext","onPostHandler","method","_request","continue","postRouteOptions","parse","maxBytes","Number","MAX_SAFE_INTEGER","failAction"],"sources":["../../../../../src/server/plugins/engine/pageControllers/QuestionPageController.ts"],"sourcesContent":["import {\n ComponentType,\n ControllerType,\n Engine,\n hasComponents,\n hasNext,\n hasRepeater,\n type Link,\n type Page\n} from '@defra/forms-model'\nimport { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'\nimport { type ValidationErrorItem } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { optionalText } from '~/src/server/plugins/engine/components/constants.js'\nimport { type BackLink } from '~/src/server/plugins/engine/components/types.js'\nimport {\n getCacheService,\n getErrors,\n normalisePath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormPayloadParams,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n paramsSchema\n} from '~/src/server/schemas/index.js'\nimport { merge } from '~/src/server/services/cacheService.js'\n\nexport class QuestionPageController extends PageController {\n collection: ComponentCollection\n errorSummaryTitle = 'There is a problem'\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponents(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n\n this.collection.formSchema = this.collection.formSchema.keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n }\n\n get next(): Link[] {\n const { def, pageDef } = this\n\n if (!hasNext(pageDef)) {\n return []\n }\n\n // Remove stale links\n return pageDef.next.filter(({ path }) => {\n const linkPath = normalisePath(path)\n\n return def.pages.some((page) => {\n const pagePath = normalisePath(page.path)\n return pagePath === linkPath\n })\n })\n }\n\n get allowContinue(): boolean {\n if (this.model.engine === Engine.V2) {\n return this.pageDef.controller !== ControllerType.Terminal\n }\n\n return this.next.length > 0\n }\n\n getItemId(request?: FormContextRequest) {\n const { itemId } = this.getFormParams(request)\n return itemId ?? request?.params.itemId\n }\n\n /**\n * Used for mapping form payloads and errors to govuk-frontend's template api, so a page can be rendered\n * @param request - the hapi request\n * @param context - the form context\n */\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { collection, viewModel } = this\n const { query } = request\n const { payload, errors } = context\n\n let { pageTitle, showTitle } = viewModel\n\n const components = collection.getViewModel(payload, errors, query)\n const formComponents = components.filter(\n ({ isFormComponent }) => isFormComponent\n )\n\n // Single form component? Hide title and customise label or legend instead\n if (formComponents.length === 1) {\n const { model } = formComponents[0]\n const { fieldset, label } = model\n\n // Set as page heading when not following other content\n const isPageHeading = formComponents[0] === components[0]\n\n // Check for legend or label\n const labelOrLegend = fieldset?.legend ?? label\n\n // Use legend or label as page heading\n if (labelOrLegend) {\n const size = isPageHeading ? 'l' : 'm'\n\n labelOrLegend.classes =\n labelOrLegend === label\n ? `govuk-label--${size}`\n : `govuk-fieldset__legend--${size}`\n\n if (isPageHeading) {\n labelOrLegend.isPageHeading = isPageHeading\n\n // Check for optional in label\n const isOptional =\n this.collection.fields.at(0)?.options.required === false\n\n if (pageTitle) {\n labelOrLegend.text = isOptional\n ? `${pageTitle}${optionalText}`\n : pageTitle\n }\n\n pageTitle = pageTitle || labelOrLegend.text\n }\n }\n\n showTitle = !isPageHeading\n } else if (formComponents.length > 1) {\n // When there is more than one form component,\n // adjust the label/legends to give equal prominence\n for (const { model } of formComponents) {\n if (model.fieldset?.legend) {\n model.fieldset.legend.classes = 'govuk-fieldset__legend--m'\n }\n if (model.label) {\n model.label.classes = 'govuk-label--m'\n }\n }\n }\n\n return {\n ...viewModel,\n backLink: this.getBackLink(request, context),\n context,\n showTitle,\n components,\n errors,\n allowSaveAndReturn: this.shouldShowSaveAndReturn()\n }\n }\n\n getRelevantPath(\n request: FormRequest | FormRequestPayload,\n context: FormContext\n ) {\n const { paths } = context\n\n const startPath = this.getStartPath()\n const relevantPath = paths.at(-1) ?? startPath\n\n return !paths.length\n ? startPath // First possible path\n : relevantPath // Last possible path\n }\n\n /**\n * Apply conditions to evaluation state to determine next page path\n */\n getNextPath(context: FormContext) {\n const { model, next, path } = this\n const { evaluationState } = context\n\n const summaryPath = this.getSummaryPath()\n const statusPath = this.getStatusPath()\n\n // Walk from summary page (no next links) to status page\n let defaultPath = path === summaryPath ? statusPath : undefined\n\n if (model.engine === Engine.V2) {\n if (this.pageDef.controller !== ControllerType.Terminal) {\n const { pages } = this.model\n const pageIndex = pages.indexOf(this)\n\n // The \"next\" page is the first found after the current which is\n // either unconditional or has a condition that evaluates to \"true\"\n const nextPage = pages.slice(pageIndex + 1).find((page) => {\n const { condition } = page\n\n if (condition) {\n const conditionResult = condition.fn(evaluationState)\n\n if (!conditionResult) {\n return false\n }\n }\n\n return true\n })\n\n return nextPage?.path ?? defaultPath\n } else {\n return defaultPath\n }\n }\n\n const nextLink = next.find((link) => {\n const { condition } = link\n\n if (condition) {\n return model.conditions[condition]?.fn(evaluationState) ?? false\n }\n\n defaultPath = link.path\n return false\n })\n\n return nextLink?.path ?? defaultPath\n }\n\n /**\n * Gets the form payload (from state) for this page only\n */\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ): FormPayload {\n const { collection } = this\n\n // Form params from request\n const params = this.getFormParams(request)\n\n // Form payload from state\n const payload = collection.getFormDataFromState(state)\n\n return {\n ...params,\n ...payload\n }\n }\n\n /**\n * Gets form params (from payload) for this page only\n */\n getFormParams(request?: FormContextRequest): FormPayloadParams {\n const { payload } = request ?? {}\n\n const result = paramsSchema.validate(payload, {\n abortEarly: false,\n stripUnknown: true\n })\n\n return result.value as FormPayloadParams\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ): FormState {\n return this.collection.getStateFromValidForm(payload)\n }\n\n getErrors(details?: ValidationErrorItem[]) {\n return getErrors(details)\n }\n\n async getState(request: FormRequest | FormRequestPayload) {\n const { query } = request\n\n // Skip get for preview URL direct access\n if ('force' in query) {\n return {}\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.getState(request)\n }\n\n async setState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState\n ) {\n const { query } = request\n\n // Skip set for preview URL direct access\n if ('force' in query) {\n return state\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.setState(request, state)\n }\n\n async mergeState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState,\n update: object\n ) {\n const { query } = request\n\n // Merge state before set\n const updated = merge(state, update)\n\n // Skip set for preview URL direct access\n if ('force' in query) {\n return updated\n }\n\n const cacheService = getCacheService(request.server)\n\n return cacheService.setState(request, updated)\n }\n\n filterConditionalComponents(\n viewModel: FormPageViewModel,\n model: FormModel,\n evaluationState: Partial<Record<string, FormStateValue>>\n ) {\n // Filter our components based on their conditions using our evaluated state\n let filtered = viewModel.components.filter((component) => {\n if (\n (!!component.model.content ||\n component.type === ComponentType.Details) &&\n component.model.condition\n ) {\n const condition = model.conditions[component.model.condition]\n return condition?.fn(evaluationState)\n }\n return true\n })\n\n /**\n * For conditional reveal components (which we no longer support until GDS resolves the related accessibility issues {@link https://github.com/alphagov/govuk-frontend/issues/1991}\n */\n filtered = filtered.map((component) => {\n const evaluatedComponent = component\n const content = evaluatedComponent.model.content\n if (Array.isArray(content)) {\n evaluatedComponent.model.content = content.filter((item) =>\n item.condition\n ? model.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n }\n // apply condition to items for radios, checkboxes etc\n const items = evaluatedComponent.model.items\n\n if (Array.isArray(items)) {\n evaluatedComponent.model.items = items.filter((item) =>\n item.condition\n ? model.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n }\n\n return evaluatedComponent\n })\n\n return filtered\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { collection, model, viewName } = this\n const { evaluationState } = context\n\n const viewModel = this.getViewModel(request, context)\n viewModel.errors = collection.getViewErrors(viewModel.errors)\n\n /**\n * Content components can be hidden based on a condition. If the condition evaluates to true, it is safe to be kept, otherwise discard it\n */\n\n // Filter our components based on their conditions using our evaluated state\n viewModel.components = this.filterConditionalComponents(\n viewModel,\n model,\n evaluationState\n )\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n async hasMissingNotificationEmail(\n request: FormRequest,\n context: FormContext\n ) {\n const { path } = this\n const { params } = request\n const { isForceAccess } = context\n\n const startPath = this.getStartPath()\n const summaryPath = this.getSummaryPath()\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Warn the user if the form has no notification email set only on start page and summary page\n if ([startPath, summaryPath].includes(path) && !isForceAccess) {\n const { notificationEmail } = await getFormMetadata(params.slug)\n return !notificationEmail\n }\n\n return false\n }\n\n /**\n * Get the back link for a given progress.\n */\n protected getBackLink(\n request: FormContextRequest,\n context: FormContext\n ): BackLink | undefined {\n const { pageDef } = this\n const { path, query } = request\n const { returnUrl } = query\n const { paths } = context\n\n const itemId = this.getItemId(request)\n\n // Check answers back link\n if (returnUrl) {\n return {\n text:\n hasRepeater(pageDef) && itemId\n ? 'Go back to add another'\n : 'Go back to check answers',\n href: returnUrl\n }\n }\n\n // Item delete pages etc\n const backPath =\n itemId && !path.endsWith(itemId)\n ? paths.at(-1) // Back to main page\n : paths.at(-2) // Back to previous page\n\n // No back link\n if (!backPath) {\n return\n }\n\n // Default back link\n return {\n text: 'Back',\n href: this.getHref(backPath)\n }\n }\n\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { collection, viewName, model } = this\n const { isForceAccess, state, evaluationState } = context\n\n /**\n * If there are any errors, render the page with the parsed errors\n * @todo Refactor to match POST REDIRECT GET pattern\n */\n if (context.errors || isForceAccess) {\n const viewModel = this.getViewModel(request, context)\n viewModel.errors = collection.getViewErrors(viewModel.errors)\n\n // Filter our components based on their conditions using our evaluated state\n viewModel.components = this.filterConditionalComponents(\n viewModel,\n model,\n evaluationState\n )\n\n return h.view(viewName, viewModel)\n }\n\n // Check if this is a save-and-return action\n const { action } = request.payload\n if (action === FormAction.SaveAndReturn) {\n return this.handleSaveAndReturn(request, context, h)\n }\n\n // Save and proceed\n await this.setState(request, state)\n return this.proceed(request, h, this.getNextPath(context))\n }\n }\n\n proceed(\n request: FormContextRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>,\n nextPath?: string\n ) {\n const nextUrl = nextPath\n ? this.getHref(nextPath) // Redirect to next page\n : this.href // Redirect to current page (refresh)\n\n return proceed(request, h, nextUrl)\n }\n\n shouldShowSaveAndReturn(): boolean {\n return true\n }\n\n /**\n * Handle save-and-return action by processing form data and redirecting to exit page\n */\n async handleSaveAndReturn(\n request: FormRequestPayload,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { state } = context\n\n // Save the current state and redirect to exit page\n await this.setState(request, state)\n return h.redirect(this.getHref('/exit'))\n }\n\n /**\n * {@link https://hapi.dev/api/?v=20.1.2#route-options}\n */\n get getRouteOptions(): RouteOptions<FormRequestRefs> {\n return {\n ext: {\n onPostHandler: {\n method(_request, h) {\n return h.continue\n }\n }\n }\n }\n }\n\n /**\n * {@link https://hapi.dev/api/?v=20.1.2#route-options}\n */\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n payload: {\n parse: true,\n maxBytes: Number.MAX_SAFE_INTEGER,\n failAction: 'ignore'\n },\n ext: {\n onPostHandler: {\n method(_request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}