@defra/forms-engine-plugin 3.0.9 → 4.0.0

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 (34) hide show
  1. package/.server/server/plugins/engine/plugin.js +3 -2
  2. package/.server/server/plugins/engine/plugin.js.map +1 -1
  3. package/.server/server/plugins/engine/routes/index.d.ts +2 -2
  4. package/.server/server/plugins/engine/routes/index.js +10 -8
  5. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  6. package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
  7. package/.server/server/plugins/engine/routes/questions.js +10 -10
  8. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  9. package/.server/server/plugins/engine/routes/repeaters/item-delete.d.ts +2 -1
  10. package/.server/server/plugins/engine/routes/repeaters/item-delete.js +31 -27
  11. package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
  12. package/.server/server/plugins/engine/routes/repeaters/summary.d.ts +2 -1
  13. package/.server/server/plugins/engine/routes/repeaters/summary.js +31 -27
  14. package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
  15. package/.server/server/plugins/engine/types.d.ts +3 -3
  16. package/.server/server/plugins/engine/types.js.map +1 -1
  17. package/.server/server/routes/types.d.ts +1 -1
  18. package/.server/server/routes/types.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/server/plugins/engine/helpers.test.ts +2 -1
  21. package/src/server/plugins/engine/pageControllers/PageController.test.ts +2 -1
  22. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +8 -4
  23. package/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts +2 -1
  24. package/src/server/plugins/engine/plugin.ts +10 -4
  25. package/src/server/plugins/engine/routes/index.test.ts +316 -0
  26. package/src/server/plugins/engine/routes/index.ts +11 -6
  27. package/src/server/plugins/engine/routes/questions.test.ts +126 -15
  28. package/src/server/plugins/engine/routes/questions.ts +71 -57
  29. package/src/server/plugins/engine/routes/repeaters/item-delete.test.ts +83 -0
  30. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +39 -33
  31. package/src/server/plugins/engine/routes/repeaters/summary.test.ts +75 -0
  32. package/src/server/plugins/engine/routes/repeaters/summary.ts +28 -22
  33. package/src/server/plugins/engine/types.ts +6 -7
  34. package/src/server/routes/types.ts +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"summary.js","names":["slugSchema","Boom","Joi","RepeatPageController","redirectOrMakeHandler","actionSchema","crumbSchema","pathSchema","stateSchema","getHandler","request","h","params","page","context","notFound","path","makeGetListSummaryRouteHandler","postHandler","isForceAccess","makePostListSummaryRouteHandler","getRoutes","getRouteOptions","postRouteOptions","method","handler","options","validate","object","keys","slug","state","payload","crumb","action","required"],"sources":["../../../../../../src/server/plugins/engine/routes/repeaters/summary.ts"],"sourcesContent":["// List summary GET route\nimport { slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type RouteOptions, type ServerRoute } from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { redirectOrMakeHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\n\nfunction getHandler(request: FormRequest, h: FormResponseToolkit) {\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\nfunction postHandler(request: FormRequestPayload, h: FormResponseToolkit) {\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\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}/{path}/summary',\n handler: getHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n },\n\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: getHandler,\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 {\n method: 'post',\n path: '/{slug}/{path}/summary',\n handler: postHandler,\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 {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: postHandler,\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}\n"],"mappings":"AAAA;AACA,SAASA,UAAU,QAAQ,oBAAoB;AAC/C,OAAOC,IAAI,MAAM,YAAY;AAE7B,OAAOC,GAAG,MAAM,KAAK;AAErB,SAASC,oBAAoB;AAC7B,SAASC,qBAAqB;AAQ9B,SACEC,YAAY,EACZC,WAAW,EACXC,UAAU,EACVC,WAAW;AAGb,SAASC,UAAUA,CAACC,OAAoB,EAAEC,CAAsB,EAAE;EAChE,MAAM;IAAEC;EAAO,CAAC,GAAGF,OAAO;EAE1B,OAAON,qBAAqB,CAACM,OAAO,EAAEC,CAAC,EAAE,CAACE,IAAI,EAAEC,OAAO,KAAK;IAC1D,IAAI,EAAED,IAAI,YAAYV,oBAAoB,CAAC,EAAE;MAC3C,MAAMF,IAAI,CAACc,QAAQ,CAAC,+BAA+BH,MAAM,CAACI,IAAI,EAAE,CAAC;IACnE;IAEA,OAAOH,IAAI,CAACI,8BAA8B,CAAC,CAAC,CAACP,OAAO,EAAEI,OAAO,EAAEH,CAAC,CAAC;EACnE,CAAC,CAAC;AACJ;AAEA,SAASO,WAAWA,CAACR,OAA2B,EAAEC,CAAsB,EAAE;EACxE,MAAM;IAAEC;EAAO,CAAC,GAAGF,OAAO;EAE1B,OAAON,qBAAqB,CAACM,OAAO,EAAEC,CAAC,EAAE,CAACE,IAAI,EAAEC,OAAO,KAAK;IAC1D,MAAM;MAAEK;IAAc,CAAC,GAAGL,OAAO;IAEjC,IAAIK,aAAa,IAAI,EAAEN,IAAI,YAAYV,oBAAoB,CAAC,EAAE;MAC5D,MAAMF,IAAI,CAACc,QAAQ,CAAC,+BAA+BH,MAAM,CAACI,IAAI,EAAE,CAAC;IACnE;IAEA,OAAOH,IAAI,CAACO,+BAA+B,CAAC,CAAC,CAACV,OAAO,EAAEI,OAAO,EAAEH,CAAC,CAAC;EACpE,CAAC,CAAC;AACJ;AAEA,OAAO,SAASU,SAASA,CACvBC,eAA8C,EAC9CC,gBAAsD,EACkB;EACxE,OAAO,CACL;IACEC,MAAM,EAAE,KAAK;IACbR,IAAI,EAAE,wBAAwB;IAC9BS,OAAO,EAAEhB,UAAU;IACnBiB,OAAO,EAAE;MACP,GAAGJ,eAAe;MAClBK,QAAQ,EAAE;QACRf,MAAM,EAAEV,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE9B,UAAU;UAChBgB,IAAI,EAAET;QACR,CAAC;MACH;IACF;EACF,CAAC,EAED;IACEiB,MAAM,EAAE,KAAK;IACbR,IAAI,EAAE,wCAAwC;IAC9CS,OAAO,EAAEhB,UAAU;IACnBiB,OAAO,EAAE;MACP,GAAGJ,eAAe;MAClBK,QAAQ,EAAE;QACRf,MAAM,EAAEV,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAEvB,WAAW;UAClBsB,IAAI,EAAE9B,UAAU;UAChBgB,IAAI,EAAET;QACR,CAAC;MACH;IACF;EACF,CAAC,EAED;IACEiB,MAAM,EAAE,MAAM;IACdR,IAAI,EAAE,wBAAwB;IAC9BS,OAAO,EAAEP,WAAW;IACpBQ,OAAO,EAAE;MACP,GAAGH,gBAAgB;MACnBI,QAAQ,EAAE;QACRf,MAAM,EAAEV,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE9B,UAAU;UAChBgB,IAAI,EAAET;QACR,CAAC,CAAC;QACFyB,OAAO,EAAE9B,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJI,KAAK,EAAE3B,WAAW;UAClB4B,MAAM,EAAE7B;QACV,CAAC,CAAC,CACD8B,QAAQ,CAAC;MACd;IACF;EACF,CAAC,EAED;IACEX,MAAM,EAAE,MAAM;IACdR,IAAI,EAAE,wCAAwC;IAC9CS,OAAO,EAAEP,WAAW;IACpBQ,OAAO,EAAE;MACP,GAAGH,gBAAgB;MACnBI,QAAQ,EAAE;QACRf,MAAM,EAAEV,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAEvB,WAAW;UAClBsB,IAAI,EAAE9B,UAAU;UAChBgB,IAAI,EAAET;QACR,CAAC,CAAC;QACFyB,OAAO,EAAE9B,GAAG,CAAC0B,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJI,KAAK,EAAE3B,WAAW;UAClB4B,MAAM,EAAE7B;QACV,CAAC,CAAC,CACD8B,QAAQ,CAAC;MACd;IACF;EACF,CAAC,CACF;AACH","ignoreList":[]}
1
+ {"version":3,"file":"summary.js","names":["slugSchema","Boom","Joi","RepeatPageController","redirectOrMakeHandler","actionSchema","crumbSchema","pathSchema","stateSchema","getHandler","onRequest","request","h","params","page","context","notFound","path","makeGetListSummaryRouteHandler","postHandler","isForceAccess","makePostListSummaryRouteHandler","getRoutes","getRouteOptions","postRouteOptions","method","handler","options","validate","object","keys","slug","state","payload","crumb","action","required"],"sources":["../../../../../../src/server/plugins/engine/routes/repeaters/summary.ts"],"sourcesContent":["// List summary GET route\nimport { slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type RouteOptions, type ServerRoute } from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { redirectOrMakeHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { type OnRequestCallback } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\n\nfunction getHandler(onRequest?: OnRequestCallback) {\n return async function (request: FormRequest, h: FormResponseToolkit) {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, onRequest, (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\nfunction postHandler(onRequest?: OnRequestCallback) {\n return async function (request: FormRequestPayload, h: FormResponseToolkit) {\n const { params } = request\n\n return redirectOrMakeHandler(request, h, onRequest, (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\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>,\n onRequest?: OnRequestCallback\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}/{path}/summary',\n handler: getHandler(onRequest),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema\n })\n }\n }\n },\n\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: getHandler(onRequest),\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 {\n method: 'post',\n path: '/{slug}/{path}/summary',\n handler: postHandler(onRequest),\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 {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/summary',\n handler: postHandler(onRequest),\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}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,4 @@
1
- import { type ComponentDef, type Event, type FormDefinition, type FormMetadata, type FormVersionMetadata, type Item, type List, type Page } from '@defra/forms-model';
1
+ import { type ComponentDef, type Event, type FormVersionMetadata, type Item, type List, type Page } from '@defra/forms-model';
2
2
  import { type PluginProperties, type Request, type ResponseObject } from '@hapi/hapi';
3
3
  import { type JoiExpression, type ValidationErrorItem } from 'joi';
4
4
  import { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js';
@@ -11,7 +11,7 @@ import { type PageController } from '~/src/server/plugins/engine/pageControllers
11
11
  import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js';
12
12
  import { type FileStatus, type FormAdapterSubmissionSchemaVersion, type UploadStatus } from '~/src/server/plugins/engine/types/enums.js';
13
13
  import { type ViewContext } from '~/src/server/plugins/nunjucks/types.js';
14
- import { type FormAction, type FormParams, type FormRequest, type FormRequestPayload, type FormResponseToolkit, type FormStatus } from '~/src/server/routes/types.js';
14
+ import { type FormAction, type FormRequest, type FormRequestPayload, type FormResponseToolkit, type FormStatus } from '~/src/server/routes/types.js';
15
15
  import { type CacheService } from '~/src/server/services/cacheService.js';
16
16
  import { type RequestOptions } from '~/src/server/services/httpService.js';
17
17
  import { type Services } from '~/src/server/types.js';
@@ -261,7 +261,7 @@ export interface ErrorMessageTemplateList {
261
261
  advancedSettingsErrors: ErrorMessageTemplate[];
262
262
  }
263
263
  export type PreparePageEventRequestOptions = (options: RequestOptions, event: Event, page: PageControllerClass, context: FormContext) => void;
264
- export type OnRequestCallback = (request: AnyFormRequest, params: FormParams, definition: FormDefinition, metadata: FormMetadata) => void;
264
+ export type OnRequestCallback = (request: AnyFormRequest, h: FormResponseToolkit, context: FormContext) => ResponseObject | FormResponseToolkit['continue'] | Promise<ResponseObject | FormResponseToolkit['continue']>;
265
265
  export type SaveAndExitHandler = (request: FormRequestPayload, h: FormResponseToolkit, context: FormContext) => ResponseObject;
266
266
  export interface PluginOptions {
267
267
  model?: FormModel;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormDefinition,\n type FormMetadata,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormParams,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\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 FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | 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 submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n 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 allowSaveAndExit: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n params: FormParams,\n definition: FormDefinition,\n metadata: FormMetadata\n) => void\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAqDA;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;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AA+Nd;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\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 FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | 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 submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n 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 allowSaveAndExit: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAkDA;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;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAiOd;AACA;AACA","ignoreList":[]}
@@ -25,7 +25,7 @@ export interface FormRequestPayloadRefs extends FormRequestRefs {
25
25
  }
26
26
  export type FormRequest = Request<FormRequestRefs>;
27
27
  export type FormRequestPayload = Request<FormRequestPayloadRefs>;
28
- export type FormResponseToolkit = Pick<ResponseToolkit, 'redirect' | 'view'>;
28
+ export type FormResponseToolkit = Pick<ResponseToolkit, 'redirect' | 'view' | 'continue'>;
29
29
  export declare enum FormAction {
30
30
  Continue = "continue",
31
31
  Validate = "validate",
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["FormAction","FormStatus"],"sources":["../../../src/server/routes/types.ts"],"sourcesContent":["import {\n type ReqRefDefaults,\n type Request,\n type ResponseToolkit\n} from '@hapi/hapi'\n\nimport { type FormPayload } from '~/src/server/plugins/engine/types.js'\n\nexport interface FormQuery extends Partial<Record<string, string>> {\n /**\n * Allow preview URL direct access without relevant page checks\n */\n force?: string\n\n /**\n * Redirect location after 'continue' form action\n */\n returnUrl?: string\n}\n\nexport interface FormParams extends Partial<Record<string, string>> {\n path: string\n slug: string\n state?: FormStatus\n}\n\nexport interface FormRequestRefs\n extends Omit<ReqRefDefaults, 'Params' | 'Payload' | 'Query'> {\n Params: FormParams\n Payload: object | undefined\n Query: FormQuery\n}\n\nexport interface FormRequestPayloadRefs extends FormRequestRefs {\n Payload: FormPayload\n}\n\nexport type FormRequest = Request<FormRequestRefs>\nexport type FormRequestPayload = Request<FormRequestPayloadRefs>\nexport type FormResponseToolkit = Pick<ResponseToolkit, 'redirect' | 'view'>\n\nexport enum FormAction {\n Continue = 'continue',\n Validate = 'validate',\n Delete = 'delete',\n AddAnother = 'add-another',\n Send = 'send',\n SaveAndExit = 'save-and-exit'\n}\n\nexport enum FormStatus {\n Draft = 'draft',\n Live = 'live'\n}\n"],"mappings":"AAyCA,WAAYA,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA;AAStB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["FormAction","FormStatus"],"sources":["../../../src/server/routes/types.ts"],"sourcesContent":["import {\n type ReqRefDefaults,\n type Request,\n type ResponseToolkit\n} from '@hapi/hapi'\n\nimport { type FormPayload } from '~/src/server/plugins/engine/types.js'\n\nexport interface FormQuery extends Partial<Record<string, string>> {\n /**\n * Allow preview URL direct access without relevant page checks\n */\n force?: string\n\n /**\n * Redirect location after 'continue' form action\n */\n returnUrl?: string\n}\n\nexport interface FormParams extends Partial<Record<string, string>> {\n path: string\n slug: string\n state?: FormStatus\n}\n\nexport interface FormRequestRefs\n extends Omit<ReqRefDefaults, 'Params' | 'Payload' | 'Query'> {\n Params: FormParams\n Payload: object | undefined\n Query: FormQuery\n}\n\nexport interface FormRequestPayloadRefs extends FormRequestRefs {\n Payload: FormPayload\n}\n\nexport type FormRequest = Request<FormRequestRefs>\nexport type FormRequestPayload = Request<FormRequestPayloadRefs>\nexport type FormResponseToolkit = Pick<\n ResponseToolkit,\n 'redirect' | 'view' | 'continue'\n>\n\nexport enum FormAction {\n Continue = 'continue',\n Validate = 'validate',\n Delete = 'delete',\n AddAnother = 'add-another',\n Send = 'send',\n SaveAndExit = 'save-and-exit'\n}\n\nexport enum FormStatus {\n Draft = 'draft',\n Live = 'live'\n}\n"],"mappings":"AA4CA,WAAYA,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA;AAStB,WAAYC,UAAU,0BAAVA,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": "3.0.9",
3
+ "version": "4.0.0",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -76,7 +76,8 @@ describe('Helpers', () => {
76
76
 
77
77
  h = {
78
78
  redirect: jest.fn().mockImplementation(() => response),
79
- view: jest.fn()
79
+ view: jest.fn(),
80
+ continue: Symbol('continue')
80
81
  }
81
82
  })
82
83
 
@@ -160,7 +160,8 @@ describe('PageController', () => {
160
160
 
161
161
  const h: FormResponseToolkit = {
162
162
  redirect: jest.fn(),
163
- view: jest.fn()
163
+ view: jest.fn(),
164
+ continue: Symbol('continue')
164
165
  }
165
166
 
166
167
  it('returns default route options', () => {
@@ -805,7 +805,8 @@ describe('QuestionPageController', () => {
805
805
 
806
806
  const h: FormResponseToolkit = {
807
807
  redirect: jest.fn().mockReturnValue(response),
808
- view: jest.fn()
808
+ view: jest.fn(),
809
+ continue: Symbol('continue')
809
810
  }
810
811
 
811
812
  it('returns default route options', () => {
@@ -1373,7 +1374,8 @@ describe('QuestionPageController V2', () => {
1373
1374
 
1374
1375
  const h: FormResponseToolkit = {
1375
1376
  redirect: jest.fn().mockReturnValue(response),
1376
- view: jest.fn()
1377
+ view: jest.fn(),
1378
+ continue: Symbol('continue')
1377
1379
  }
1378
1380
 
1379
1381
  it('returns default route options', () => {
@@ -1532,7 +1534,8 @@ describe('Save and Exit functionality', () => {
1532
1534
 
1533
1535
  const h: FormResponseToolkit = {
1534
1536
  redirect: jest.fn().mockReturnValue(response),
1535
- view: jest.fn()
1537
+ view: jest.fn(),
1538
+ continue: Symbol('continue')
1536
1539
  }
1537
1540
 
1538
1541
  beforeEach(() => {
@@ -1663,7 +1666,8 @@ describe('Save and Exit functionality', () => {
1663
1666
 
1664
1667
  const mockH = {
1665
1668
  redirect: jest.fn().mockReturnValue(mockResponse),
1666
- view: jest.fn()
1669
+ view: jest.fn(),
1670
+ continue: Symbol('continue')
1667
1671
  }
1668
1672
 
1669
1673
  const postHandler = controller1.makePostRouteHandler()
@@ -20,7 +20,8 @@ describe('SummaryPageController', () => {
20
20
  }
21
21
  const h: FormResponseToolkit = {
22
22
  redirect: jest.fn().mockReturnValue(response),
23
- view: jest.fn()
23
+ view: jest.fn(),
24
+ continue: Symbol('continue')
24
25
  }
25
26
 
26
27
  beforeEach(() => {
@@ -34,7 +34,8 @@ export const plugin = {
34
34
  saveAndExit,
35
35
  nunjucks: nunjucksOptions,
36
36
  viewContext,
37
- preparePageEventRequestOptions
37
+ preparePageEventRequestOptions,
38
+ onRequest
38
39
  } = options
39
40
 
40
41
  const cacheService =
@@ -83,10 +84,15 @@ export const plugin = {
83
84
  ...getQuestionRoutes(
84
85
  getRouteOptions,
85
86
  postRouteOptions,
86
- preparePageEventRequestOptions
87
+ preparePageEventRequestOptions,
88
+ onRequest
89
+ ),
90
+ ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions, onRequest),
91
+ ...getRepeaterItemDeleteRoutes(
92
+ getRouteOptions,
93
+ postRouteOptions,
94
+ onRequest
87
95
  ),
88
- ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions),
89
- ...getRepeaterItemDeleteRoutes(getRouteOptions, postRouteOptions),
90
96
  ...getFileUploadStatusRoutes()
91
97
  ]
92
98
 
@@ -0,0 +1,316 @@
1
+ import Boom from '@hapi/boom'
2
+ import { type ResponseObject, type ResponseToolkit } from '@hapi/hapi'
3
+
4
+ import {
5
+ findPage,
6
+ getCacheService,
7
+ getPage,
8
+ proceed
9
+ } from '~/src/server/plugins/engine/helpers.js'
10
+ import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
11
+ import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'
12
+ import { redirectOrMakeHandler } from '~/src/server/plugins/engine/routes/index.js'
13
+ import {
14
+ type AnyFormRequest,
15
+ type OnRequestCallback
16
+ } from '~/src/server/plugins/engine/types.js'
17
+ import { type FormResponseToolkit } from '~/src/server/routes/types.js'
18
+
19
+ jest.mock('~/src/server/plugins/engine/helpers')
20
+
21
+ describe('redirectOrMakeHandler', () => {
22
+ const mockServer = {} as unknown as Parameters<
23
+ typeof redirectOrMakeHandler
24
+ >[0]['server']
25
+ const mockRequest: AnyFormRequest = {
26
+ server: mockServer,
27
+ app: {},
28
+ params: { path: 'test-path' },
29
+ query: {}
30
+ } as unknown as AnyFormRequest
31
+
32
+ const mockH: FormResponseToolkit = {
33
+ redirect: jest.fn(),
34
+ view: jest.fn(),
35
+ continue: Symbol('continue')
36
+ } as unknown as FormResponseToolkit
37
+
38
+ let mockPage: PageControllerClass
39
+
40
+ const mockModel: FormModel = {
41
+ def: {
42
+ metadata: {
43
+ submission: { code: 'TEST-CODE' }
44
+ } as { submission: { code: string } }
45
+ },
46
+ getFormContext: jest.fn().mockReturnValue({
47
+ isForceAccess: false,
48
+ data: {}
49
+ })
50
+ } as unknown as FormModel
51
+
52
+ const mockMakeHandler = jest
53
+ .fn()
54
+ .mockResolvedValue({ statusCode: 200 } as ResponseObject)
55
+
56
+ beforeEach(() => {
57
+ jest.clearAllMocks()
58
+ mockRequest.app = { model: mockModel }
59
+
60
+ // Reset mock page
61
+ mockPage = {
62
+ getState: jest.fn().mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
63
+ mergeState: jest
64
+ .fn()
65
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
66
+ getRelevantPath: jest.fn().mockReturnValue('/test-path'),
67
+ getSummaryPath: jest.fn().mockReturnValue('/summary'),
68
+ getHref: jest.fn().mockReturnValue('/test-href'),
69
+ path: '/test-path'
70
+ } as unknown as PageControllerClass
71
+
72
+ // Reset mock model
73
+ mockModel.getFormContext = jest.fn().mockReturnValue({
74
+ isForceAccess: false,
75
+ data: {}
76
+ })
77
+
78
+ // Setup mocks
79
+ ;(getCacheService as jest.Mock).mockReturnValue({
80
+ getFlash: jest.fn().mockReturnValue({ errors: [] })
81
+ })
82
+ ;(getPage as jest.Mock).mockReturnValue(mockPage)
83
+ ;(findPage as jest.Mock).mockReturnValue({ next: [] })
84
+ ;(proceed as jest.Mock).mockReturnValue({ statusCode: 302 })
85
+ })
86
+
87
+ describe('onRequest callback functionality', () => {
88
+ it('should call onRequest callback when provided', async () => {
89
+ const onRequestCallback: OnRequestCallback = jest
90
+ .fn()
91
+ .mockResolvedValue(undefined)
92
+
93
+ await redirectOrMakeHandler(
94
+ mockRequest,
95
+ mockH,
96
+ onRequestCallback,
97
+ mockMakeHandler
98
+ )
99
+
100
+ expect(onRequestCallback).toHaveBeenCalledWith(
101
+ mockRequest,
102
+ mockH as ResponseToolkit,
103
+ expect.objectContaining({
104
+ isForceAccess: false,
105
+ data: {}
106
+ })
107
+ )
108
+ })
109
+
110
+ it('should not call onRequest callback when not provided', async () => {
111
+ const onRequestCallback = jest.fn()
112
+
113
+ await redirectOrMakeHandler(
114
+ mockRequest,
115
+ mockH,
116
+ undefined,
117
+ mockMakeHandler
118
+ )
119
+
120
+ expect(onRequestCallback).not.toHaveBeenCalled()
121
+ })
122
+
123
+ it('should return takeover response when onRequest returns takeover response', async () => {
124
+ const takeoverResponse = {
125
+ statusCode: 302,
126
+ headers: { location: '/redirect-url' },
127
+ _takeover: true
128
+ } as unknown as ResponseObject
129
+
130
+ const onRequestCallback: OnRequestCallback = jest
131
+ .fn()
132
+ .mockResolvedValue(takeoverResponse)
133
+
134
+ const result = await redirectOrMakeHandler(
135
+ mockRequest,
136
+ mockH,
137
+ onRequestCallback,
138
+ mockMakeHandler
139
+ )
140
+
141
+ expect(result).toBe(takeoverResponse)
142
+ expect(mockMakeHandler).not.toHaveBeenCalled()
143
+ })
144
+
145
+ it('should continue processing when onRequest returns h.continue', async () => {
146
+ const onRequestCallback: OnRequestCallback = jest
147
+ .fn()
148
+ .mockResolvedValue(mockH.continue)
149
+
150
+ await redirectOrMakeHandler(
151
+ mockRequest,
152
+ mockH,
153
+ onRequestCallback,
154
+ mockMakeHandler
155
+ )
156
+
157
+ expect(mockMakeHandler).toHaveBeenCalledWith(mockPage, expect.any(Object))
158
+ })
159
+
160
+ it('should handle onRequest callback errors', async () => {
161
+ const error = new Error('onRequest callback error')
162
+ const onRequestCallback: OnRequestCallback = jest
163
+ .fn()
164
+ .mockRejectedValue(error)
165
+
166
+ await expect(
167
+ redirectOrMakeHandler(
168
+ mockRequest,
169
+ mockH,
170
+ onRequestCallback,
171
+ mockMakeHandler
172
+ )
173
+ ).rejects.toThrow('onRequest callback error')
174
+ })
175
+ })
176
+
177
+ describe('existing functionality', () => {
178
+ it('should throw error when model is missing', async () => {
179
+ mockRequest.app = {}
180
+
181
+ await expect(
182
+ redirectOrMakeHandler(mockRequest, mockH, undefined, mockMakeHandler)
183
+ ).rejects.toThrow(Boom.notFound('No model found for /test-path'))
184
+ })
185
+
186
+ it('should call makeHandler when page is relevant', async () => {
187
+ const testPage = {
188
+ getState: jest
189
+ .fn()
190
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
191
+ mergeState: jest
192
+ .fn()
193
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
194
+ getSummaryPath: jest.fn().mockReturnValue('/summary'),
195
+ getHref: jest.fn().mockReturnValue('/test-href'),
196
+ getRelevantPath: jest.fn().mockReturnValue('/test-path'),
197
+ path: '/test-path'
198
+ } as unknown as PageControllerClass
199
+ ;(getPage as jest.Mock).mockReturnValue(testPage)
200
+
201
+ await redirectOrMakeHandler(
202
+ mockRequest,
203
+ mockH,
204
+ undefined,
205
+ mockMakeHandler
206
+ )
207
+
208
+ expect(mockMakeHandler).toHaveBeenCalledWith(testPage, expect.any(Object))
209
+ })
210
+
211
+ it('should call makeHandler when context has force access', async () => {
212
+ mockModel.getFormContext = jest.fn().mockReturnValue({
213
+ isForceAccess: true,
214
+ data: {}
215
+ })
216
+
217
+ await redirectOrMakeHandler(
218
+ mockRequest,
219
+ mockH,
220
+ undefined,
221
+ mockMakeHandler
222
+ )
223
+
224
+ expect(mockMakeHandler).toHaveBeenCalledWith(mockPage, expect.any(Object))
225
+ })
226
+
227
+ it('should redirect when page is not relevant', async () => {
228
+ const testPage = {
229
+ getState: jest
230
+ .fn()
231
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
232
+ mergeState: jest
233
+ .fn()
234
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
235
+ getSummaryPath: jest.fn().mockReturnValue('/summary'),
236
+ getHref: jest.fn().mockReturnValue('/test-href'),
237
+ getRelevantPath: jest.fn().mockReturnValue('/other-path'),
238
+ path: '/test-path'
239
+ } as unknown as PageControllerClass
240
+ ;(getPage as jest.Mock).mockReturnValue(testPage)
241
+
242
+ await redirectOrMakeHandler(
243
+ mockRequest,
244
+ mockH,
245
+ undefined,
246
+ mockMakeHandler
247
+ )
248
+
249
+ expect(proceed).toHaveBeenCalledWith(mockRequest, mockH, '/test-href')
250
+ expect(mockMakeHandler).not.toHaveBeenCalled()
251
+ })
252
+
253
+ it('should set returnUrl when redirecting and next pages exist', async () => {
254
+ const testPage = {
255
+ getState: jest
256
+ .fn()
257
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
258
+ mergeState: jest
259
+ .fn()
260
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
261
+ getSummaryPath: jest.fn().mockReturnValue('/summary'),
262
+ getRelevantPath: jest.fn().mockReturnValue('/other-path'),
263
+ path: '/test-path',
264
+ getHref: jest
265
+ .fn()
266
+ .mockReturnValueOnce('/summary-href') // First call: for summaryPath (returnUrl)
267
+ .mockReturnValueOnce('/relevant-path-href') // Second call: for relevantPath (redirect)
268
+ } as unknown as PageControllerClass
269
+ ;(getPage as jest.Mock).mockReturnValue(testPage)
270
+ ;(findPage as jest.Mock).mockReturnValue({ next: ['next-page'] })
271
+
272
+ await redirectOrMakeHandler(
273
+ mockRequest,
274
+ mockH,
275
+ undefined,
276
+ mockMakeHandler
277
+ )
278
+
279
+ expect(mockRequest.query.returnUrl).toBe('/summary-href')
280
+ expect(proceed).toHaveBeenCalledWith(
281
+ mockRequest,
282
+ mockH,
283
+ '/relevant-path-href'
284
+ )
285
+ })
286
+
287
+ it('should not set returnUrl when redirecting and no next pages exist', async () => {
288
+ const testPage = {
289
+ getState: jest
290
+ .fn()
291
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
292
+ mergeState: jest
293
+ .fn()
294
+ .mockResolvedValue({ $$__referenceNumber: 'REF-123' }),
295
+ getSummaryPath: jest.fn().mockReturnValue('/summary'),
296
+ getHref: jest.fn().mockReturnValue('/test-href'),
297
+ getRelevantPath: jest.fn().mockReturnValue('/other-path'),
298
+ path: '/test-path'
299
+ } as unknown as PageControllerClass
300
+ ;(getPage as jest.Mock).mockReturnValue(testPage)
301
+ const returnUrlBefore = mockRequest.query.returnUrl
302
+ ;(findPage as jest.Mock).mockReturnValue({ next: [] })
303
+
304
+ await redirectOrMakeHandler(
305
+ mockRequest,
306
+ mockH,
307
+ undefined,
308
+ mockMakeHandler
309
+ )
310
+
311
+ // returnUrl should not be set if next pages don't exist
312
+ expect(mockRequest.query.returnUrl).toBe(returnUrlBefore)
313
+ expect(proceed).toHaveBeenCalledWith(mockRequest, mockH, '/test-href')
314
+ })
315
+ })
316
+ })
@@ -23,6 +23,7 @@ import * as defaultServices from '~/src/server/plugins/engine/services/index.js'
23
23
  import {
24
24
  type AnyFormRequest,
25
25
  type FormContext,
26
+ type OnRequestCallback,
26
27
  type PluginOptions
27
28
  } from '~/src/server/plugins/engine/types.js'
28
29
  import {
@@ -33,6 +34,7 @@ import {
33
34
  export async function redirectOrMakeHandler(
34
35
  request: AnyFormRequest,
35
36
  h: FormResponseToolkit,
37
+ onRequest: OnRequestCallback | undefined,
36
38
  makeHandler: (
37
39
  page: PageControllerClass,
38
40
  context: FormContext
@@ -69,6 +71,14 @@ export async function redirectOrMakeHandler(
69
71
  const relevantPath = page.getRelevantPath(request, context)
70
72
  const summaryPath = page.getSummaryPath()
71
73
 
74
+ // Call the onRequest callback if it has been supplied
75
+ if (onRequest) {
76
+ const result = await onRequest(request, h, context)
77
+ if (result !== h.continue) {
78
+ return result
79
+ }
80
+ }
81
+
72
82
  // Return handler for relevant pages or preview URL direct access
73
83
  if (relevantPath.startsWith(page.path) || context.isForceAccess) {
74
84
  return makeHandler(page, context)
@@ -89,7 +99,7 @@ export function makeLoadFormPreHandler(server: Server, options: PluginOptions) {
89
99
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- hapi types are wrong
90
100
  const prefix = server.realm.modifiers.route.prefix ?? ''
91
101
 
92
- const { services = defaultServices, controllers, onRequest } = options
102
+ const { services = defaultServices, controllers } = options
93
103
 
94
104
  const { formsService } = services
95
105
 
@@ -166,11 +176,6 @@ export function makeLoadFormPreHandler(server: Server, options: PluginOptions) {
166
176
  server.app.models.set(key, item)
167
177
  }
168
178
 
169
- // Call the onRequest callback if it has been supplied
170
- if (onRequest) {
171
- onRequest(request, params, item.model.def, metadata)
172
- }
173
-
174
179
  // Assign the model to the request data
175
180
  // for use in the downstream handler
176
181
  request.app.model = item.model