@defra/forms-engine-plugin 0.1.7 → 0.1.9

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 (24) hide show
  1. package/.server/server/forms/report-a-terrorist.json +7 -7
  2. package/.server/server/forms/runner-components-test.json +10 -10
  3. package/.server/server/plugins/engine/models/FormModel.js +8 -1
  4. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  5. package/.server/server/plugins/engine/plugin.js +12 -1
  6. package/.server/server/plugins/engine/plugin.js.map +1 -1
  7. package/.server/server/plugins/engine/referenceNumbers.js +17 -0
  8. package/.server/server/plugins/engine/referenceNumbers.js.map +1 -0
  9. package/.server/server/plugins/engine/types.js.map +1 -1
  10. package/package.json +2 -2
  11. package/src/server/forms/report-a-terrorist.json +7 -7
  12. package/src/server/forms/runner-components-test.json +10 -10
  13. package/src/server/plugins/engine/helpers.test.ts +2 -1
  14. package/src/server/plugins/engine/models/FormModel.test.ts +56 -1
  15. package/src/server/plugins/engine/models/FormModel.ts +13 -1
  16. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +3 -0
  17. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +1 -0
  18. package/src/server/plugins/engine/pageControllers/PageController.test.ts +2 -2
  19. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +24 -12
  20. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +4 -2
  21. package/src/server/plugins/engine/plugin.ts +18 -1
  22. package/src/server/plugins/engine/referenceNumbers.test.ts +33 -0
  23. package/src/server/plugins/engine/referenceNumbers.ts +18 -0
  24. package/src/server/plugins/engine/types.ts +1 -0
@@ -54,7 +54,7 @@
54
54
  "schema": {}
55
55
  },
56
56
  {
57
- "name": "3jdOpV",
57
+ "name": "ajdOpV",
58
58
  "options": {},
59
59
  "type": "Details",
60
60
  "title": "Help me take a screenshot",
@@ -62,7 +62,7 @@
62
62
  "schema": {}
63
63
  },
64
64
  {
65
- "name": "LU6RMD",
65
+ "name": "LUBRMD",
66
66
  "options": {},
67
67
  "type": "RadiosField",
68
68
  "title": "Do you have any evidence?",
@@ -92,7 +92,7 @@
92
92
  "title": "Is there anything else you can tell us?",
93
93
  "components": [
94
94
  {
95
- "name": "HETM3o",
95
+ "name": "HETMBo",
96
96
  "title": "Html",
97
97
  "options": {},
98
98
  "type": "Html",
@@ -100,7 +100,7 @@
100
100
  "schema": {}
101
101
  },
102
102
  {
103
- "name": "evZ-IJ",
103
+ "name": "evZxIJ",
104
104
  "options": {
105
105
  "required": false
106
106
  },
@@ -141,7 +141,7 @@
141
141
  "title": "Yes I have evidence",
142
142
  "components": [
143
143
  {
144
- "name": "koE_ae",
144
+ "name": "koExae",
145
145
  "options": {
146
146
  "required": false
147
147
  },
@@ -230,7 +230,7 @@
230
230
  "conditions": [
231
231
  {
232
232
  "field": {
233
- "name": "PMXq1s.LU6RMD",
233
+ "name": "PMXq1s.LUBRMD",
234
234
  "type": "RadiosField",
235
235
  "display": "Do you have any evidence? in PMXq1s"
236
236
  },
@@ -252,7 +252,7 @@
252
252
  "conditions": [
253
253
  {
254
254
  "field": {
255
- "name": "PMXq1s.LU6RMD",
255
+ "name": "PMXq1s.LUBRMD",
256
256
  "type": "RadiosField",
257
257
  "display": "Do you have any evidence? in PMXq1s"
258
258
  },
@@ -6,7 +6,7 @@
6
6
  "path": "/do-you-own-a-vehicle",
7
7
  "components": [
8
8
  {
9
- "name": "qqbRw1",
9
+ "name": "qqbRwx",
10
10
  "options": {},
11
11
  "type": "YesNoField",
12
12
  "title": "Do you own a vehicle?",
@@ -32,7 +32,7 @@
32
32
  "title": "What address is the vehicle registered to?",
33
33
  "components": [
34
34
  {
35
- "name": "sFR4aX",
35
+ "name": "sFRxaX",
36
36
  "options": {},
37
37
  "type": "UkAddressField",
38
38
  "title": "What address is the vehicle registered to?",
@@ -46,7 +46,7 @@
46
46
  "schema": {}
47
47
  },
48
48
  {
49
- "name": "Z0Guyn",
49
+ "name": "ZxGuyn",
50
50
  "options": {},
51
51
  "type": "CheckboxesField",
52
52
  "title": "Which Clean Air Zones are you claiming an exemption for?",
@@ -66,7 +66,7 @@
66
66
  "title": "Clean Air Zone (CAZ) Exemption",
67
67
  "components": [
68
68
  {
69
- "name": "MOB13t",
69
+ "name": "MOBxxt",
70
70
  "title": "Html",
71
71
  "options": {},
72
72
  "type": "Html",
@@ -86,7 +86,7 @@
86
86
  "title": "Details about your vehicle",
87
87
  "components": [
88
88
  {
89
- "name": "0ZVmN_",
89
+ "name": "xZVmNx",
90
90
  "options": {},
91
91
  "type": "AutocompleteField",
92
92
  "title": "What is the make of you vehicle?",
@@ -94,7 +94,7 @@
94
94
  "schema": {}
95
95
  },
96
96
  {
97
- "name": "gHSgo2",
97
+ "name": "gHSgox",
98
98
  "options": {},
99
99
  "type": "TextField",
100
100
  "title": "Vehicle Model",
@@ -102,7 +102,7 @@
102
102
  "schema": {}
103
103
  },
104
104
  {
105
- "name": "4LZ9to",
105
+ "name": "xLZxto",
106
106
  "options": {},
107
107
  "type": "DatePartsField",
108
108
  "title": "Date you purchased the vehicle?",
@@ -152,7 +152,7 @@
152
152
  "schema": {}
153
153
  },
154
154
  {
155
- "name": "0zL5bB",
155
+ "name": "xzLxbB",
156
156
  "options": {},
157
157
  "type": "TelephoneNumberField",
158
158
  "title": "Contact number",
@@ -171,7 +171,7 @@
171
171
  "title": "final steps",
172
172
  "components": [
173
173
  {
174
- "name": "fkd8av",
174
+ "name": "fkdxav",
175
175
  "options": {},
176
176
  "type": "List",
177
177
  "title": "Declaration",
@@ -179,7 +179,7 @@
179
179
  "schema": {}
180
180
  },
181
181
  {
182
- "name": "L_2AYe",
182
+ "name": "LxxAYe",
183
183
  "options": {},
184
184
  "type": "EmailAddressField",
185
185
  "title": "Your email address",
@@ -196,7 +196,8 @@ export class FormModel {
196
196
  listDefMap: this.listDefMap,
197
197
  componentDefMap: this.componentDefMap,
198
198
  pageMap: this.pageMap,
199
- componentMap: this.componentMap
199
+ componentMap: this.componentMap,
200
+ referenceNumber: getReferenceNumber(state)
200
201
  };
201
202
 
202
203
  // Validate current page
@@ -360,4 +361,10 @@ function validateFormState(request, page, context) {
360
361
  }
361
362
  return context;
362
363
  }
364
+ function getReferenceNumber(state) {
365
+ if (!state.$$__referenceNumber || typeof state.$$__referenceNumber !== 'string') {
366
+ throw Error('Reference number not found in form state');
367
+ }
368
+ return state.$$__referenceNumber;
369
+ }
363
370
  //# sourceMappingURL=FormModel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","Engine","formDefinitionSchema","hasComponents","hasRepeater","add","Parser","joi","findPage","getError","getPage","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","componentDefMap","pageMap","componentMap","constructor","options","result","validate","abortEarly","error","structuredClone","value","push","title","type","items","text","forEach","conditionDef","condition","makeCondition","map","pageDef","some","controller","Status","path","Map","page","list","filter","flatMap","components","component","collection","makeSchema","makeFilteredSchema","relevantPages","schema","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","Date","toISOString","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","key","defineProperty","get","from","parse","toExpression","getList","find","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","getNextPath","validateFormState","assignPaths","V2","keys","getContextValueFromState","includes","action","getFormParams","Validate","update","fields","field","CheckboxesField","undefined","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n Engine,\n formDefinitionSchema,\n hasComponents,\n hasRepeater,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionsModelData,\n type DateUnits,\n type FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n findPage,\n getError,\n getPage\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n values: FormDefinition\n basePath: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n const result = formDefinitionSchema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n text: 'Yes',\n value: true\n },\n {\n text: 'No',\n value: false\n }\n ]\n })\n\n this.engine = def.engine\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(conditionDef)\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections\n */\n makeSchema() {\n return this.makeFilteredSchema(this.pages)\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n return add(new Date(), { [timeUnit]: timePeriod }).toISOString()\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const key in conditions) {\n Object.defineProperty(context, key, {\n get() {\n return conditions[key]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(name: string): List | undefined {\n return this.lists.find((list) => list.name === name)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (nextPage.path === currentPath) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\n }\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (!request.payload || action !== FormAction.Validate) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","Engine","formDefinitionSchema","hasComponents","hasRepeater","add","Parser","joi","findPage","getError","getPage","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","componentDefMap","pageMap","componentMap","constructor","options","result","validate","abortEarly","error","structuredClone","value","push","title","type","items","text","forEach","conditionDef","condition","makeCondition","map","pageDef","some","controller","Status","path","Map","page","list","filter","flatMap","components","component","collection","makeSchema","makeFilteredSchema","relevantPages","schema","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","Date","toISOString","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","key","defineProperty","get","from","parse","toExpression","getList","find","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","referenceNumber","getReferenceNumber","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","getNextPath","validateFormState","assignPaths","V2","keys","getContextValueFromState","includes","action","getFormParams","Validate","update","fields","field","CheckboxesField","undefined","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details","$$__referenceNumber","Error"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n Engine,\n formDefinitionSchema,\n hasComponents,\n hasRepeater,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionsModelData,\n type DateUnits,\n type FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n findPage,\n getError,\n getPage\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n values: FormDefinition\n basePath: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n const result = formDefinitionSchema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n text: 'Yes',\n value: true\n },\n {\n text: 'No',\n value: false\n }\n ]\n })\n\n this.engine = def.engine\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(conditionDef)\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections\n */\n makeSchema() {\n return this.makeFilteredSchema(this.pages)\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n return add(new Date(), { [timeUnit]: timePeriod }).toISOString()\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const key in conditions) {\n Object.defineProperty(context, key, {\n get() {\n return conditions[key]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(name: string): List | undefined {\n return this.lists.find((list) => list.name === name)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap,\n referenceNumber: getReferenceNumber(state)\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (nextPage.path === currentPath) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\n }\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (!request.payload || action !== FormAction.Validate) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n\nfunction getReferenceNumber(state: FormState): string {\n if (\n !state.$$__referenceNumber ||\n typeof state.$$__referenceNumber !== 'string'\n ) {\n throw Error('Reference number not found in form state')\n }\n\n return state.$$__referenceNumber\n}\n"],"mappings":"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","ignoreList":[]}
@@ -12,6 +12,7 @@ import { format } from "./outputFormatters/machine/v1.js";
12
12
  import { FileUploadPageController } from "./pageControllers/FileUploadPageController.js";
13
13
  import { RepeatPageController } from "./pageControllers/RepeatPageController.js";
14
14
  import { getFormSubmissionData } from "./pageControllers/SummaryPageController.js";
15
+ import { generateUniqueReference } from "./referenceNumbers.js";
15
16
  import * as defaultServices from "./services/index.js";
16
17
  import { getUploadStatus } from "./services/uploadService.js";
17
18
  import { actionSchema, confirmSchema, crumbSchema, itemIdSchema, pathSchema, stateSchema } from "../../schemas/index.js";
@@ -174,7 +175,17 @@ export const plugin = {
174
175
  }
175
176
  const cacheService = getCacheService(request.server);
176
177
  const page = getPage(model, request);
177
- const state = await page.getState(request);
178
+ let state = await page.getState(request);
179
+ if (!state.$$__referenceNumber) {
180
+ const prefix = model.def.metadata?.referenceNumberPrefix ?? '';
181
+ if (typeof prefix !== 'string') {
182
+ throw Boom.badImplementation('Reference number prefix must be a string or undefined');
183
+ }
184
+ const referenceNumber = generateUniqueReference(prefix);
185
+ state = await page.mergeState(request, state, {
186
+ $$__referenceNumber: referenceNumber
187
+ });
188
+ }
178
189
  const flash = cacheService.getFlash(request);
179
190
  const context = model.getFormContext(request, state, flash?.errors);
180
191
  const relevantPath = page.getRelevantPath(request, context);
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["hasFormComponents","slugSchema","Boom","vision","isEqual","Joi","nunjucks","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","checkFormStatus","findPage","getCacheService","getPage","getStartPath","normalisePath","proceed","redirectPath","PLUGIN_PATH","VIEW_PATH","context","prepareNunjucksEnvironment","FormModel","SummaryViewModel","format","FileUploadPageController","RepeatPageController","getFormSubmissionData","defaultServices","getUploadStatus","actionSchema","confirmSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","CacheService","plugin","name","dependencies","multiple","register","server","options","model","services","controllers","cacheName","viewPaths","filters","pluginPath","formsService","cacheService","path","Array","isArray","length","push","engines","html","compile","compileOptions","template","environment","render","prepare","next","configure","expose","app","itemCache","Map","models","loadFormPreHandler","request","h","continue","params","slug","isPreview","state","formState","metadata","getFormMetadata","id","notFound","key","item","get","updatedAt","logger","info","definition","getFormDefinition","emailAddress","notificationEmail","outputEmail","basePath","substring","set","dispatchHandler","servicePath","redirectOrMakeHandler","makeHandler","page","getState","flash","getFlash","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","startsWith","isForceAccess","redirectTo","query","returnUrl","getHref","getHandler","events","onLoad","type","url","viewModel","items","details","payload","undefined","response","postJson","Object","assign","data","makeGetRouteHandler","postHandler","pageDef","href","makePostRouteHandler","dispatchRouteOptions","pre","method","route","handler","validate","object","keys","getRouteOptions","itemId","optional","postRouteOptions","parse","crumb","action","unknown","required","getListSummaryHandler","makeGetListSummaryRouteHandler","postListSummaryHandler","makePostListSummaryRouteHandler","getItemDeleteHandler","makeGetItemDeleteRouteHandler","postItemDeleteHandler","makePostItemDeleteRouteHandler","confirm","uploadId","status","error","code","plugins","string","guid"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import { hasFormComponents, slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type Plugin,\n type ResponseObject,\n type ResponseToolkit,\n type RouteOptions,\n type Server\n} from '@hapi/hapi'\nimport vision from '@hapi/vision'\nimport { isEqual } from 'date-fns'\nimport Joi from 'joi'\nimport nunjucks, { type Environment } from 'nunjucks'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getStartPath,\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n PLUGIN_PATH,\n VIEW_PATH,\n context,\n prepareNunjucksEnvironment\n} from '~/src/server/plugins/engine/index.js'\nimport {\n FormModel,\n SummaryViewModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { FileUploadPageController } from '~/src/server/plugins/engine/pageControllers/FileUploadPageController.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport { getUploadStatus } from '~/src/server/plugins/engine/services/uploadService.js'\nimport {\n type FilterFunction,\n type FormContext\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 confirmSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\nimport { CacheService } from '~/src/server/services/index.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cacheName?: string\n viewPaths?: string[]\n filters?: Record<string, FilterFunction>\n pluginPath?: string\n}\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n const {\n model,\n services = defaultServices,\n controllers,\n cacheName,\n viewPaths,\n filters,\n pluginPath = PLUGIN_PATH\n } = options\n const { formsService } = services\n const cacheService = new CacheService(server, cacheName)\n\n // Paths array to tell `vision` and `nunjucks` where template files are stored.\n // We need to include `VIEW_PATH` in addition the runtime path (node_modules)\n // to keep the local tests working\n const path = [`${pluginPath}/${VIEW_PATH}`, VIEW_PATH]\n\n // Include any additional user provided view paths so our internal views engine\n // can find any files they provide from the consumer side if using custom `page.view`s\n if (Array.isArray(viewPaths) && viewPaths.length) {\n path.push(...viewPaths)\n }\n\n await server.register({\n plugin: vision,\n options: {\n engines: {\n html: {\n compile: (\n path: string,\n compileOptions: { environment: Environment }\n ) => {\n const template = nunjucks.compile(\n path,\n compileOptions.environment\n )\n\n return (context: object | undefined) => {\n return template.render(context)\n }\n },\n prepare: (\n options: EngineConfigurationObject,\n next: (err?: Error) => void\n ) => {\n // Nunjucks also needs an additional path configuration\n // to use the templates and macros from `govuk-frontend`\n const environment = nunjucks.configure([\n ...path,\n 'node_modules/govuk-frontend/dist'\n ])\n\n // Applies custom filters and globals for nunjucks\n // that are required by the `forms-engine-plugin`\n prepareNunjucksEnvironment(environment, filters)\n\n options.compileOptions.environment = environment\n\n next()\n }\n }\n },\n path,\n // Provides global context used with all templates\n context\n }\n })\n\n server.expose('cacheService', cacheService)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = async (\n request: FormRequest | FormRequestPayload,\n h: Pick<ResponseToolkit, 'continue'>\n ) => {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params, path } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(path)\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 = itemCache.get(key)\n\n if (!item || !isEqual(item.updatedAt, state.updatedAt)) {\n server.logger.info(\n `Getting form definition ${id} (${slug}) ${formState}`\n )\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 =\n 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 = isPreview\n ? `${PREVIEW_PATH_PREFIX.substring(1)}/${formState}/${slug}`\n : slug\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 itemCache.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 const 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\n const redirectOrMakeHandler = async (\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 const state = await page.getState(request)\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\n const getHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n const { options } = events.onLoad\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(\n viewModel.context,\n viewModel.details\n )\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(items, model, undefined, undefined)\n\n const { payload: response } = await httpService.postJson(url, {\n payload\n })\n\n Object.assign(context.data, response)\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n\n const postHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n return page.makePostRouteHandler()(request, context, h)\n })\n }\n\n const dispatchRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method: loadFormPreHandler\n }\n ]\n }\n\n server.route({\n method: 'get',\n path: '/{slug}',\n handler: dispatchHandler,\n options: {\n ...dispatchRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...dispatchRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n })\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method: loadFormPreHandler\n }\n ]\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: getHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: getHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n })\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [{ method: loadFormPreHandler }]\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n })\n\n /**\n * \"AddAnother\" repeat routes\n */\n\n // List summary GET route\n const getListSummaryHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n if (!(page instanceof RepeatPageController)) {\n throw Boom.notFound(`No repeater page found for /${params.path}`)\n }\n\n return page.makeGetListSummaryRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/summary',\n handler: getListSummaryHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: getListSummaryHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n })\n\n // List summary POST route\n const postListSummaryHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { isForceAccess } = context\n\n if (isForceAccess || !(page instanceof RepeatPageController)) {\n throw Boom.notFound(`No repeater page found for /${params.path}`)\n }\n\n return page.makePostListSummaryRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/summary',\n handler: postListSummaryHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: postListSummaryHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .required()\n }\n }\n })\n\n // Item delete GET route\n const getItemDeleteHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n if (\n !(\n page instanceof RepeatPageController ||\n page instanceof FileUploadPageController\n )\n ) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page.makeGetItemDeleteRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/{itemId}/confirm-delete',\n handler: getItemDeleteHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId}/confirm-delete',\n handler: getItemDeleteHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n })\n }\n }\n })\n\n // Item delete POST route\n const postItemDeleteHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { isForceAccess } = context\n\n if (\n isForceAccess ||\n !(\n page instanceof RepeatPageController ||\n page instanceof FileUploadPageController\n )\n ) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page.makePostItemDeleteRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/{itemId}/confirm-delete',\n handler: postItemDeleteHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema,\n confirm: confirmSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId}/confirm-delete',\n handler: postItemDeleteHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema,\n confirm: confirmSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/upload-status/{uploadId}',\n handler: async (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'response'>\n ) => {\n try {\n const { uploadId } = request.params as unknown as {\n uploadId: string\n }\n const status = await getUploadStatus(uploadId)\n\n if (!status) {\n return h.response({ error: 'Status check failed' }).code(400)\n }\n\n return h.response(status)\n } catch (error) {\n request.logger.error(\n ['upload-status'],\n 'Upload status check failed',\n error\n )\n return h.response({ error: 'Status check error' }).code(500)\n }\n },\n options: {\n plugins: {\n crumb: false\n },\n validate: {\n params: Joi.object().keys({\n uploadId: Joi.string().guid().required()\n })\n }\n }\n })\n }\n} satisfies Plugin<PluginOptions>\n\ninterface CompileOptions {\n environment: Environment\n}\n\nexport interface EngineConfigurationObject {\n compileOptions: CompileOptions\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"plugin.js","names":["hasFormComponents","slugSchema","Boom","vision","isEqual","Joi","nunjucks","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","checkFormStatus","findPage","getCacheService","getPage","getStartPath","normalisePath","proceed","redirectPath","PLUGIN_PATH","VIEW_PATH","context","prepareNunjucksEnvironment","FormModel","SummaryViewModel","format","FileUploadPageController","RepeatPageController","getFormSubmissionData","generateUniqueReference","defaultServices","getUploadStatus","actionSchema","confirmSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","CacheService","plugin","name","dependencies","multiple","register","server","options","model","services","controllers","cacheName","viewPaths","filters","pluginPath","formsService","cacheService","path","Array","isArray","length","push","engines","html","compile","compileOptions","template","environment","render","prepare","next","configure","expose","app","itemCache","Map","models","loadFormPreHandler","request","h","continue","params","slug","isPreview","state","formState","metadata","getFormMetadata","id","notFound","key","item","get","updatedAt","logger","info","definition","getFormDefinition","emailAddress","notificationEmail","outputEmail","basePath","substring","set","dispatchHandler","servicePath","redirectOrMakeHandler","makeHandler","page","getState","$$__referenceNumber","prefix","def","referenceNumberPrefix","badImplementation","referenceNumber","mergeState","flash","getFlash","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","startsWith","isForceAccess","redirectTo","query","returnUrl","getHref","getHandler","events","onLoad","type","url","viewModel","items","details","payload","undefined","response","postJson","Object","assign","data","makeGetRouteHandler","postHandler","pageDef","href","makePostRouteHandler","dispatchRouteOptions","pre","method","route","handler","validate","object","keys","getRouteOptions","itemId","optional","postRouteOptions","parse","crumb","action","unknown","required","getListSummaryHandler","makeGetListSummaryRouteHandler","postListSummaryHandler","makePostListSummaryRouteHandler","getItemDeleteHandler","makeGetItemDeleteRouteHandler","postItemDeleteHandler","makePostItemDeleteRouteHandler","confirm","uploadId","status","error","code","plugins","string","guid"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import { hasFormComponents, slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type Plugin,\n type ResponseObject,\n type ResponseToolkit,\n type RouteOptions,\n type Server\n} from '@hapi/hapi'\nimport vision from '@hapi/vision'\nimport { isEqual } from 'date-fns'\nimport Joi from 'joi'\nimport nunjucks, { type Environment } from 'nunjucks'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n findPage,\n getCacheService,\n getPage,\n getStartPath,\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n PLUGIN_PATH,\n VIEW_PATH,\n context,\n prepareNunjucksEnvironment\n} from '~/src/server/plugins/engine/index.js'\nimport {\n FormModel,\n SummaryViewModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { FileUploadPageController } from '~/src/server/plugins/engine/pageControllers/FileUploadPageController.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.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 { getUploadStatus } from '~/src/server/plugins/engine/services/uploadService.js'\nimport {\n type FilterFunction,\n type FormContext\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 confirmSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\nimport { CacheService } from '~/src/server/services/index.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cacheName?: string\n viewPaths?: string[]\n filters?: Record<string, FilterFunction>\n pluginPath?: string\n}\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n const {\n model,\n services = defaultServices,\n controllers,\n cacheName,\n viewPaths,\n filters,\n pluginPath = PLUGIN_PATH\n } = options\n const { formsService } = services\n const cacheService = new CacheService(server, cacheName)\n\n // Paths array to tell `vision` and `nunjucks` where template files are stored.\n // We need to include `VIEW_PATH` in addition the runtime path (node_modules)\n // to keep the local tests working\n const path = [`${pluginPath}/${VIEW_PATH}`, VIEW_PATH]\n\n // Include any additional user provided view paths so our internal views engine\n // can find any files they provide from the consumer side if using custom `page.view`s\n if (Array.isArray(viewPaths) && viewPaths.length) {\n path.push(...viewPaths)\n }\n\n await server.register({\n plugin: vision,\n options: {\n engines: {\n html: {\n compile: (\n path: string,\n compileOptions: { environment: Environment }\n ) => {\n const template = nunjucks.compile(\n path,\n compileOptions.environment\n )\n\n return (context: object | undefined) => {\n return template.render(context)\n }\n },\n prepare: (\n options: EngineConfigurationObject,\n next: (err?: Error) => void\n ) => {\n // Nunjucks also needs an additional path configuration\n // to use the templates and macros from `govuk-frontend`\n const environment = nunjucks.configure([\n ...path,\n 'node_modules/govuk-frontend/dist'\n ])\n\n // Applies custom filters and globals for nunjucks\n // that are required by the `forms-engine-plugin`\n prepareNunjucksEnvironment(environment, filters)\n\n options.compileOptions.environment = environment\n\n next()\n }\n }\n },\n path,\n // Provides global context used with all templates\n context\n }\n })\n\n server.expose('cacheService', cacheService)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = async (\n request: FormRequest | FormRequestPayload,\n h: Pick<ResponseToolkit, 'continue'>\n ) => {\n if (server.app.model) {\n request.app.model = server.app.model\n\n return h.continue\n }\n\n const { params, path } = request\n const { slug } = params\n const { isPreview, state: formState } = checkFormStatus(path)\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 = itemCache.get(key)\n\n if (!item || !isEqual(item.updatedAt, state.updatedAt)) {\n server.logger.info(\n `Getting form definition ${id} (${slug}) ${formState}`\n )\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 =\n 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 = isPreview\n ? `${PREVIEW_PATH_PREFIX.substring(1)}/${formState}/${slug}`\n : slug\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 itemCache.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 const 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\n const redirectOrMakeHandler = async (\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\n const getHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n const { options } = events.onLoad\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(\n viewModel.context,\n viewModel.details\n )\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(items, model, undefined, undefined)\n\n const { payload: response } = await httpService.postJson(url, {\n payload\n })\n\n Object.assign(context.data, response)\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n\n const postHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n return page.makePostRouteHandler()(request, context, h)\n })\n }\n\n const dispatchRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method: loadFormPreHandler\n }\n ]\n }\n\n server.route({\n method: 'get',\n path: '/{slug}',\n handler: dispatchHandler,\n options: {\n ...dispatchRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...dispatchRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n })\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method: loadFormPreHandler\n }\n ]\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: getHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: getHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n })\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [{ method: loadFormPreHandler }]\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n })\n\n /**\n * \"AddAnother\" repeat routes\n */\n\n // List summary GET route\n const getListSummaryHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n if (!(page instanceof RepeatPageController)) {\n throw Boom.notFound(`No repeater page found for /${params.path}`)\n }\n\n return page.makeGetListSummaryRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/summary',\n handler: getListSummaryHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: getListSummaryHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n })\n\n // List summary POST route\n const postListSummaryHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { isForceAccess } = context\n\n if (isForceAccess || !(page instanceof RepeatPageController)) {\n throw Boom.notFound(`No repeater page found for /${params.path}`)\n }\n\n return page.makePostListSummaryRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/summary',\n handler: postListSummaryHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: postListSummaryHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .required()\n }\n }\n })\n\n // Item delete GET route\n const getItemDeleteHandler = (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n if (\n !(\n page instanceof RepeatPageController ||\n page instanceof FileUploadPageController\n )\n ) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page.makeGetItemDeleteRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'get',\n path: '/{slug}/{path}/{itemId}/confirm-delete',\n handler: getItemDeleteHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n })\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId}/confirm-delete',\n handler: getItemDeleteHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n })\n }\n }\n })\n\n // Item delete POST route\n const postItemDeleteHandler = (\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { isForceAccess } = context\n\n if (\n isForceAccess ||\n !(\n page instanceof RepeatPageController ||\n page instanceof FileUploadPageController\n )\n ) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page.makePostItemDeleteRouteHandler()(request, context, h)\n })\n }\n\n server.route({\n method: 'post',\n path: '/{slug}/{path}/{itemId}/confirm-delete',\n handler: postItemDeleteHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema,\n confirm: confirmSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId}/confirm-delete',\n handler: postItemDeleteHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema,\n confirm: confirmSchema\n })\n .required()\n }\n }\n })\n\n server.route({\n method: 'get',\n path: '/upload-status/{uploadId}',\n handler: async (\n request: FormRequest,\n h: Pick<ResponseToolkit, 'response'>\n ) => {\n try {\n const { uploadId } = request.params as unknown as {\n uploadId: string\n }\n const status = await getUploadStatus(uploadId)\n\n if (!status) {\n return h.response({ error: 'Status check failed' }).code(400)\n }\n\n return h.response(status)\n } catch (error) {\n request.logger.error(\n ['upload-status'],\n 'Upload status check failed',\n error\n )\n return h.response({ error: 'Status check error' }).code(500)\n }\n },\n options: {\n plugins: {\n crumb: false\n },\n validate: {\n params: Joi.object().keys({\n uploadId: Joi.string().guid().required()\n })\n }\n }\n })\n }\n} satisfies Plugin<PluginOptions>\n\ninterface CompileOptions {\n environment: Environment\n}\n\nexport interface EngineConfigurationObject {\n compileOptions: CompileOptions\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,17 @@
1
+ import { randomBytes } from 'node:crypto';
2
+
3
+ /**
4
+ * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.
5
+ * Provides no guarantee on uniqueness.
6
+ */
7
+ export function generateUniqueReference(prefix) {
8
+ const segmentLength = 3;
9
+ const segmentCount = prefix ? 2 : 3;
10
+ prefix = prefix ? `${prefix}-` : '';
11
+ const segments = Array.from({
12
+ length: segmentCount
13
+ }, () => randomBytes(segmentLength).toString('hex').slice(0, segmentLength) // 0-9a-f, might be good enough?
14
+ );
15
+ return `${prefix}${segments.join('-')}`.toUpperCase();
16
+ }
17
+ //# sourceMappingURL=referenceNumbers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referenceNumbers.js","names":["randomBytes","generateUniqueReference","prefix","segmentLength","segmentCount","segments","Array","from","length","toString","slice","join","toUpperCase"],"sources":["../../../../src/server/plugins/engine/referenceNumbers.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\n\n/**\n * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.\n * Provides no guarantee on uniqueness.\n */\nexport function generateUniqueReference(prefix?: string) {\n const segmentLength = 3\n const segmentCount = prefix ? 2 : 3\n prefix = prefix ? `${prefix}-` : ''\n\n const segments = Array.from(\n { length: segmentCount },\n () => randomBytes(segmentLength).toString('hex').slice(0, segmentLength) // 0-9a-f, might be good enough?\n )\n\n return `${prefix}${segments.join('-')}`.toUpperCase()\n}\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,aAAa;;AAEzC;AACA;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CAACC,MAAe,EAAE;EACvD,MAAMC,aAAa,GAAG,CAAC;EACvB,MAAMC,YAAY,GAAGF,MAAM,GAAG,CAAC,GAAG,CAAC;EACnCA,MAAM,GAAGA,MAAM,GAAG,GAAGA,MAAM,GAAG,GAAG,EAAE;EAEnC,MAAMG,QAAQ,GAAGC,KAAK,CAACC,IAAI,CACzB;IAAEC,MAAM,EAAEJ;EAAa,CAAC,EACxB,MAAMJ,WAAW,CAACG,aAAa,CAAC,CAACM,QAAQ,CAAC,KAAK,CAAC,CAACC,KAAK,CAAC,CAAC,EAAEP,aAAa,CAAC,CAAC;EAC3E,CAAC;EAED,OAAO,GAAGD,MAAM,GAAGG,QAAQ,CAACM,IAAI,CAAC,GAAG,CAAC,EAAE,CAACC,WAAW,CAAC,CAAC;AACvD","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["UploadStatus","FileStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport { type FormAction, type FormRequest } from '~/src/server/routes/types.js'\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<FormRequest, 'app' | 'method' | 'params' | 'path' | 'query' | 'url'>\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport enum UploadStatus {\n initiated = 'initiated',\n pending = 'pending',\n ready = 'ready'\n}\n\nexport enum FileStatus {\n complete = 'complete',\n rejected = 'rejected',\n pending = 'pending'\n}\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type FilterFunction = (value: unknown) => unknown\n"],"mappings":"AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAmBA;AACA;AACA;AACA;;AAiGA,WAAYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAMxB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["UploadStatus","FileStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport { type FormAction, type FormRequest } from '~/src/server/routes/types.js'\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<FormRequest, 'app' | 'method' | 'params' | 'path' | 'query' | 'url'>\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport enum UploadStatus {\n initiated = 'initiated',\n pending = 'pending',\n ready = 'ready'\n}\n\nexport enum FileStatus {\n complete = 'complete',\n rejected = 'rejected',\n pending = 'pending'\n}\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type FilterFunction = (value: unknown) => unknown\n"],"mappings":"AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAmBA;AACA;AACA;AACA;;AAkGA,WAAYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAMxB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "license": "SEE LICENSE IN LICENSE",
62
62
  "dependencies": {
63
- "@defra/forms-model": "^3.0.409",
63
+ "@defra/forms-model": "^3.0.432",
64
64
  "@defra/hapi-tracing": "^1.0.0",
65
65
  "@elastic/ecs-pino-format": "^1.5.0",
66
66
  "@hapi/boom": "^10.0.1",
@@ -54,7 +54,7 @@
54
54
  "schema": {}
55
55
  },
56
56
  {
57
- "name": "3jdOpV",
57
+ "name": "ajdOpV",
58
58
  "options": {},
59
59
  "type": "Details",
60
60
  "title": "Help me take a screenshot",
@@ -62,7 +62,7 @@
62
62
  "schema": {}
63
63
  },
64
64
  {
65
- "name": "LU6RMD",
65
+ "name": "LUBRMD",
66
66
  "options": {},
67
67
  "type": "RadiosField",
68
68
  "title": "Do you have any evidence?",
@@ -92,7 +92,7 @@
92
92
  "title": "Is there anything else you can tell us?",
93
93
  "components": [
94
94
  {
95
- "name": "HETM3o",
95
+ "name": "HETMBo",
96
96
  "title": "Html",
97
97
  "options": {},
98
98
  "type": "Html",
@@ -100,7 +100,7 @@
100
100
  "schema": {}
101
101
  },
102
102
  {
103
- "name": "evZ-IJ",
103
+ "name": "evZxIJ",
104
104
  "options": {
105
105
  "required": false
106
106
  },
@@ -141,7 +141,7 @@
141
141
  "title": "Yes I have evidence",
142
142
  "components": [
143
143
  {
144
- "name": "koE_ae",
144
+ "name": "koExae",
145
145
  "options": {
146
146
  "required": false
147
147
  },
@@ -230,7 +230,7 @@
230
230
  "conditions": [
231
231
  {
232
232
  "field": {
233
- "name": "PMXq1s.LU6RMD",
233
+ "name": "PMXq1s.LUBRMD",
234
234
  "type": "RadiosField",
235
235
  "display": "Do you have any evidence? in PMXq1s"
236
236
  },
@@ -252,7 +252,7 @@
252
252
  "conditions": [
253
253
  {
254
254
  "field": {
255
- "name": "PMXq1s.LU6RMD",
255
+ "name": "PMXq1s.LUBRMD",
256
256
  "type": "RadiosField",
257
257
  "display": "Do you have any evidence? in PMXq1s"
258
258
  },
@@ -6,7 +6,7 @@
6
6
  "path": "/do-you-own-a-vehicle",
7
7
  "components": [
8
8
  {
9
- "name": "qqbRw1",
9
+ "name": "qqbRwx",
10
10
  "options": {},
11
11
  "type": "YesNoField",
12
12
  "title": "Do you own a vehicle?",
@@ -32,7 +32,7 @@
32
32
  "title": "What address is the vehicle registered to?",
33
33
  "components": [
34
34
  {
35
- "name": "sFR4aX",
35
+ "name": "sFRxaX",
36
36
  "options": {},
37
37
  "type": "UkAddressField",
38
38
  "title": "What address is the vehicle registered to?",
@@ -46,7 +46,7 @@
46
46
  "schema": {}
47
47
  },
48
48
  {
49
- "name": "Z0Guyn",
49
+ "name": "ZxGuyn",
50
50
  "options": {},
51
51
  "type": "CheckboxesField",
52
52
  "title": "Which Clean Air Zones are you claiming an exemption for?",
@@ -66,7 +66,7 @@
66
66
  "title": "Clean Air Zone (CAZ) Exemption",
67
67
  "components": [
68
68
  {
69
- "name": "MOB13t",
69
+ "name": "MOBxxt",
70
70
  "title": "Html",
71
71
  "options": {},
72
72
  "type": "Html",
@@ -86,7 +86,7 @@
86
86
  "title": "Details about your vehicle",
87
87
  "components": [
88
88
  {
89
- "name": "0ZVmN_",
89
+ "name": "xZVmNx",
90
90
  "options": {},
91
91
  "type": "AutocompleteField",
92
92
  "title": "What is the make of you vehicle?",
@@ -94,7 +94,7 @@
94
94
  "schema": {}
95
95
  },
96
96
  {
97
- "name": "gHSgo2",
97
+ "name": "gHSgox",
98
98
  "options": {},
99
99
  "type": "TextField",
100
100
  "title": "Vehicle Model",
@@ -102,7 +102,7 @@
102
102
  "schema": {}
103
103
  },
104
104
  {
105
- "name": "4LZ9to",
105
+ "name": "xLZxto",
106
106
  "options": {},
107
107
  "type": "DatePartsField",
108
108
  "title": "Date you purchased the vehicle?",
@@ -152,7 +152,7 @@
152
152
  "schema": {}
153
153
  },
154
154
  {
155
- "name": "0zL5bB",
155
+ "name": "xzLxbB",
156
156
  "options": {},
157
157
  "type": "TelephoneNumberField",
158
158
  "title": "Contact number",
@@ -171,7 +171,7 @@
171
171
  "title": "final steps",
172
172
  "components": [
173
173
  {
174
- "name": "fkd8av",
174
+ "name": "fkdxav",
175
175
  "options": {},
176
176
  "type": "List",
177
177
  "title": "Declaration",
@@ -179,7 +179,7 @@
179
179
  "schema": {}
180
180
  },
181
181
  {
182
- "name": "L_2AYe",
182
+ "name": "LxxAYe",
183
183
  "options": {},
184
184
  "type": "EmailAddressField",
185
185
  "title": "Your email address",
@@ -561,7 +561,8 @@ describe('Helpers', () => {
561
561
  listDefMap: model.listDefMap,
562
562
  componentDefMap: model.componentDefMap,
563
563
  pageMap: model.pageMap,
564
- componentMap: model.componentMap
564
+ componentMap: model.componentMap,
565
+ referenceNumber: 'foobar'
565
566
  }
566
567
  })
567
568
 
@@ -18,7 +18,10 @@ describe('FormModel', () => {
18
18
  basePath: '/components'
19
19
  })
20
20
 
21
- const state = { checkboxesSingle: ['Arabian', 'Shetland'] }
21
+ const state = {
22
+ $$__referenceNumber: 'foobar',
23
+ checkboxesSingle: ['Arabian', 'Shetland']
24
+ }
22
25
  const pageUrl = new URL('http://example.com/components/fields-required')
23
26
 
24
27
  const request: FormContextRequest = {
@@ -37,6 +40,58 @@ describe('FormModel', () => {
37
40
  expect(context.errors).toContainEqual(
38
41
  expect.objectContaining({ name: 'checkboxesSingle' })
39
42
  )
43
+ expect(context.referenceNumber).toEqual(expect.any(String))
44
+ })
45
+
46
+ it('handles missing reference numbers', () => {
47
+ const formModel = new FormModel(fieldsRequiredDefinition, {
48
+ basePath: '/components'
49
+ })
50
+
51
+ const state = {
52
+ checkboxesSingle: ['Arabian', 'Shetland']
53
+ }
54
+ const pageUrl = new URL('http://example.com/components/fields-required')
55
+
56
+ const request: FormContextRequest = {
57
+ method: 'post',
58
+ payload: { crumb: 'dummyCrumb', action: 'validate' },
59
+ query: {},
60
+ path: pageUrl.pathname,
61
+ params: { path: 'components', slug: 'fields-required' },
62
+ url: pageUrl,
63
+ app: { model: formModel }
64
+ }
65
+
66
+ expect(() => formModel.getFormContext(request, state)).toThrow(
67
+ 'Reference number not found in form state'
68
+ )
69
+ })
70
+
71
+ it('handles non-string reference numbers', () => {
72
+ const formModel = new FormModel(fieldsRequiredDefinition, {
73
+ basePath: '/components'
74
+ })
75
+
76
+ const state = {
77
+ $$__referenceNumber: 1232456,
78
+ checkboxesSingle: ['Arabian', 'Shetland']
79
+ }
80
+ const pageUrl = new URL('http://example.com/components/fields-required')
81
+
82
+ const request: FormContextRequest = {
83
+ method: 'post',
84
+ payload: { crumb: 'dummyCrumb', action: 'validate' },
85
+ query: {},
86
+ path: pageUrl.pathname,
87
+ params: { path: 'components', slug: 'fields-required' },
88
+ url: pageUrl,
89
+ app: { model: formModel }
90
+ }
91
+
92
+ expect(() => formModel.getFormContext(request, state)).toThrow(
93
+ 'Reference number not found in form state'
94
+ )
40
95
  })
41
96
  })
42
97
  })
@@ -275,7 +275,8 @@ export class FormModel {
275
275
  listDefMap: this.listDefMap,
276
276
  componentDefMap: this.componentDefMap,
277
277
  pageMap: this.pageMap,
278
- componentMap: this.componentMap
278
+ componentMap: this.componentMap,
279
+ referenceNumber: getReferenceNumber(state)
279
280
  }
280
281
 
281
282
  // Validate current page
@@ -441,3 +442,14 @@ function validateFormState(
441
442
 
442
443
  return context
443
444
  }
445
+
446
+ function getReferenceNumber(state: FormState): string {
447
+ if (
448
+ !state.$$__referenceNumber ||
449
+ typeof state.$$__referenceNumber !== 'string'
450
+ ) {
451
+ throw Error('Reference number not found in form state')
452
+ }
453
+
454
+ return state.$$__referenceNumber
455
+ }
@@ -51,6 +51,7 @@ describe('SummaryViewModel', () => {
51
51
  {
52
52
  description: '0 items',
53
53
  state: {
54
+ $$__referenceNumber: 'foobar',
54
55
  orderType: 'collection',
55
56
  pizza: []
56
57
  } satisfies FormState,
@@ -60,6 +61,7 @@ describe('SummaryViewModel', () => {
60
61
  {
61
62
  description: '1 item',
62
63
  state: {
64
+ $$__referenceNumber: 'foobar',
63
65
  orderType: 'delivery',
64
66
  pizza: [
65
67
  {
@@ -75,6 +77,7 @@ describe('SummaryViewModel', () => {
75
77
  {
76
78
  description: '2 items',
77
79
  state: {
80
+ $$__referenceNumber: 'foobar',
78
81
  orderType: 'delivery',
79
82
  pizza: [
80
83
  {
@@ -31,6 +31,7 @@ const model = new FormModel(definition, {
31
31
  })
32
32
 
33
33
  const state = {
34
+ $$__referenceNumber: 'foobar',
34
35
  orderType: 'delivery',
35
36
  pizza: [
36
37
  {
@@ -167,13 +167,13 @@ describe('PageController', () => {
167
167
 
168
168
  await controller1.makeGetRouteHandler()(
169
169
  request,
170
- model.getFormContext(request, {}),
170
+ model.getFormContext(request, { $$__referenceNumber: 'foobar' }),
171
171
  h
172
172
  )
173
173
 
174
174
  await controller2.makeGetRouteHandler()(
175
175
  request,
176
- model.getFormContext(request, {}),
176
+ model.getFormContext(request, { $$__referenceNumber: 'foobar' }),
177
177
  h
178
178
  )
179
179
 
@@ -178,12 +178,12 @@ describe('QuestionPageController', () => {
178
178
  beforeEach(() => {
179
179
  viewModel1 = controller1.getViewModel(
180
180
  requestPage1,
181
- model.getFormContext(requestPage1, {})
181
+ model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' })
182
182
  )
183
183
 
184
184
  viewModel2 = controller2.getViewModel(
185
185
  requestPage2,
186
- model.getFormContext(requestPage2, {})
186
+ model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' })
187
187
  )
188
188
  })
189
189
 
@@ -267,6 +267,7 @@ describe('QuestionPageController', () => {
267
267
 
268
268
  // The state below shows we said we had a UKPassport and entered details for an applicant
269
269
  const state: FormSubmissionState = {
270
+ $$__referenceNumber: 'foobar',
270
271
  ukPassport: true,
271
272
  numberOfApplicants: 2,
272
273
  applicantOneFirstName: 'Enrique',
@@ -384,6 +385,7 @@ describe('QuestionPageController', () => {
384
385
  } satisfies FormContextRequest
385
386
 
386
387
  const context = controller.model.getFormContext(request, {
388
+ $$__referenceNumber: 'foobar',
387
389
  dateField__day: 5,
388
390
  dateField__month: 1,
389
391
  dateField__year: 2024
@@ -411,7 +413,7 @@ describe('QuestionPageController', () => {
411
413
  const controller = new QuestionPageController(model, pages[0])
412
414
 
413
415
  // The state below shows we said we had a UKPassport and entered details for an applicant
414
- const state: FormSubmissionState = {}
416
+ const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
415
417
 
416
418
  const request = {
417
419
  method: 'get',
@@ -467,7 +469,7 @@ describe('QuestionPageController', () => {
467
469
  const controller = new QuestionPageController(model, pages[0])
468
470
 
469
471
  // The state below shows we said we had a UKPassport and entered details for an applicant
470
- const state: FormSubmissionState = {}
472
+ const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
471
473
 
472
474
  const request = {
473
475
  method: 'get',
@@ -531,15 +533,19 @@ describe('QuestionPageController', () => {
531
533
 
532
534
  beforeEach(() => {
533
535
  // Empty state
534
- context = model.getFormContext(requestPage1, {})
536
+ context = model.getFormContext(requestPage1, {
537
+ $$__referenceNumber: 'foobar'
538
+ })
535
539
 
536
540
  // Question 1: Selected 'No'
537
541
  contextNo = model.getFormContext(requestPage1, {
542
+ $$__referenceNumber: 'foobar',
538
543
  yesNoField: false
539
544
  })
540
545
 
541
546
  // Question 1: Selected 'Yes'
542
547
  contextYes = model.getFormContext(requestPage1, {
548
+ $$__referenceNumber: 'foobar',
543
549
  yesNoField: true
544
550
  })
545
551
  })
@@ -619,13 +625,13 @@ describe('QuestionPageController', () => {
619
625
 
620
626
  await controller1.makeGetRouteHandler()(
621
627
  requestPage1,
622
- model.getFormContext(requestPage1, {}),
628
+ model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' }),
623
629
  h
624
630
  )
625
631
 
626
632
  await controller2.makeGetRouteHandler()(
627
633
  requestPage2,
628
- model.getFormContext(requestPage2, {}),
634
+ model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' }),
629
635
  h
630
636
  )
631
637
 
@@ -856,12 +862,12 @@ describe('QuestionPageController V2', () => {
856
862
  beforeEach(() => {
857
863
  viewModel1 = controller1.getViewModel(
858
864
  requestPage1,
859
- model.getFormContext(requestPage1, {})
865
+ model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' })
860
866
  )
861
867
 
862
868
  viewModel2 = controller2.getViewModel(
863
869
  requestPage2,
864
- model.getFormContext(requestPage2, {})
870
+ model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' })
865
871
  )
866
872
  })
867
873
 
@@ -945,6 +951,7 @@ describe('QuestionPageController V2', () => {
945
951
 
946
952
  // The state below shows we said we had a UKPassport and entered details for an applicant
947
953
  const state: FormSubmissionState = {
954
+ $$__referenceNumber: 'foobar',
948
955
  ukPassport: true,
949
956
  numberOfApplicants: 2,
950
957
  applicantOneFirstName: 'Enrique',
@@ -1062,6 +1069,7 @@ describe('QuestionPageController V2', () => {
1062
1069
  } satisfies FormContextRequest
1063
1070
 
1064
1071
  const context = controller.model.getFormContext(request, {
1072
+ $$__referenceNumber: 'foobar',
1065
1073
  dateField__day: 5,
1066
1074
  dateField__month: 1,
1067
1075
  dateField__year: 2024
@@ -1097,15 +1105,19 @@ describe('QuestionPageController V2', () => {
1097
1105
 
1098
1106
  beforeEach(() => {
1099
1107
  // Empty state
1100
- context = model.getFormContext(requestPage1, {})
1108
+ context = model.getFormContext(requestPage1, {
1109
+ $$__referenceNumber: 'foobar'
1110
+ })
1101
1111
 
1102
1112
  // Question 1: Selected 'No'
1103
1113
  contextNo = model.getFormContext(requestPage1, {
1114
+ $$__referenceNumber: 'foobar',
1104
1115
  yesNoField: false
1105
1116
  })
1106
1117
 
1107
1118
  // Question 1: Selected 'Yes'
1108
1119
  contextYes = model.getFormContext(requestPage1, {
1120
+ $$__referenceNumber: 'foobar',
1109
1121
  yesNoField: true
1110
1122
  })
1111
1123
  })
@@ -1174,13 +1186,13 @@ describe('QuestionPageController V2', () => {
1174
1186
 
1175
1187
  await controller1.makeGetRouteHandler()(
1176
1188
  requestPage1,
1177
- model.getFormContext(requestPage1, {}),
1189
+ model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' }),
1178
1190
  h
1179
1191
  )
1180
1192
 
1181
1193
  await controller2.makeGetRouteHandler()(
1182
1194
  requestPage2,
1183
- model.getFormContext(requestPage2, {}),
1195
+ model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' }),
1184
1196
  h
1185
1197
  )
1186
1198
 
@@ -128,7 +128,7 @@ describe('RepeatPageController', () => {
128
128
  beforeEach(() => {
129
129
  viewModel = controller.getViewModel(
130
130
  requestPageItem,
131
- model.getFormContext(requestPageItem, {})
131
+ model.getFormContext(requestPageItem, { $$__referenceNumber: 'foobar' })
132
132
  )
133
133
  })
134
134
 
@@ -190,7 +190,9 @@ describe('RepeatPageController', () => {
190
190
  beforeEach(() => {
191
191
  viewModel = controller.getListSummaryViewModel(
192
192
  requestPageSummary,
193
- model.getFormContext(requestPageSummary, {}),
193
+ model.getFormContext(requestPageSummary, {
194
+ $$__referenceNumber: 'foobar'
195
+ }),
194
196
  list
195
197
  )
196
198
  })
@@ -40,6 +40,7 @@ import { type PageController } from '~/src/server/plugins/engine/pageControllers
40
40
  import { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'
41
41
  import { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'
42
42
  import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'
43
+ import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'
43
44
  import * as defaultServices from '~/src/server/plugins/engine/services/index.js'
44
45
  import { getUploadStatus } from '~/src/server/plugins/engine/services/uploadService.js'
45
46
  import {
@@ -265,7 +266,23 @@ export const plugin = {
265
266
 
266
267
  const cacheService = getCacheService(request.server)
267
268
  const page = getPage(model, request)
268
- const state = await page.getState(request)
269
+ let state = await page.getState(request)
270
+
271
+ if (!state.$$__referenceNumber) {
272
+ const prefix = model.def.metadata?.referenceNumberPrefix ?? ''
273
+
274
+ if (typeof prefix !== 'string') {
275
+ throw Boom.badImplementation(
276
+ 'Reference number prefix must be a string or undefined'
277
+ )
278
+ }
279
+
280
+ const referenceNumber = generateUniqueReference(prefix)
281
+ state = await page.mergeState(request, state, {
282
+ $$__referenceNumber: referenceNumber
283
+ })
284
+ }
285
+
269
286
  const flash = cacheService.getFlash(request)
270
287
  const context = model.getFormContext(request, state, flash?.errors)
271
288
  const relevantPath = page.getRelevantPath(request, context)
@@ -0,0 +1,33 @@
1
+ import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'
2
+
3
+ describe('generateUniqueReference', () => {
4
+ it('should generate a reference number with 3 segments when no prefix is provided', () => {
5
+ const referenceNumber = generateUniqueReference()
6
+ const segments = referenceNumber.split('-')
7
+
8
+ expect(segments).toHaveLength(3)
9
+
10
+ segments.forEach((segment) => {
11
+ expect(segment).toHaveLength(3)
12
+ })
13
+ })
14
+
15
+ it('should generate a reference number with 2 segments when a prefix is provided', () => {
16
+ const prefix = 'ABC'
17
+ const referenceNumber = generateUniqueReference(prefix)
18
+ const segments = referenceNumber.split('-')
19
+
20
+ expect(segments).toHaveLength(3) // 1 for prefix and 2 for segments
21
+ expect(segments[0]).toBe(prefix)
22
+
23
+ segments.slice(1).forEach((segment) => {
24
+ expect(segment).toHaveLength(3)
25
+ })
26
+ })
27
+
28
+ it('should generate different reference numbers on subsequent calls', () => {
29
+ const referenceNumber1 = generateUniqueReference()
30
+ const referenceNumber2 = generateUniqueReference()
31
+ expect(referenceNumber1).not.toBe(referenceNumber2)
32
+ })
33
+ })
@@ -0,0 +1,18 @@
1
+ import { randomBytes } from 'node:crypto'
2
+
3
+ /**
4
+ * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.
5
+ * Provides no guarantee on uniqueness.
6
+ */
7
+ export function generateUniqueReference(prefix?: string) {
8
+ const segmentLength = 3
9
+ const segmentCount = prefix ? 2 : 3
10
+ prefix = prefix ? `${prefix}-` : ''
11
+
12
+ const segments = Array.from(
13
+ { length: segmentCount },
14
+ () => randomBytes(segmentLength).toString('hex').slice(0, segmentLength) // 0-9a-f, might be good enough?
15
+ )
16
+
17
+ return `${prefix}${segments.join('-')}`.toUpperCase()
18
+ }
@@ -146,6 +146,7 @@ export interface FormContext {
146
146
  componentDefMap: Map<string, ComponentDef>
147
147
  pageMap: Map<string, PageControllerClass>
148
148
  componentMap: Map<string, Component>
149
+ referenceNumber: string
149
150
  }
150
151
 
151
152
  export type FormContextRequest = (