@defra/forms-engine-plugin 1.0.4 → 1.0.6

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 (45) hide show
  1. package/.server/server/forms/components.json +8 -0
  2. package/.server/server/plugins/engine/components/ComponentBase.js +2 -2
  3. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  4. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  5. package/.server/server/plugins/engine/configureEnginePlugin.js +5 -2
  6. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  7. package/.server/server/plugins/engine/index.d.ts +2 -2
  8. package/.server/server/plugins/engine/index.js +5 -3
  9. package/.server/server/plugins/engine/index.js.map +1 -1
  10. package/.server/server/plugins/engine/options.js +6 -1
  11. package/.server/server/plugins/engine/options.js.map +1 -1
  12. package/.server/server/plugins/engine/options.test.js +3 -1
  13. package/.server/server/plugins/engine/options.test.js.map +1 -1
  14. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +2 -2
  15. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  16. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +1 -1
  17. package/.server/server/plugins/engine/routes/index.js +7 -1
  18. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  19. package/.server/server/plugins/engine/types.d.ts +7 -4
  20. package/.server/server/plugins/engine/types.js.map +1 -1
  21. package/.server/server/plugins/engine/vision.js +1 -1
  22. package/.server/server/plugins/engine/vision.js.map +1 -1
  23. package/.server/server/plugins/nunjucks/filters/index.d.ts +0 -1
  24. package/.server/server/plugins/nunjucks/filters/index.js +0 -1
  25. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  26. package/.server/server/schemas/index.d.ts +2 -2
  27. package/.server/server/schemas/index.js.map +1 -1
  28. package/.server/server/types.d.ts +2 -1
  29. package/.server/server/types.js.map +1 -1
  30. package/package.json +3 -2
  31. package/src/server/forms/components.json +8 -0
  32. package/src/server/index.test.ts +73 -0
  33. package/src/server/plugins/engine/components/ComponentBase.ts +2 -2
  34. package/src/server/plugins/engine/configureEnginePlugin.ts +5 -2
  35. package/src/server/plugins/engine/index.ts +9 -4
  36. package/src/server/plugins/engine/options.js +10 -1
  37. package/src/server/plugins/engine/options.test.js +3 -1
  38. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +2 -2
  39. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +3 -3
  40. package/src/server/plugins/engine/routes/index.ts +6 -1
  41. package/src/server/plugins/engine/types.ts +14 -2
  42. package/src/server/plugins/engine/vision.ts +1 -1
  43. package/src/server/plugins/nunjucks/filters/index.js +0 -1
  44. package/src/server/schemas/index.ts +2 -2
  45. package/src/server/types.ts +5 -1
@@ -112,6 +112,14 @@
112
112
  "content": "Content",
113
113
  "options": {},
114
114
  "schema": {}
115
+ },
116
+ {
117
+ "type": "Markdown",
118
+ "name": "markdown",
119
+ "title": "Title",
120
+ "content": "### This is a H3 in markdown\n\n[An internal link](http://localhost:3009/fictional-page)\n\n[An external link](https://defra.gov.uk/fictional-page)",
121
+ "options": {},
122
+ "schema": {}
115
123
  }
116
124
  ]
117
125
  }
@@ -1,4 +1,4 @@
1
- import { isConditionalType } from '@defra/forms-model';
1
+ import { isConditionalRevealType } from '@defra/forms-model';
2
2
  import joi from 'joi';
3
3
  export class ComponentBase {
4
4
  page;
@@ -46,7 +46,7 @@ export class ComponentBase {
46
46
  if ('classes' in options) {
47
47
  viewModel.classes = options.classes;
48
48
  }
49
- if ('condition' in options && isConditionalType(type)) {
49
+ if ('condition' in options && isConditionalRevealType(type)) {
50
50
  viewModel.condition = options.condition;
51
51
  }
52
52
  return viewModel;
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentBase.js","names":["isConditionalType","joi","ComponentBase","page","parent","collection","type","name","title","schema","options","isFormComponent","model","formSchema","string","stateSchema","constructor","def","props","viewModel","attributes","autocomplete","classes","condition"],"sources":["../../../../../src/server/plugins/engine/components/ComponentBase.ts"],"sourcesContent":["import { isConditionalType, type ComponentDef } from '@defra/forms-model'\nimport joi, {\n type ArraySchema,\n type BooleanSchema,\n type DateSchema,\n type NumberSchema,\n type ObjectSchema,\n type StringSchema\n} from 'joi'\n\nimport { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport { type ViewModel } from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\n\nexport class ComponentBase {\n page?: PageControllerClass\n parent: Component | undefined\n collection: ComponentCollection | undefined\n\n type: ComponentDef['type']\n name: ComponentDef['name']\n title: ComponentDef['title']\n schema?: Extract<ComponentDef, { schema: object }>['schema']\n options?: Extract<ComponentDef, { options: object }>['options']\n\n isFormComponent = false\n model: FormModel\n\n /** joi schemas based on a component defined in the form JSON. This validates a user's answer and is generated from {@link ComponentDef} */\n formSchema: ComponentSchema = joi.string()\n stateSchema: ComponentSchema = joi.string()\n\n constructor(\n def: ComponentDef,\n props: {\n page?: PageControllerClass\n parent?: Component\n model: FormModel\n }\n ) {\n this.type = def.type\n this.name = def.name\n this.title = def.title\n\n if ('schema' in def) {\n this.schema = def.schema\n }\n\n if ('options' in def) {\n this.options = def.options\n }\n\n this.page = props.page\n this.parent = props.parent\n this.model = props.model\n }\n\n get viewModel() {\n const { options, type } = this\n\n const viewModel: ViewModel = {\n attributes: {}\n }\n\n if (!options) {\n return viewModel\n }\n\n if ('autocomplete' in options) {\n viewModel.attributes.autocomplete = options.autocomplete\n }\n\n if ('classes' in options) {\n viewModel.classes = options.classes\n }\n\n if ('condition' in options && isConditionalType(type)) {\n viewModel.condition = options.condition\n }\n\n return viewModel\n }\n}\n\nexport type ComponentSchema =\n | ArraySchema<string>\n | ArraySchema<number>\n | ArraySchema<boolean>\n | ArraySchema<object>\n | BooleanSchema<string>\n | DateSchema\n | NumberSchema<string>\n | NumberSchema\n | ObjectSchema\n | StringSchema\n"],"mappings":"AAAA,SAASA,iBAAiB,QAA2B,oBAAoB;AACzE,OAAOC,GAAG,MAOH,KAAK;AAQZ,OAAO,MAAMC,aAAa,CAAC;EACzBC,IAAI;EACJC,MAAM;EACNC,UAAU;EAEVC,IAAI;EACJC,IAAI;EACJC,KAAK;EACLC,MAAM;EACNC,OAAO;EAEPC,eAAe,GAAG,KAAK;EACvBC,KAAK;;EAEL;EACAC,UAAU,GAAoBZ,GAAG,CAACa,MAAM,CAAC,CAAC;EAC1CC,WAAW,GAAoBd,GAAG,CAACa,MAAM,CAAC,CAAC;EAE3CE,WAAWA,CACTC,GAAiB,EACjBC,KAIC,EACD;IACA,IAAI,CAACZ,IAAI,GAAGW,GAAG,CAACX,IAAI;IACpB,IAAI,CAACC,IAAI,GAAGU,GAAG,CAACV,IAAI;IACpB,IAAI,CAACC,KAAK,GAAGS,GAAG,CAACT,KAAK;IAEtB,IAAI,QAAQ,IAAIS,GAAG,EAAE;MACnB,IAAI,CAACR,MAAM,GAAGQ,GAAG,CAACR,MAAM;IAC1B;IAEA,IAAI,SAAS,IAAIQ,GAAG,EAAE;MACpB,IAAI,CAACP,OAAO,GAAGO,GAAG,CAACP,OAAO;IAC5B;IAEA,IAAI,CAACP,IAAI,GAAGe,KAAK,CAACf,IAAI;IACtB,IAAI,CAACC,MAAM,GAAGc,KAAK,CAACd,MAAM;IAC1B,IAAI,CAACQ,KAAK,GAAGM,KAAK,CAACN,KAAK;EAC1B;EAEA,IAAIO,SAASA,CAAA,EAAG;IACd,MAAM;MAAET,OAAO;MAAEJ;IAAK,CAAC,GAAG,IAAI;IAE9B,MAAMa,SAAoB,GAAG;MAC3BC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,CAACV,OAAO,EAAE;MACZ,OAAOS,SAAS;IAClB;IAEA,IAAI,cAAc,IAAIT,OAAO,EAAE;MAC7BS,SAAS,CAACC,UAAU,CAACC,YAAY,GAAGX,OAAO,CAACW,YAAY;IAC1D;IAEA,IAAI,SAAS,IAAIX,OAAO,EAAE;MACxBS,SAAS,CAACG,OAAO,GAAGZ,OAAO,CAACY,OAAO;IACrC;IAEA,IAAI,WAAW,IAAIZ,OAAO,IAAIV,iBAAiB,CAACM,IAAI,CAAC,EAAE;MACrDa,SAAS,CAACI,SAAS,GAAGb,OAAO,CAACa,SAAS;IACzC;IAEA,OAAOJ,SAAS;EAClB;AACF","ignoreList":[]}
1
+ {"version":3,"file":"ComponentBase.js","names":["isConditionalRevealType","joi","ComponentBase","page","parent","collection","type","name","title","schema","options","isFormComponent","model","formSchema","string","stateSchema","constructor","def","props","viewModel","attributes","autocomplete","classes","condition"],"sources":["../../../../../src/server/plugins/engine/components/ComponentBase.ts"],"sourcesContent":["import { isConditionalRevealType, type ComponentDef } from '@defra/forms-model'\nimport joi, {\n type ArraySchema,\n type BooleanSchema,\n type DateSchema,\n type NumberSchema,\n type ObjectSchema,\n type StringSchema\n} from 'joi'\n\nimport { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport { type ViewModel } from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\n\nexport class ComponentBase {\n page?: PageControllerClass\n parent: Component | undefined\n collection: ComponentCollection | undefined\n\n type: ComponentDef['type']\n name: ComponentDef['name']\n title: ComponentDef['title']\n schema?: Extract<ComponentDef, { schema: object }>['schema']\n options?: Extract<ComponentDef, { options: object }>['options']\n\n isFormComponent = false\n model: FormModel\n\n /** joi schemas based on a component defined in the form JSON. This validates a user's answer and is generated from {@link ComponentDef} */\n formSchema: ComponentSchema = joi.string()\n stateSchema: ComponentSchema = joi.string()\n\n constructor(\n def: ComponentDef,\n props: {\n page?: PageControllerClass\n parent?: Component\n model: FormModel\n }\n ) {\n this.type = def.type\n this.name = def.name\n this.title = def.title\n\n if ('schema' in def) {\n this.schema = def.schema\n }\n\n if ('options' in def) {\n this.options = def.options\n }\n\n this.page = props.page\n this.parent = props.parent\n this.model = props.model\n }\n\n get viewModel() {\n const { options, type } = this\n\n const viewModel: ViewModel = {\n attributes: {}\n }\n\n if (!options) {\n return viewModel\n }\n\n if ('autocomplete' in options) {\n viewModel.attributes.autocomplete = options.autocomplete\n }\n\n if ('classes' in options) {\n viewModel.classes = options.classes\n }\n\n if ('condition' in options && isConditionalRevealType(type)) {\n viewModel.condition = options.condition\n }\n\n return viewModel\n }\n}\n\nexport type ComponentSchema =\n | ArraySchema<string>\n | ArraySchema<number>\n | ArraySchema<boolean>\n | ArraySchema<object>\n | BooleanSchema<string>\n | DateSchema\n | NumberSchema<string>\n | NumberSchema\n | ObjectSchema\n | StringSchema\n"],"mappings":"AAAA,SAASA,uBAAuB,QAA2B,oBAAoB;AAC/E,OAAOC,GAAG,MAOH,KAAK;AAQZ,OAAO,MAAMC,aAAa,CAAC;EACzBC,IAAI;EACJC,MAAM;EACNC,UAAU;EAEVC,IAAI;EACJC,IAAI;EACJC,KAAK;EACLC,MAAM;EACNC,OAAO;EAEPC,eAAe,GAAG,KAAK;EACvBC,KAAK;;EAEL;EACAC,UAAU,GAAoBZ,GAAG,CAACa,MAAM,CAAC,CAAC;EAC1CC,WAAW,GAAoBd,GAAG,CAACa,MAAM,CAAC,CAAC;EAE3CE,WAAWA,CACTC,GAAiB,EACjBC,KAIC,EACD;IACA,IAAI,CAACZ,IAAI,GAAGW,GAAG,CAACX,IAAI;IACpB,IAAI,CAACC,IAAI,GAAGU,GAAG,CAACV,IAAI;IACpB,IAAI,CAACC,KAAK,GAAGS,GAAG,CAACT,KAAK;IAEtB,IAAI,QAAQ,IAAIS,GAAG,EAAE;MACnB,IAAI,CAACR,MAAM,GAAGQ,GAAG,CAACR,MAAM;IAC1B;IAEA,IAAI,SAAS,IAAIQ,GAAG,EAAE;MACpB,IAAI,CAACP,OAAO,GAAGO,GAAG,CAACP,OAAO;IAC5B;IAEA,IAAI,CAACP,IAAI,GAAGe,KAAK,CAACf,IAAI;IACtB,IAAI,CAACC,MAAM,GAAGc,KAAK,CAACd,MAAM;IAC1B,IAAI,CAACQ,KAAK,GAAGM,KAAK,CAACN,KAAK;EAC1B;EAEA,IAAIO,SAASA,CAAA,EAAG;IACd,MAAM;MAAET,OAAO;MAAEJ;IAAK,CAAC,GAAG,IAAI;IAE9B,MAAMa,SAAoB,GAAG;MAC3BC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,IAAI,CAACV,OAAO,EAAE;MACZ,OAAOS,SAAS;IAClB;IAEA,IAAI,cAAc,IAAIT,OAAO,EAAE;MAC7BS,SAAS,CAACC,UAAU,CAACC,YAAY,GAAGX,OAAO,CAACW,YAAY;IAC1D;IAEA,IAAI,SAAS,IAAIX,OAAO,EAAE;MACxBS,SAAS,CAACG,OAAO,GAAGZ,OAAO,CAACY,OAAO;IACrC;IAEA,IAAI,WAAW,IAAIZ,OAAO,IAAIV,uBAAuB,CAACM,IAAI,CAAC,EAAE;MAC3Da,SAAS,CAACI,SAAS,GAAGb,OAAO,CAACa,SAAS;IACzC;IAEA,OAAOJ,SAAS;EAClB;AACF","ignoreList":[]}
@@ -2,7 +2,7 @@ import { type FormDefinition } from '@defra/forms-model';
2
2
  import { plugin } from '~/src/server/plugins/engine/plugin.js';
3
3
  import { type PluginOptions } from '~/src/server/plugins/engine/types.js';
4
4
  import { type RouteConfig } from '~/src/server/types.js';
5
- export declare const configureEnginePlugin: ({ formFileName, formFilePath, services, controllers, preparePageEventRequestOptions }?: RouteConfig) => Promise<{
5
+ export declare const configureEnginePlugin: ({ formFileName, formFilePath, services, controllers, preparePageEventRequestOptions, onRequest }?: RouteConfig) => Promise<{
6
6
  plugin: typeof plugin;
7
7
  options: PluginOptions;
8
8
  }>;
@@ -11,7 +11,8 @@ export const configureEnginePlugin = async ({
11
11
  formFilePath,
12
12
  services,
13
13
  controllers,
14
- preparePageEventRequestOptions
14
+ preparePageEventRequestOptions,
15
+ onRequest
15
16
  } = {}) => {
16
17
  let model;
17
18
  if (formFileName && formFilePath) {
@@ -40,7 +41,9 @@ export const configureEnginePlugin = async ({
40
41
  paths: [join(findPackageRoot(), 'src/server/devserver')] // custom layout to make it really clear this is not the same as the runner
41
42
  },
42
43
  viewContext: devtoolContext,
43
- preparePageEventRequestOptions
44
+ preparePageEventRequestOptions,
45
+ onRequest,
46
+ baseUrl: 'http://localhost:3009' // always runs locally
44
47
  }
45
48
  };
46
49
  };
@@ -1 +1 @@
1
- {"version":3,"file":"configureEnginePlugin.js","names":["join","parse","FORM_PREFIX","FormModel","plugin","defaultServices","formsService","findPackageRoot","devtoolContext","configureEnginePlugin","formFileName","formFilePath","services","controllers","preparePageEventRequestOptions","model","definition","getForm","name","initialBasePath","basePath","options","cacheName","nunjucks","baseLayoutPath","paths","viewContext","importPath","ext","attributes","type","formImport","with","default"],"sources":["../../../../src/server/plugins/engine/configureEnginePlugin.ts"],"sourcesContent":["import { join, parse } from 'node:path'\n\nimport { type FormDefinition } from '@defra/forms-model'\n\nimport { FORM_PREFIX } from '~/src/server/constants.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport { formsService } from '~/src/server/plugins/engine/services/localFormsService.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { findPackageRoot } from '~/src/server/plugins/engine/vision.js'\nimport { devtoolContext } from '~/src/server/plugins/nunjucks/context.js'\nimport { type RouteConfig } from '~/src/server/types.js'\n\nexport const configureEnginePlugin = async ({\n formFileName,\n formFilePath,\n services,\n controllers,\n preparePageEventRequestOptions\n}: RouteConfig = {}): Promise<{\n plugin: typeof plugin\n options: PluginOptions\n}> => {\n let model: FormModel | undefined\n\n if (formFileName && formFilePath) {\n const definition = await getForm(join(formFilePath, formFileName))\n const { name } = parse(formFileName)\n\n const initialBasePath = `${FORM_PREFIX}${name}`\n\n model = new FormModel(\n definition,\n { basePath: initialBasePath },\n services,\n controllers\n )\n }\n\n return {\n plugin,\n options: {\n model,\n services: services ?? {\n // services for testing, else use the disk loader option for running this service locally\n ...defaultServices,\n formsService: await formsService()\n },\n controllers,\n cacheName: 'session',\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: [join(findPackageRoot(), 'src/server/devserver')] // custom layout to make it really clear this is not the same as the runner\n },\n viewContext: devtoolContext,\n preparePageEventRequestOptions\n }\n }\n}\n\nexport async function getForm(importPath: string) {\n const { ext } = parse(importPath)\n\n const attributes: ImportAttributes = {\n type: ext === '.json' ? 'json' : 'module'\n }\n\n const formImport = import(importPath, { with: attributes }) as Promise<{\n default: FormDefinition\n }>\n\n const { default: definition } = await formImport\n return definition\n}\n"],"mappings":"AAAA,SAASA,IAAI,EAAEC,KAAK,QAAQ,WAAW;AAIvC,SAASC,WAAW;AACpB,SAASC,SAAS;AAClB,SAASC,MAAM;AACf,OAAO,KAAKC,eAAe;AAC3B,SAASC,YAAY;AAErB,SAASC,eAAe;AACxB,SAASC,cAAc;AAGvB,OAAO,MAAMC,qBAAqB,GAAG,MAAAA,CAAO;EAC1CC,YAAY;EACZC,YAAY;EACZC,QAAQ;EACRC,WAAW;EACXC;AACW,CAAC,GAAG,CAAC,CAAC,KAGb;EACJ,IAAIC,KAA4B;EAEhC,IAAIL,YAAY,IAAIC,YAAY,EAAE;IAChC,MAAMK,UAAU,GAAG,MAAMC,OAAO,CAACjB,IAAI,CAACW,YAAY,EAAED,YAAY,CAAC,CAAC;IAClE,MAAM;MAAEQ;IAAK,CAAC,GAAGjB,KAAK,CAACS,YAAY,CAAC;IAEpC,MAAMS,eAAe,GAAG,GAAGjB,WAAW,GAAGgB,IAAI,EAAE;IAE/CH,KAAK,GAAG,IAAIZ,SAAS,CACnBa,UAAU,EACV;MAAEI,QAAQ,EAAED;IAAgB,CAAC,EAC7BP,QAAQ,EACRC,WACF,CAAC;EACH;EAEA,OAAO;IACLT,MAAM;IACNiB,OAAO,EAAE;MACPN,KAAK;MACLH,QAAQ,EAAEA,QAAQ,IAAI;QACpB;QACA,GAAGP,eAAe;QAClBC,YAAY,EAAE,MAAMA,YAAY,CAAC;MACnC,CAAC;MACDO,WAAW;MACXS,SAAS,EAAE,SAAS;MACpBC,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAACzB,IAAI,CAACO,eAAe,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC;MAC3D,CAAC;MACDmB,WAAW,EAAElB,cAAc;MAC3BM;IACF;EACF,CAAC;AACH,CAAC;AAED,OAAO,eAAeG,OAAOA,CAACU,UAAkB,EAAE;EAChD,MAAM;IAAEC;EAAI,CAAC,GAAG3B,KAAK,CAAC0B,UAAU,CAAC;EAEjC,MAAME,UAA4B,GAAG;IACnCC,IAAI,EAAEF,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG;EACnC,CAAC;EAED,MAAMG,UAAU,GAAG,MAAM,CAACJ,UAAU,EAAE;IAAEK,IAAI,EAAEH;EAAW,CAAC,CAExD;EAEF,MAAM;IAAEI,OAAO,EAAEjB;EAAW,CAAC,GAAG,MAAMe,UAAU;EAChD,OAAOf,UAAU;AACnB","ignoreList":[]}
1
+ {"version":3,"file":"configureEnginePlugin.js","names":["join","parse","FORM_PREFIX","FormModel","plugin","defaultServices","formsService","findPackageRoot","devtoolContext","configureEnginePlugin","formFileName","formFilePath","services","controllers","preparePageEventRequestOptions","onRequest","model","definition","getForm","name","initialBasePath","basePath","options","cacheName","nunjucks","baseLayoutPath","paths","viewContext","baseUrl","importPath","ext","attributes","type","formImport","with","default"],"sources":["../../../../src/server/plugins/engine/configureEnginePlugin.ts"],"sourcesContent":["import { join, parse } from 'node:path'\n\nimport { type FormDefinition } from '@defra/forms-model'\n\nimport { FORM_PREFIX } from '~/src/server/constants.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport { formsService } from '~/src/server/plugins/engine/services/localFormsService.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { findPackageRoot } from '~/src/server/plugins/engine/vision.js'\nimport { devtoolContext } from '~/src/server/plugins/nunjucks/context.js'\nimport { type RouteConfig } from '~/src/server/types.js'\n\nexport const configureEnginePlugin = async ({\n formFileName,\n formFilePath,\n services,\n controllers,\n preparePageEventRequestOptions,\n onRequest\n}: RouteConfig = {}): Promise<{\n plugin: typeof plugin\n options: PluginOptions\n}> => {\n let model: FormModel | undefined\n\n if (formFileName && formFilePath) {\n const definition = await getForm(join(formFilePath, formFileName))\n const { name } = parse(formFileName)\n\n const initialBasePath = `${FORM_PREFIX}${name}`\n\n model = new FormModel(\n definition,\n { basePath: initialBasePath },\n services,\n controllers\n )\n }\n\n return {\n plugin,\n options: {\n model,\n services: services ?? {\n // services for testing, else use the disk loader option for running this service locally\n ...defaultServices,\n formsService: await formsService()\n },\n controllers,\n cacheName: 'session',\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: [join(findPackageRoot(), 'src/server/devserver')] // custom layout to make it really clear this is not the same as the runner\n },\n viewContext: devtoolContext,\n preparePageEventRequestOptions,\n onRequest,\n baseUrl: 'http://localhost:3009' // always runs locally\n }\n }\n}\n\nexport async function getForm(importPath: string) {\n const { ext } = parse(importPath)\n\n const attributes: ImportAttributes = {\n type: ext === '.json' ? 'json' : 'module'\n }\n\n const formImport = import(importPath, { with: attributes }) as Promise<{\n default: FormDefinition\n }>\n\n const { default: definition } = await formImport\n return definition\n}\n"],"mappings":"AAAA,SAASA,IAAI,EAAEC,KAAK,QAAQ,WAAW;AAIvC,SAASC,WAAW;AACpB,SAASC,SAAS;AAClB,SAASC,MAAM;AACf,OAAO,KAAKC,eAAe;AAC3B,SAASC,YAAY;AAErB,SAASC,eAAe;AACxB,SAASC,cAAc;AAGvB,OAAO,MAAMC,qBAAqB,GAAG,MAAAA,CAAO;EAC1CC,YAAY;EACZC,YAAY;EACZC,QAAQ;EACRC,WAAW;EACXC,8BAA8B;EAC9BC;AACW,CAAC,GAAG,CAAC,CAAC,KAGb;EACJ,IAAIC,KAA4B;EAEhC,IAAIN,YAAY,IAAIC,YAAY,EAAE;IAChC,MAAMM,UAAU,GAAG,MAAMC,OAAO,CAAClB,IAAI,CAACW,YAAY,EAAED,YAAY,CAAC,CAAC;IAClE,MAAM;MAAES;IAAK,CAAC,GAAGlB,KAAK,CAACS,YAAY,CAAC;IAEpC,MAAMU,eAAe,GAAG,GAAGlB,WAAW,GAAGiB,IAAI,EAAE;IAE/CH,KAAK,GAAG,IAAIb,SAAS,CACnBc,UAAU,EACV;MAAEI,QAAQ,EAAED;IAAgB,CAAC,EAC7BR,QAAQ,EACRC,WACF,CAAC;EACH;EAEA,OAAO;IACLT,MAAM;IACNkB,OAAO,EAAE;MACPN,KAAK;MACLJ,QAAQ,EAAEA,QAAQ,IAAI;QACpB;QACA,GAAGP,eAAe;QAClBC,YAAY,EAAE,MAAMA,YAAY,CAAC;MACnC,CAAC;MACDO,WAAW;MACXU,SAAS,EAAE,SAAS;MACpBC,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC1B,IAAI,CAACO,eAAe,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC;MAC3D,CAAC;MACDoB,WAAW,EAAEnB,cAAc;MAC3BM,8BAA8B;MAC9BC,SAAS;MACTa,OAAO,EAAE,uBAAuB,CAAC;IACnC;EACF,CAAC;AACH,CAAC;AAED,OAAO,eAAeV,OAAOA,CAACW,UAAkB,EAAE;EAChD,MAAM;IAAEC;EAAI,CAAC,GAAG7B,KAAK,CAAC4B,UAAU,CAAC;EAEjC,MAAME,UAA4B,GAAG;IACnCC,IAAI,EAAEF,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG;EACnC,CAAC;EAED,MAAMG,UAAU,GAAG,MAAM,CAACJ,UAAU,EAAE;IAAEK,IAAI,EAAEH;EAAW,CAAC,CAExD;EAEF,MAAM;IAAEI,OAAO,EAAElB;EAAW,CAAC,GAAG,MAAMgB,UAAU;EAChD,OAAOhB,UAAU;AACnB","ignoreList":[]}
@@ -1,9 +1,9 @@
1
1
  import { type Environment } from 'nunjucks';
2
2
  import { plugin } from '~/src/server/plugins/engine/plugin.js';
3
- import { type FilterFunction } from '~/src/server/plugins/engine/types.js';
3
+ import { type PluginOptions } from '~/src/server/plugins/engine/types.js';
4
4
  export { getPageHref } from '~/src/server/plugins/engine/helpers.js';
5
5
  export { context } from '~/src/server/plugins/nunjucks/context.js';
6
6
  export declare const VIEW_PATH = "src/server/plugins/engine/views";
7
7
  export declare const PLUGIN_PATH = "node_modules/@defra/forms-engine-plugin";
8
- export declare const prepareNunjucksEnvironment: (env: Environment, additionalFilters?: Record<string, FilterFunction>) => void;
8
+ export declare const prepareNunjucksEnvironment: (env: Environment, pluginOptions: PluginOptions) => void;
9
9
  export default plugin;
@@ -1,3 +1,4 @@
1
+ import { markdownToHtml } from '@defra/forms-model';
1
2
  import { engine } from "./helpers.js";
2
3
  import { plugin } from "./plugin.js";
3
4
  import { checkComponentTemplates, checkErrorTemplates, evaluate, govukRebrand } from "../nunjucks/environment.js";
@@ -12,17 +13,18 @@ const globals = {
12
13
  };
13
14
  export const VIEW_PATH = 'src/server/plugins/engine/views';
14
15
  export const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin';
15
- export const prepareNunjucksEnvironment = function (env, additionalFilters) {
16
+ export const prepareNunjucksEnvironment = function (env, pluginOptions) {
16
17
  for (const [name, nunjucksFilter] of Object.entries(filters)) {
17
18
  env.addFilter(name, nunjucksFilter);
18
19
  }
20
+ env.addFilter('markdown', text => markdownToHtml(text, pluginOptions.baseUrl));
19
21
  for (const [name, nunjucksGlobal] of Object.entries(globals)) {
20
22
  env.addGlobal(name, nunjucksGlobal);
21
23
  }
22
24
 
23
25
  // Apply any additional filters to both the liquid and nunjucks engines
24
- if (additionalFilters) {
25
- for (const [name, filter] of Object.entries(additionalFilters)) {
26
+ if (pluginOptions.filters) {
27
+ for (const [name, filter] of Object.entries(pluginOptions.filters)) {
26
28
  env.addFilter(name, filter);
27
29
  engine.registerFilter(name, filter);
28
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","filters","getPageHref","context","globals","VIEW_PATH","PLUGIN_PATH","prepareNunjucksEnvironment","env","additionalFilters","name","nunjucksFilter","Object","entries","addFilter","nunjucksGlobal","addGlobal","filter","registerFilter"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { 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 FilterFunction } 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 additionalFilters?: Record<string, FilterFunction>\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\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 (additionalFilters) {\n for (const [name, filter] of Object.entries(additionalFilters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n}\n\nexport default plugin\n"],"mappings":"AAEA,SAASA,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,iBAAkD,EAClD;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;EAEA,KAAK,MAAM,CAACD,IAAI,EAAEK,cAAc,CAAC,IAAIH,MAAM,CAACC,OAAO,CAACT,OAAO,CAAC,EAAE;IAC5DI,GAAG,CAACQ,SAAS,CAACN,IAAI,EAAEK,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIN,iBAAiB,EAAE;IACrB,KAAK,MAAM,CAACC,IAAI,EAAEO,MAAM,CAAC,IAAIL,MAAM,CAACC,OAAO,CAACJ,iBAAiB,CAAC,EAAE;MAC9DD,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEO,MAAM,CAAC;MAC3BtB,MAAM,CAACuB,cAAc,CAACR,IAAI,EAAEO,MAAM,CAAC;IACrC;EACF;AACF,CAAC;AAED,eAAerB,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","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter"],"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\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;AACF,CAAC;AAED,eAAevB,MAAM","ignoreList":[]}
@@ -1,4 +1,6 @@
1
1
  import Joi from 'joi';
2
+ import { createLogger } from "../../common/helpers/logging/logger.js";
3
+ const logger = createLogger();
2
4
  const pluginRegistrationOptionsSchema = Joi.object({
3
5
  model: Joi.object().optional(),
4
6
  services: Joi.object().optional(),
@@ -11,7 +13,9 @@ const pluginRegistrationOptionsSchema = Joi.object({
11
13
  paths: Joi.array().items(Joi.string()).required()
12
14
  }).required(),
13
15
  viewContext: Joi.function().required(),
14
- preparePageEventRequestOptions: Joi.function().optional()
16
+ preparePageEventRequestOptions: Joi.function().optional(),
17
+ onRequest: Joi.function().optional(),
18
+ baseUrl: Joi.string().uri().required()
15
19
  });
16
20
 
17
21
  /**
@@ -24,6 +28,7 @@ export function validatePluginOptions(options) {
24
28
  abortEarly: false
25
29
  });
26
30
  if (result.error) {
31
+ logger.error(`Missing required properties in plugin options: ${result.error.message}`);
27
32
  throw new Error('Invalid plugin options', result.error);
28
33
  }
29
34
 
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","names":["Joi","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cacheName","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","preparePageEventRequestOptions","validatePluginOptions","options","result","validate","abortEarly","error","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import Joi from 'joi'\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 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})\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 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,MAAMC,+BAA+B,GAAGD,GAAG,CAACE,MAAM,CAAC;EACjDC,KAAK,EAAEH,GAAG,CAACE,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EAC9BC,QAAQ,EAAEL,GAAG,CAACE,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EACjCE,WAAW,EAAEN,GAAG,CAACE,MAAM,CAAC,CAAC,CAACK,OAAO,CAACP,GAAG,CAACQ,MAAM,CAAC,CAAC,EAAER,GAAG,CAACS,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACrEM,SAAS,EAAEV,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EAClCO,OAAO,EAAEX,GAAG,CAACE,MAAM,CAAC,CAAC,CAACK,OAAO,CAACP,GAAG,CAACQ,MAAM,CAAC,CAAC,EAAER,GAAG,CAACS,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjEQ,UAAU,EAAEZ,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EACnCS,QAAQ,EAAEb,GAAG,CAACE,MAAM,CAAC;IACnBY,cAAc,EAAEd,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACO,QAAQ,CAAC,CAAC;IACvCC,KAAK,EAAEhB,GAAG,CAACiB,KAAK,CAAC,CAAC,CAACC,KAAK,CAAClB,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,CAACO,QAAQ,CAAC;EAClD,CAAC,CAAC,CAACA,QAAQ,CAAC,CAAC;EACbI,WAAW,EAAEnB,GAAG,CAACoB,QAAQ,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACtCM,8BAA8B,EAAErB,GAAG,CAACoB,QAAQ,CAAC,CAAC,CAAChB,QAAQ,CAAC;AAC1D,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA,OAAO,SAASkB,qBAAqBA,CAACC,OAAO,EAAE;EAC7C,MAAMC,MAAM,GAAGvB,+BAA+B,CAACwB,QAAQ,CAACF,OAAO,EAAE;IAC/DG,UAAU,EAAE;EACd,CAAC,CAAC;EAEF,IAAIF,MAAM,CAACG,KAAK,EAAE;IAChB,MAAM,IAAIC,KAAK,CAAC,wBAAwB,EAAEJ,MAAM,CAACG,KAAK,CAAC;EACzD;;EAEA;EACA,OAAOH,MAAM,CAACK,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","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 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,UAAU,EAAEd,GAAG,CAACU,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EACnCS,QAAQ,EAAEf,GAAG,CAACI,MAAM,CAAC;IACnBY,cAAc,EAAEhB,GAAG,CAACU,MAAM,CAAC,CAAC,CAACO,QAAQ,CAAC,CAAC;IACvCC,KAAK,EAAElB,GAAG,CAACmB,KAAK,CAAC,CAAC,CAACC,KAAK,CAACpB,GAAG,CAACU,MAAM,CAAC,CAAC,CAAC,CAACO,QAAQ,CAAC;EAClD,CAAC,CAAC,CAACA,QAAQ,CAAC,CAAC;EACbI,WAAW,EAAErB,GAAG,CAACsB,QAAQ,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACtCM,8BAA8B,EAAEvB,GAAG,CAACsB,QAAQ,CAAC,CAAC,CAAChB,QAAQ,CAAC,CAAC;EACzDkB,SAAS,EAAExB,GAAG,CAACsB,QAAQ,CAAC,CAAC,CAAChB,QAAQ,CAAC,CAAC;EACpCmB,OAAO,EAAEzB,GAAG,CAACU,MAAM,CAAC,CAAC,CAACgB,GAAG,CAAC,CAAC,CAACT,QAAQ,CAAC;AACvC,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA,OAAO,SAASU,qBAAqBA,CAACC,OAAO,EAAE;EAC7C,MAAMC,MAAM,GAAG1B,+BAA+B,CAAC2B,QAAQ,CAACF,OAAO,EAAE;IAC/DG,UAAU,EAAE;EACd,CAAC,CAAC;EAEF,IAAIF,MAAM,CAACG,KAAK,EAAE;IAChB9B,MAAM,CAAC8B,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":[]}
@@ -10,7 +10,8 @@ describe('validatePluginOptions', () => {
10
10
  return {
11
11
  hello: 'world'
12
12
  };
13
- }
13
+ },
14
+ baseUrl: 'http://localhost:3009'
14
15
  };
15
16
  expect(validatePluginOptions(validOptions)).toEqual(validOptions);
16
17
  });
@@ -19,6 +20,7 @@ describe('validatePluginOptions', () => {
19
20
  * tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test
20
21
  */
21
22
  it('fails if a required attribute is missing', () => {
23
+ // viewContext is missing
22
24
  const invalidOptions = {
23
25
  nunjucks: {
24
26
  baseLayoutPath: 'dxt-devtool-baselayout.html',
@@ -1 +1 @@
1
- {"version":3,"file":"options.test.js","names":["validatePluginOptions","describe","it","validOptions","nunjucks","baseLayoutPath","paths","viewContext","hello","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 }\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 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;IACF,CAAC;IAEDC,MAAM,CAACT,qBAAqB,CAACG,YAAY,CAAC,CAAC,CAACO,OAAO,CAACP,YAAY,CAAC;EACnE,CAAC,CAAC;;EAEF;AACF;AACA;EACED,EAAE,CAAC,0CAA0C,EAAE,MAAM;IACnD,MAAMS,cAAc,GAAG;MACrBP,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC;IACF,CAAC;;IAED;IACAG,MAAM,CAAC,MAAMT,qBAAqB,CAACW,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","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":[]}
@@ -5,7 +5,7 @@ import { ComponentCollection } from '~/src/server/plugins/engine/components/Comp
5
5
  import { type BackLink } from '~/src/server/plugins/engine/components/types.js';
6
6
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
7
7
  import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js';
8
- import { type FormContext, type FormContextRequest, type FormPageViewModel, type FormParams, type FormPayload, type FormState, type FormStateValue, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
8
+ import { type FormContext, type FormContextRequest, type FormPageViewModel, type FormPayload, type FormPayloadParams, type FormState, type FormStateValue, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
9
9
  import { type FormRequest, type FormRequestPayload, type FormRequestPayloadRefs, type FormRequestRefs } from '~/src/server/routes/types.js';
10
10
  export declare class QuestionPageController extends PageController {
11
11
  collection: ComponentCollection;
@@ -32,7 +32,7 @@ export declare class QuestionPageController extends PageController {
32
32
  /**
33
33
  * Gets form params (from payload) for this page only
34
34
  */
35
- getFormParams(request?: FormContextRequest): FormParams;
35
+ getFormParams(request?: FormContextRequest): FormPayloadParams;
36
36
  getStateFromValidForm(request: FormContextRequest, state: FormSubmissionState, payload: FormPayload): FormState;
37
37
  getErrors(details?: ValidationErrorItem[]): import("~/src/server/plugins/engine/types.js").FormSubmissionError[] | undefined;
38
38
  getState(request: FormRequest | FormRequestPayload): Promise<FormSubmissionState>;
@@ -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 FormParams,\n type FormPayload,\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): FormParams {\n const { payload } = request ?? {}\n\n const result = paramsSchema.validate(payload, {\n abortEarly: false,\n stripUnknown: true\n })\n\n return result.value as FormParams\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":"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","ignoreList":[]}
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":"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","ignoreList":[]}
@@ -11,7 +11,7 @@ export declare class RepeatPageController extends QuestionPageController {
11
11
  repeat: Repeat;
12
12
  constructor(model: FormModel, pageDef: PageRepeat);
13
13
  get keys(): string[];
14
- getFormParams(request?: FormContextRequest): import("~/src/server/plugins/engine/types.js").FormParams;
14
+ getFormParams(request?: FormContextRequest): import("~/src/server/plugins/engine/types.js").FormPayloadParams;
15
15
  getFormDataFromState(request: FormContextRequest | undefined, state: FormSubmissionState): {
16
16
  action?: FormAction;
17
17
  confirm?: true;
@@ -53,7 +53,8 @@ export function makeLoadFormPreHandler(server, options) {
53
53
  const prefix = server.realm.modifiers.route.prefix ?? '';
54
54
  const {
55
55
  services = defaultServices,
56
- controllers
56
+ controllers,
57
+ onRequest
57
58
  } = options;
58
59
  const {
59
60
  formsService
@@ -124,6 +125,11 @@ export function makeLoadFormPreHandler(server, options) {
124
125
  server.app.models.set(key, item);
125
126
  }
126
127
 
128
+ // Call the onRequest callback if it has been supplied
129
+ if (onRequest) {
130
+ onRequest(request, params, item.model.def, metadata);
131
+ }
132
+
127
133
  // Assign the model to the request data
128
134
  // for use in the downstream handler
129
135
  request.app.model = item.model;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","checkFormStatus","findPage","getCacheService","getPage","getStartPath","proceed","FormModel","generateUniqueReference","defaultServices","redirectOrMakeHandler","request","h","makeHandler","app","params","model","notFound","path","cacheService","server","page","state","getState","$$__referenceNumber","prefix","def","metadata","referenceNumberPrefix","badImplementation","referenceNumber","mergeState","flash","getFlash","context","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","startsWith","isForceAccess","redirectTo","next","length","query","returnUrl","getHref","makeLoadFormPreHandler","options","realm","modifiers","route","services","controllers","formsService","handler","continue","slug","isPreview","formState","getFormMetadata","id","key","item","models","get","updatedAt","logger","info","definition","getFormDefinition","emailAddress","notificationEmail","outputEmail","basePath","substring","set","dispatchHandler","servicePath"],"sources":["../../../../../src/server/plugins/engine/routes/index.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport {\n type ResponseObject,\n type ResponseToolkit,\n type Server\n} from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getStartPath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\n\nexport async function redirectOrMakeHandler(\n request: FormRequest | FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>,\n makeHandler: (\n page: PageControllerClass,\n context: FormContext\n ) => ResponseObject | Promise<ResponseObject>\n) {\n const { app, params } = request\n const { model } = app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n const cacheService = getCacheService(request.server)\n const page = getPage(model, request)\n let state = await page.getState(request)\n\n if (!state.$$__referenceNumber) {\n const prefix = model.def.metadata?.referenceNumberPrefix ?? ''\n\n if (typeof prefix !== 'string') {\n throw Boom.badImplementation(\n 'Reference number prefix must be a string or undefined'\n )\n }\n\n const referenceNumber = generateUniqueReference(prefix)\n state = await page.mergeState(request, state, {\n $$__referenceNumber: referenceNumber\n })\n }\n\n const flash = cacheService.getFlash(request)\n const context = model.getFormContext(request, state, flash?.errors)\n const relevantPath = page.getRelevantPath(request, context)\n const summaryPath = page.getSummaryPath()\n\n // Return handler for relevant pages or preview URL direct access\n if (relevantPath.startsWith(page.path) || context.isForceAccess) {\n return makeHandler(page, context)\n }\n\n // Redirect back to last relevant page\n const redirectTo = findPage(model, relevantPath)\n\n // Set the return URL unless an exit page\n if (redirectTo?.next.length) {\n request.query.returnUrl = page.getHref(summaryPath)\n }\n\n return proceed(request, h, page.getHref(relevantPath))\n}\n\nexport function makeLoadFormPreHandler(server: Server, options: PluginOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- hapi types are wrong\n const prefix = server.realm.modifiers.route.prefix ?? ''\n\n const { services = defaultServices, controllers } = options\n\n const { formsService } = services\n\n async function handler(\n request: FormRequest | FormRequestPayload,\n h: ResponseToolkit\n ) {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(params)\n\n // Get the form metadata using the `slug` param\n const metadata = await formsService.getFormMetadata(slug)\n\n const { id, [formState]: state } = metadata\n\n // Check the metadata supports the requested state\n if (!state) {\n throw Boom.notFound(`No '${formState}' state for form metadata ${id}`)\n }\n\n // Cache the models based on id, state and whether\n // it's a preview or not. There could be up to 3 models\n // cached for a single form:\n // \"{id}_live_false\" (live/live)\n // \"{id}_live_true\" (live/preview)\n // \"{id}_draft_true\" (draft/preview)\n const key = `${id}_${formState}_${isPreview}`\n let item = server.app.models.get(key)\n\n if (!item || !isEqual(item.updatedAt, state.updatedAt)) {\n server.logger.info(`Getting form definition ${id} (${slug}) ${formState}`)\n\n // Get the form definition using the `id` from the metadata\n const definition = await formsService.getFormDefinition(id, formState)\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${id} (${slug}) ${formState}`\n )\n }\n\n const emailAddress = metadata.notificationEmail ?? definition.outputEmail\n\n checkEmailAddressForLiveFormSubmission(emailAddress, isPreview)\n\n // Build the form model\n server.logger.info(\n `Building model for form definition ${id} (${slug}) ${formState}`\n )\n\n // Set up the basePath for the model\n const basePath = (\n isPreview\n ? `${prefix}${PREVIEW_PATH_PREFIX}/${formState}/${slug}`\n : `${prefix}/${slug}`\n ).substring(1)\n\n // Construct the form model\n const model = new FormModel(\n definition,\n { basePath },\n services,\n controllers\n )\n\n // Create new item and add it to the item cache\n item = { model, updatedAt: state.updatedAt }\n server.app.models.set(key, item)\n }\n\n // Assign the model to the request data\n // for use in the downstream handler\n request.app.model = item.model\n\n return h.continue\n }\n\n return handler\n}\n\nexport function dispatchHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n) {\n const { model } = request.app\n\n const servicePath = model ? `/${model.basePath}` : ''\n return proceed(request, h, `${servicePath}${getStartPath(model)}`)\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","checkFormStatus","findPage","getCacheService","getPage","getStartPath","proceed","FormModel","generateUniqueReference","defaultServices","redirectOrMakeHandler","request","h","makeHandler","app","params","model","notFound","path","cacheService","server","page","state","getState","$$__referenceNumber","prefix","def","metadata","referenceNumberPrefix","badImplementation","referenceNumber","mergeState","flash","getFlash","context","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","startsWith","isForceAccess","redirectTo","next","length","query","returnUrl","getHref","makeLoadFormPreHandler","options","realm","modifiers","route","services","controllers","onRequest","formsService","handler","continue","slug","isPreview","formState","getFormMetadata","id","key","item","models","get","updatedAt","logger","info","definition","getFormDefinition","emailAddress","notificationEmail","outputEmail","basePath","substring","set","dispatchHandler","servicePath"],"sources":["../../../../../src/server/plugins/engine/routes/index.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport {\n type ResponseObject,\n type ResponseToolkit,\n type Server\n} from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getStartPath,\n proceed\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type PluginOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\n\nexport async function redirectOrMakeHandler(\n request: FormRequest | FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>,\n makeHandler: (\n page: PageControllerClass,\n context: FormContext\n ) => ResponseObject | Promise<ResponseObject>\n) {\n const { app, params } = request\n const { model } = app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n const cacheService = getCacheService(request.server)\n const page = getPage(model, request)\n let state = await page.getState(request)\n\n if (!state.$$__referenceNumber) {\n const prefix = model.def.metadata?.referenceNumberPrefix ?? ''\n\n if (typeof prefix !== 'string') {\n throw Boom.badImplementation(\n 'Reference number prefix must be a string or undefined'\n )\n }\n\n const referenceNumber = generateUniqueReference(prefix)\n state = await page.mergeState(request, state, {\n $$__referenceNumber: referenceNumber\n })\n }\n\n const flash = cacheService.getFlash(request)\n const context = model.getFormContext(request, state, flash?.errors)\n const relevantPath = page.getRelevantPath(request, context)\n const summaryPath = page.getSummaryPath()\n\n // Return handler for relevant pages or preview URL direct access\n if (relevantPath.startsWith(page.path) || context.isForceAccess) {\n return makeHandler(page, context)\n }\n\n // Redirect back to last relevant page\n const redirectTo = findPage(model, relevantPath)\n\n // Set the return URL unless an exit page\n if (redirectTo?.next.length) {\n request.query.returnUrl = page.getHref(summaryPath)\n }\n\n return proceed(request, h, page.getHref(relevantPath))\n}\n\nexport function makeLoadFormPreHandler(server: Server, options: PluginOptions) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- hapi types are wrong\n const prefix = server.realm.modifiers.route.prefix ?? ''\n\n const { services = defaultServices, controllers, onRequest } = options\n\n const { formsService } = services\n\n async function handler(\n request: FormRequest | FormRequestPayload,\n h: ResponseToolkit\n ) {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(params)\n\n // Get the form metadata using the `slug` param\n const metadata = await formsService.getFormMetadata(slug)\n\n const { id, [formState]: state } = metadata\n\n // Check the metadata supports the requested state\n if (!state) {\n throw Boom.notFound(`No '${formState}' state for form metadata ${id}`)\n }\n\n // Cache the models based on id, state and whether\n // it's a preview or not. There could be up to 3 models\n // cached for a single form:\n // \"{id}_live_false\" (live/live)\n // \"{id}_live_true\" (live/preview)\n // \"{id}_draft_true\" (draft/preview)\n const key = `${id}_${formState}_${isPreview}`\n let item = server.app.models.get(key)\n\n if (!item || !isEqual(item.updatedAt, state.updatedAt)) {\n server.logger.info(`Getting form definition ${id} (${slug}) ${formState}`)\n\n // Get the form definition using the `id` from the metadata\n const definition = await formsService.getFormDefinition(id, formState)\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${id} (${slug}) ${formState}`\n )\n }\n\n const emailAddress = metadata.notificationEmail ?? definition.outputEmail\n\n checkEmailAddressForLiveFormSubmission(emailAddress, isPreview)\n\n // Build the form model\n server.logger.info(\n `Building model for form definition ${id} (${slug}) ${formState}`\n )\n\n // Set up the basePath for the model\n const basePath = (\n isPreview\n ? `${prefix}${PREVIEW_PATH_PREFIX}/${formState}/${slug}`\n : `${prefix}/${slug}`\n ).substring(1)\n\n // Construct the form model\n const model = new FormModel(\n definition,\n { basePath },\n services,\n controllers\n )\n\n // Create new item and add it to the item cache\n item = { model, updatedAt: state.updatedAt }\n server.app.models.set(key, item)\n }\n\n // Call the onRequest callback if it has been supplied\n if (onRequest) {\n onRequest(request, params, item.model.def, metadata)\n }\n\n // Assign the model to the request data\n // for use in the downstream handler\n request.app.model = item.model\n\n return h.continue\n }\n\n return handler\n}\n\nexport function dispatchHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n) {\n const { model } = request.app\n\n const servicePath = model ? `/${model.basePath}` : ''\n return proceed(request, h, `${servicePath}${getStartPath(model)}`)\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,4 @@
1
- import { type ComponentDef, type Event, type Item, type List, type Page } from '@defra/forms-model';
1
+ import { type ComponentDef, type Event, type FormDefinition, type FormMetadata, type Item, type List, type Page } from '@defra/forms-model';
2
2
  import { type PluginProperties, type Request } from '@hapi/hapi';
3
3
  import { type JoiExpression, type ValidationErrorItem } from 'joi';
4
4
  import { type Component } from '~/src/server/plugins/engine/components/helpers.js';
@@ -7,7 +7,7 @@ import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
7
7
  import { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js';
8
8
  import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js';
9
9
  import { type ViewContext } from '~/src/server/plugins/nunjucks/types.js';
10
- import { type FormAction, type FormRequest, type FormRequestPayload } from '~/src/server/routes/types.js';
10
+ import { type FormAction, type FormParams, type FormRequest, type FormRequestPayload } from '~/src/server/routes/types.js';
11
11
  import { type RequestOptions } from '~/src/server/services/httpService.js';
12
12
  import { type Services } from '~/src/server/types.js';
13
13
  /**
@@ -51,7 +51,7 @@ export interface FormSubmissionError extends Pick<ValidationErrorItem, 'context'
51
51
  name: string;
52
52
  text: string;
53
53
  }
54
- export interface FormParams {
54
+ export interface FormPayloadParams {
55
55
  action?: FormAction;
56
56
  confirm?: true;
57
57
  crumb?: string;
@@ -61,7 +61,7 @@ export interface FormParams {
61
61
  * Form POST for question pages
62
62
  * (after Joi has converted value types)
63
63
  */
64
- export type FormPayload = FormParams & Partial<Record<string, FormValue>>;
64
+ export type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>;
65
65
  export type FormValue = Item['value'] | Item['value'][] | UploadState | RepeatListState | undefined;
66
66
  export type FormState = Partial<Record<string, FormStateValue>>;
67
67
  export type FormStateValue = Exclude<FormValue, undefined> | null;
@@ -259,6 +259,7 @@ export interface ErrorMessageTemplateList {
259
259
  advancedSettingsErrors: ErrorMessageTemplate[];
260
260
  }
261
261
  export type PreparePageEventRequestOptions = (options: RequestOptions, event: Event, page: PageControllerClass, context: FormContext) => void;
262
+ export type OnRequestCallback = (request: FormRequest | FormRequestPayload, params: FormParams, definition: FormDefinition, metadata: FormMetadata) => void;
262
263
  export interface PluginOptions {
263
264
  model?: FormModel;
264
265
  services?: Services;
@@ -274,4 +275,6 @@ export interface PluginOptions {
274
275
  };
275
276
  viewContext: PluginProperties['forms-engine-plugin']['viewContext'];
276
277
  preparePageEventRequestOptions?: PreparePageEventRequestOptions;
278
+ onRequest?: OnRequestCallback;
279
+ baseUrl: string;
277
280
  }