@defra/forms-engine-plugin 0.0.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,12 @@
1
1
  import { hasFormComponents, slugSchema } from '@defra/forms-model';
2
2
  import Boom from '@hapi/boom';
3
- // import vision from '@hapi/vision'
3
+ import vision from '@hapi/vision';
4
4
  import { isEqual } from 'date-fns';
5
5
  import Joi from 'joi';
6
- // import nunjucks, { type Environment } from 'nunjucks'
7
-
6
+ import nunjucks from 'nunjucks';
8
7
  import { PREVIEW_PATH_PREFIX } from "../../constants.js";
9
8
  import { checkEmailAddressForLiveFormSubmission, checkFormStatus, findPage, getCacheService, getPage, getStartPath, normalisePath, proceed, redirectPath } from "./helpers.js";
10
- // import {
11
- // PLUGIN_PATH,
12
- // VIEW_PATH,
13
- // context,
14
- // prepareNunjucksEnvironment
15
- // } from '~/src/server/plugins/engine/index.js'
9
+ import { PLUGIN_PATH, VIEW_PATH, context, prepareNunjucksEnvironment } from "./index.js";
16
10
  import { FormModel, SummaryViewModel } from "./models/index.js";
17
11
  import { format } from "./outputFormatters/machine/v1.js";
18
12
  import { FileUploadPageController } from "./pageControllers/FileUploadPageController.js";
@@ -27,14 +21,15 @@ export const plugin = {
27
21
  name: '@defra/forms-engine-plugin',
28
22
  dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
29
23
  multiple: true,
30
- register(server, options) {
24
+ async register(server, options) {
31
25
  const {
32
26
  model,
33
27
  services = defaultServices,
34
28
  controllers,
35
- cacheName
36
- // pluginPath = PLUGIN_PATH,
37
- // filters
29
+ cacheName,
30
+ viewPaths,
31
+ filters,
32
+ pluginPath = PLUGIN_PATH
38
33
  } = options;
39
34
  const {
40
35
  formsService
@@ -42,50 +37,44 @@ export const plugin = {
42
37
  const cacheService = new CacheService(server, cacheName);
43
38
 
44
39
  // Paths array to tell `vision` and `nunjucks` where template files are stored.
45
- // const path = [`${pluginPath}/${VIEW_PATH}`]
46
-
47
- // await server.register({
48
- // plugin: vision,
49
- // options: {
50
- // engines: {
51
- // html: {
52
- // compile: (
53
- // path: string,
54
- // compileOptions: { environment: Environment }
55
- // ) => {
56
- // const template = nunjucks.compile(
57
- // path,
58
- // compileOptions.environment
59
- // )
60
-
61
- // return (context: object | undefined) => {
62
- // return template.render(context)
63
- // }
64
- // },
65
- // prepare: (options: EngineConfigurationObject, next) => {
66
- // // Nunjucks also needs an additional path configuration
67
- // // to use the templates and macros from `govuk-frontend`
68
- // const environment = nunjucks.configure([
69
- // ...path,
70
- // 'node_modules/govuk-frontend/dist'
71
- // ])
72
-
73
- // // Applies custom filters and globals for nunjucks
74
- // // that are required by the `forms-engine-plugin`
75
- // prepareNunjucksEnvironment(environment, filters)
76
-
77
- // options.compileOptions.environment = environment
40
+ // We need to include `VIEW_PATH` in addition the runtime path (node_modules)
41
+ // to keep the local tests working
42
+ const path = [`${pluginPath}/${VIEW_PATH}`, VIEW_PATH];
78
43
 
79
- // next()
80
- // }
81
- // }
82
- // },
83
- // path,
84
- // // Provides global context used with all templates
85
- // context
86
- // }
87
- // })
44
+ // Include any additional user provided view paths so our internal views engine
45
+ // can find any files they provide from the consumer side if using custom `page.view`s
46
+ if (Array.isArray(viewPaths) && viewPaths.length) {
47
+ path.push(...viewPaths);
48
+ }
49
+ await server.register({
50
+ plugin: vision,
51
+ options: {
52
+ engines: {
53
+ html: {
54
+ compile: (path, compileOptions) => {
55
+ const template = nunjucks.compile(path, compileOptions.environment);
56
+ return context => {
57
+ return template.render(context);
58
+ };
59
+ },
60
+ prepare: (options, next) => {
61
+ // Nunjucks also needs an additional path configuration
62
+ // to use the templates and macros from `govuk-frontend`
63
+ const environment = nunjucks.configure([...path, 'node_modules/govuk-frontend/dist']);
88
64
 
65
+ // Applies custom filters and globals for nunjucks
66
+ // that are required by the `forms-engine-plugin`
67
+ prepareNunjucksEnvironment(environment, filters);
68
+ options.compileOptions.environment = environment;
69
+ next();
70
+ }
71
+ }
72
+ },
73
+ path,
74
+ // Provides global context used with all templates
75
+ context
76
+ }
77
+ });
89
78
  server.expose('cacheService', cacheService);
90
79
  server.app.model = model;
91
80
 
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["hasFormComponents","slugSchema","Boom","isEqual","Joi","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","checkFormStatus","findPage","getCacheService","getPage","getStartPath","normalisePath","proceed","redirectPath","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","formsService","cacheService","expose","app","itemCache","Map","models","loadFormPreHandler","request","h","continue","params","path","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","context","getFormContext","errors","relevantPath","getRelevantPath","summaryPath","getSummaryPath","startsWith","isForceAccess","redirectTo","next","length","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} from '@hapi/hapi'\n// import vision from '@hapi/vision'\nimport { isEqual } from 'date-fns'\nimport Joi from 'joi'\nimport { type Environment } from 'nunjucks'\n// import 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'\n// import {\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 pluginPath?: string\n filters?: Record<string, FilterFunction>\n}\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n register(server, options) {\n const {\n model,\n services = defaultServices,\n controllers,\n cacheName\n // pluginPath = PLUGIN_PATH,\n // filters\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 // const path = [`${pluginPath}/${VIEW_PATH}`]\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: (options: EngineConfigurationObject, next) => {\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 (request, h) => {\n try {\n const { uploadId } = request.params as { uploadId: string }\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","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":"AAAA,SAASA,iBAAiB,EAAEC,UAAU,QAAQ,oBAAoB;AAClE,OAAOC,IAAI,MAAM,YAAY;AAQ7B,OAAOC,MAAM,MAAM,cAAc;AACjC,SAASC,OAAO,QAAQ,UAAU;AAClC,OAAOC,GAAG,MAAM,KAAK;AACrB,OAAOC,QAAQ,MAA4B,UAAU;AAErD,SAASC,mBAAmB;AAC5B,SACEC,sCAAsC,EACtCC,eAAe,EACfC,QAAQ,EACRC,eAAe,EACfC,OAAO,EACPC,YAAY,EACZC,aAAa,EACbC,OAAO,EACPC,YAAY;AAEd,SACEC,WAAW,EACXC,SAAS,EACTC,OAAO,EACPC,0BAA0B;AAE5B,SACEC,SAAS,EACTC,gBAAgB;AAElB,SAASC,MAAM;AACf,SAASC,wBAAwB;AAEjC,SAASC,oBAAoB;AAC7B,SAASC,qBAAqB;AAE9B,OAAO,KAAKC,eAAe;AAC3B,SAASC,eAAe;AAWxB,SACEC,YAAY,EACZC,aAAa,EACbC,WAAW,EACXC,YAAY,EACZC,UAAU,EACVC,WAAW;AAEb,OAAO,KAAKC,WAAW;AACvB,SAASC,YAAY;AAarB,OAAO,MAAMC,MAAM,GAAG;EACpBC,IAAI,EAAE,4BAA4B;EAClCC,YAAY,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,CAAC;EACvDC,QAAQ,EAAE,IAAI;EACd,MAAMC,QAAQA,CAACC,MAAc,EAAEC,OAAsB,EAAE;IACrD,MAAM;MACJC,KAAK;MACLC,QAAQ,GAAGlB,eAAe;MAC1BmB,WAAW;MACXC,SAAS;MACTC,SAAS;MACTC,OAAO;MACPC,UAAU,GAAGjC;IACf,CAAC,GAAG0B,OAAO;IACX,MAAM;MAAEQ;IAAa,CAAC,GAAGN,QAAQ;IACjC,MAAMO,YAAY,GAAG,IAAIhB,YAAY,CAACM,MAAM,EAAEK,SAAS,CAAC;;IAExD;IACA;IACA;IACA,MAAMM,IAAI,GAAG,CAAC,GAAGH,UAAU,IAAIhC,SAAS,EAAE,EAAEA,SAAS,CAAC;;IAEtD;IACA;IACA,IAAIoC,KAAK,CAACC,OAAO,CAACP,SAAS,CAAC,IAAIA,SAAS,CAACQ,MAAM,EAAE;MAChDH,IAAI,CAACI,IAAI,CAAC,GAAGT,SAAS,CAAC;IACzB;IAEA,MAAMN,MAAM,CAACD,QAAQ,CAAC;MACpBJ,MAAM,EAAElC,MAAM;MACdwC,OAAO,EAAE;QACPe,OAAO,EAAE;UACPC,IAAI,EAAE;YACJC,OAAO,EAAEA,CACPP,IAAY,EACZQ,cAA4C,KACzC;cACH,MAAMC,QAAQ,GAAGxD,QAAQ,CAACsD,OAAO,CAC/BP,IAAI,EACJQ,cAAc,CAACE,WACjB,CAAC;cAED,OAAQ5C,OAA2B,IAAK;gBACtC,OAAO2C,QAAQ,CAACE,MAAM,CAAC7C,OAAO,CAAC;cACjC,CAAC;YACH,CAAC;YACD8C,OAAO,EAAEA,CACPtB,OAAkC,EAClCuB,IAA2B,KACxB;cACH;cACA;cACA,MAAMH,WAAW,GAAGzD,QAAQ,CAAC6D,SAAS,CAAC,CACrC,GAAGd,IAAI,EACP,kCAAkC,CACnC,CAAC;;cAEF;cACA;cACAjC,0BAA0B,CAAC2C,WAAW,EAAEd,OAAO,CAAC;cAEhDN,OAAO,CAACkB,cAAc,CAACE,WAAW,GAAGA,WAAW;cAEhDG,IAAI,CAAC,CAAC;YACR;UACF;QACF,CAAC;QACDb,IAAI;QACJ;QACAlC;MACF;IACF,CAAC,CAAC;IAEFuB,MAAM,CAAC0B,MAAM,CAAC,cAAc,EAAEhB,YAAY,CAAC;IAE3CV,MAAM,CAAC2B,GAAG,CAACzB,KAAK,GAAGA,KAAK;;IAExB;IACA;IACA,MAAM0B,SAAS,GAAG,IAAIC,GAAG,CAAgD,CAAC;IAC1E7B,MAAM,CAAC2B,GAAG,CAACG,MAAM,GAAGF,SAAS;IAE7B,MAAMG,kBAAkB,GAAG,MAAAA,CACzBC,OAAyC,EACzCC,CAAoC,KACjC;MACH,IAAIjC,MAAM,CAAC2B,GAAG,CAACzB,KAAK,EAAE;QACpB8B,OAAO,CAACL,GAAG,CAACzB,KAAK,GAAGF,MAAM,CAAC2B,GAAG,CAACzB,KAAK;QAEpC,OAAO+B,CAAC,CAACC,QAAQ;MACnB;MAEA,MAAM;QAAEC,MAAM;QAAExB;MAAK,CAAC,GAAGqB,OAAO;MAChC,MAAM;QAAEI;MAAK,CAAC,GAAGD,MAAM;MACvB,MAAM;QAAEE,SAAS;QAAEC,KAAK,EAAEC;MAAU,CAAC,GAAGxE,eAAe,CAAC4C,IAAI,CAAC;;MAE7D;MACA,MAAM6B,QAAQ,GAAG,MAAM/B,YAAY,CAACgC,eAAe,CAACL,IAAI,CAAC;MAEzD,MAAM;QAAEM,EAAE;QAAE,CAACH,SAAS,GAAGD;MAAM,CAAC,GAAGE,QAAQ;;MAE3C;MACA,IAAI,CAACF,KAAK,EAAE;QACV,MAAM9E,IAAI,CAACmF,QAAQ,CAAC,OAAOJ,SAAS,6BAA6BG,EAAE,EAAE,CAAC;MACxE;;MAEA;MACA;MACA;MACA;MACA;MACA;MACA,MAAME,GAAG,GAAG,GAAGF,EAAE,IAAIH,SAAS,IAAIF,SAAS,EAAE;MAC7C,IAAIQ,IAAI,GAAGjB,SAAS,CAACkB,GAAG,CAACF,GAAG,CAAC;MAE7B,IAAI,CAACC,IAAI,IAAI,CAACnF,OAAO,CAACmF,IAAI,CAACE,SAAS,EAAET,KAAK,CAACS,SAAS,CAAC,EAAE;QACtD/C,MAAM,CAACgD,MAAM,CAACC,IAAI,CAChB,2BAA2BP,EAAE,KAAKN,IAAI,KAAKG,SAAS,EACtD,CAAC;;QAED;QACA,MAAMW,UAAU,GAAG,MAAMzC,YAAY,CAAC0C,iBAAiB,CAACT,EAAE,EAAEH,SAAS,CAAC;QAEtE,IAAI,CAACW,UAAU,EAAE;UACf,MAAM1F,IAAI,CAACmF,QAAQ,CACjB,yCAAyCD,EAAE,KAAKN,IAAI,KAAKG,SAAS,EACpE,CAAC;QACH;QAEA,MAAMa,YAAY,GAChBZ,QAAQ,CAACa,iBAAiB,IAAIH,UAAU,CAACI,WAAW;QAEtDxF,sCAAsC,CAACsF,YAAY,EAAEf,SAAS,CAAC;;QAE/D;QACArC,MAAM,CAACgD,MAAM,CAACC,IAAI,CAChB,sCAAsCP,EAAE,KAAKN,IAAI,KAAKG,SAAS,EACjE,CAAC;;QAED;QACA,MAAMgB,QAAQ,GAAGlB,SAAS,GACtB,GAAGxE,mBAAmB,CAAC2F,SAAS,CAAC,CAAC,CAAC,IAAIjB,SAAS,IAAIH,IAAI,EAAE,GAC1DA,IAAI;;QAER;QACA,MAAMlC,KAAK,GAAG,IAAIvB,SAAS,CACzBuE,UAAU,EACV;UAAEK;QAAS,CAAC,EACZpD,QAAQ,EACRC,WACF,CAAC;;QAED;QACAyC,IAAI,GAAG;UAAE3C,KAAK;UAAE6C,SAAS,EAAET,KAAK,CAACS;QAAU,CAAC;QAC5CnB,SAAS,CAAC6B,GAAG,CAACb,GAAG,EAAEC,IAAI,CAAC;MAC1B;;MAEA;MACA;MACAb,OAAO,CAACL,GAAG,CAACzB,KAAK,GAAG2C,IAAI,CAAC3C,KAAK;MAE9B,OAAO+B,CAAC,CAACC,QAAQ;IACnB,CAAC;IAED,MAAMwB,eAAe,GAAGA,CACtB1B,OAAoB,EACpBC,CAA6C,KAC1C;MACH,MAAM;QAAE/B;MAAM,CAAC,GAAG8B,OAAO,CAACL,GAAG;MAE7B,MAAMgC,WAAW,GAAGzD,KAAK,GAAG,IAAIA,KAAK,CAACqD,QAAQ,EAAE,GAAG,EAAE;MACrD,OAAOlF,OAAO,CAAC2D,OAAO,EAAEC,CAAC,EAAE,GAAG0B,WAAW,GAAGxF,YAAY,CAAC+B,KAAK,CAAC,EAAE,CAAC;IACpE,CAAC;IAED,MAAM0D,qBAAqB,GAAG,MAAAA,CAC5B5B,OAAyC,EACzCC,CAA6C,EAC7C4B,WAG6C,KAC1C;MACH,MAAM;QAAElC,GAAG;QAAEQ;MAAO,CAAC,GAAGH,OAAO;MAC/B,MAAM;QAAE9B;MAAM,CAAC,GAAGyB,GAAG;MAErB,IAAI,CAACzB,KAAK,EAAE;QACV,MAAM1C,IAAI,CAACmF,QAAQ,CAAC,uBAAuBR,MAAM,CAACxB,IAAI,EAAE,CAAC;MAC3D;MAEA,MAAMD,YAAY,GAAGzC,eAAe,CAAC+D,OAAO,CAAChC,MAAM,CAAC;MACpD,MAAM8D,IAAI,GAAG5F,OAAO,CAACgC,KAAK,EAAE8B,OAAO,CAAC;MACpC,MAAMM,KAAK,GAAG,MAAMwB,IAAI,CAACC,QAAQ,CAAC/B,OAAO,CAAC;MAC1C,MAAMgC,KAAK,GAAGtD,YAAY,CAACuD,QAAQ,CAACjC,OAAO,CAAC;MAC5C,MAAMvD,OAAO,GAAGyB,KAAK,CAACgE,cAAc,CAAClC,OAAO,EAAEM,KAAK,EAAE0B,KAAK,EAAEG,MAAM,CAAC;MACnE,MAAMC,YAAY,GAAGN,IAAI,CAACO,eAAe,CAACrC,OAAO,EAAEvD,OAAO,CAAC;MAC3D,MAAM6F,WAAW,GAAGR,IAAI,CAACS,cAAc,CAAC,CAAC;;MAEzC;MACA,IAAIH,YAAY,CAACI,UAAU,CAACV,IAAI,CAACnD,IAAI,CAAC,IAAIlC,OAAO,CAACgG,aAAa,EAAE;QAC/D,OAAOZ,WAAW,CAACC,IAAI,EAAErF,OAAO,CAAC;MACnC;;MAEA;MACA,MAAMiG,UAAU,GAAG1G,QAAQ,CAACkC,KAAK,EAAEkE,YAAY,CAAC;;MAEhD;MACA,IAAIM,UAAU,EAAElD,IAAI,CAACV,MAAM,EAAE;QAC3BkB,OAAO,CAAC2C,KAAK,CAACC,SAAS,GAAGd,IAAI,CAACe,OAAO,CAACP,WAAW,CAAC;MACrD;MAEA,OAAOjG,OAAO,CAAC2D,OAAO,EAAEC,CAAC,EAAE6B,IAAI,CAACe,OAAO,CAACT,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,MAAMU,UAAU,GAAGA,CACjB9C,OAAoB,EACpBC,CAA6C,KAC1C;MACH,MAAM;QAAEE;MAAO,CAAC,GAAGH,OAAO;MAE1B,IAAI5D,aAAa,CAAC+D,MAAM,CAACxB,IAAI,CAAC,KAAK,EAAE,EAAE;QACrC,OAAO+C,eAAe,CAAC1B,OAAO,EAAEC,CAAC,CAAC;MACpC;MAEA,OAAO2B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,OAAO6B,IAAI,EAAErF,OAAO,KAAK;QAChE;QACA;QACA,MAAM;UAAEsG;QAAO,CAAC,GAAGjB,IAAI;QACvB,MAAM;UAAE5D;QAAM,CAAC,GAAG8B,OAAO,CAACL,GAAG;QAE7B,IAAI,CAACzB,KAAK,EAAE;UACV,MAAM1C,IAAI,CAACmF,QAAQ,CAAC,uBAAuBR,MAAM,CAACxB,IAAI,EAAE,CAAC;QAC3D;QAEA,IAAIoE,MAAM,EAAEC,MAAM,IAAID,MAAM,CAACC,MAAM,CAACC,IAAI,KAAK,MAAM,EAAE;UACnD,MAAM;YAAEhF;UAAQ,CAAC,GAAG8E,MAAM,CAACC,MAAM;UACjC,MAAM;YAAEE;UAAI,CAAC,GAAGjF,OAAO;;UAEvB;UACA;UACA,MAAMkF,SAAS,GAAG,IAAIvG,gBAAgB,CAACoD,OAAO,EAAE8B,IAAI,EAAErF,OAAO,CAAC;UAC9D,MAAM2G,KAAK,GAAGpG,qBAAqB,CACjCmG,SAAS,CAAC1G,OAAO,EACjB0G,SAAS,CAACE,OACZ,CAAC;;UAED;UACA,MAAMC,OAAO,GAAGzG,MAAM,CAACuG,KAAK,EAAElF,KAAK,EAAEqF,SAAS,EAAEA,SAAS,CAAC;UAE1D,MAAM;YAAED,OAAO,EAAEE;UAAS,CAAC,GAAG,MAAM/F,WAAW,CAACgG,QAAQ,CAACP,GAAG,EAAE;YAC5DI;UACF,CAAC,CAAC;UAEFI,MAAM,CAACC,MAAM,CAAClH,OAAO,CAACmH,IAAI,EAAEJ,QAAQ,CAAC;QACvC;QAEA,OAAO1B,IAAI,CAAC+B,mBAAmB,CAAC,CAAC,CAAC7D,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MACxD,CAAC,CAAC;IACJ,CAAC;IAED,MAAM6D,WAAW,GAAGA,CAClB9D,OAA2B,EAC3BC,CAA6C,KAC1C;MACH,MAAM;QAAE0C;MAAM,CAAC,GAAG3C,OAAO;MAEzB,OAAO4B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,CAAC6B,IAAI,EAAErF,OAAO,KAAK;QAC1D,MAAM;UAAEsH;QAAQ,CAAC,GAAGjC,IAAI;QACxB,MAAM;UAAEW;QAAc,CAAC,GAAGhG,OAAO;;QAEjC;QACA,IAAIgG,aAAa,IAAI,CAACnH,iBAAiB,CAACyI,OAAO,CAAC,EAAE;UAChD,OAAO1H,OAAO,CAAC2D,OAAO,EAAEC,CAAC,EAAE3D,YAAY,CAACwF,IAAI,CAACkC,IAAI,EAAErB,KAAK,CAAC,CAAC;QAC5D;QAEA,OAAOb,IAAI,CAACmC,oBAAoB,CAAC,CAAC,CAACjE,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MACzD,CAAC,CAAC;IACJ,CAAC;IAED,MAAMiE,oBAAmD,GAAG;MAC1DC,GAAG,EAAE,CACH;QACEC,MAAM,EAAErE;MACV,CAAC;IAEL,CAAC;IAED/B,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,SAAS;MACf2F,OAAO,EAAE5C,eAAe;MACxBzD,OAAO,EAAE;QACP,GAAGiG,oBAAoB;QACvBK,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E;UACR,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEFyC,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,yBAAyB;MAC/B2F,OAAO,EAAE5C,eAAe;MACxBzD,OAAO,EAAE;QACP,GAAGiG,oBAAoB;QACvBK,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E;UACR,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEF,MAAMmJ,eAA8C,GAAG;MACrDP,GAAG,EAAE,CACH;QACEC,MAAM,EAAErE;MACV,CAAC;IAEL,CAAC;IAED/B,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,0BAA0B;MAChC2F,OAAO,EAAExB,UAAU;MACnB7E,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH,YAAY,CAACsH,QAAQ,CAAC;UAChC,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEF5G,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,0CAA0C;MAChD2F,OAAO,EAAExB,UAAU;MACnB7E,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH,YAAY,CAACsH,QAAQ,CAAC;UAChC,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEF,MAAMC,gBAAsD,GAAG;MAC7DvB,OAAO,EAAE;QACPwB,KAAK,EAAE;MACT,CAAC;MACDX,GAAG,EAAE,CAAC;QAAEC,MAAM,EAAErE;MAAmB,CAAC;IACtC,CAAC;IAED/B,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,0BAA0B;MAChC2F,OAAO,EAAER,WAAW;MACpB7F,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH,YAAY,CAACsH,QAAQ,CAAC;UAChC,CAAC,CAAC;UACFtB,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H;UACV,CAAC,CAAC,CACD8H,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;IAEFlH,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,0CAA0C;MAChD2F,OAAO,EAAER,WAAW;MACpB7F,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH,YAAY,CAACsH,QAAQ,CAAC;UAChC,CAAC,CAAC;UACFtB,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H;UACV,CAAC,CAAC,CACD8H,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;;IAEF;AACJ;AACA;;IAEI;IACA,MAAMC,qBAAqB,GAAGA,CAC5BnF,OAAoB,EACpBC,CAA6C,KAC1C;MACH,MAAM;QAAEE;MAAO,CAAC,GAAGH,OAAO;MAE1B,OAAO4B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,CAAC6B,IAAI,EAAErF,OAAO,KAAK;QAC1D,IAAI,EAAEqF,IAAI,YAAY/E,oBAAoB,CAAC,EAAE;UAC3C,MAAMvB,IAAI,CAACmF,QAAQ,CAAC,+BAA+BR,MAAM,CAACxB,IAAI,EAAE,CAAC;QACnE;QAEA,OAAOmD,IAAI,CAACsD,8BAA8B,CAAC,CAAC,CAACpF,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MACnE,CAAC,CAAC;IACJ,CAAC;IAEDjC,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,wBAAwB;MAC9B2F,OAAO,EAAEa,qBAAqB;MAC9BlH,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB;UACR,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEFS,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,wCAAwC;MAC9C2F,OAAO,EAAEa,qBAAqB;MAC9BlH,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB;UACR,CAAC;QACH;MACF;IACF,CAAC,CAAC;;IAEF;IACA,MAAM8H,sBAAsB,GAAGA,CAC7BrF,OAA2B,EAC3BC,CAA6C,KAC1C;MACH,MAAM;QAAEE;MAAO,CAAC,GAAGH,OAAO;MAE1B,OAAO4B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,CAAC6B,IAAI,EAAErF,OAAO,KAAK;QAC1D,MAAM;UAAEgG;QAAc,CAAC,GAAGhG,OAAO;QAEjC,IAAIgG,aAAa,IAAI,EAAEX,IAAI,YAAY/E,oBAAoB,CAAC,EAAE;UAC5D,MAAMvB,IAAI,CAACmF,QAAQ,CAAC,+BAA+BR,MAAM,CAACxB,IAAI,EAAE,CAAC;QACnE;QAEA,OAAOmD,IAAI,CAACwD,+BAA+B,CAAC,CAAC,CAACtF,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MACpE,CAAC,CAAC;IACJ,CAAC;IAEDjC,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,wBAAwB;MAC9B2F,OAAO,EAAEe,sBAAsB;MAC/BpH,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB;UACR,CAAC,CAAC;UACF+F,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H;UACV,CAAC,CAAC,CACD+H,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;IAEFlH,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,wCAAwC;MAC9C2F,OAAO,EAAEe,sBAAsB;MAC/BpH,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB;UACR,CAAC,CAAC;UACF+F,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H;UACV,CAAC,CAAC,CACD+H,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;;IAEF;IACA,MAAMK,oBAAoB,GAAGA,CAC3BvF,OAAoB,EACpBC,CAA6C,KAC1C;MACH,MAAM;QAAEE;MAAO,CAAC,GAAGH,OAAO;MAE1B,OAAO4B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,CAAC6B,IAAI,EAAErF,OAAO,KAAK;QAC1D,IACE,EACEqF,IAAI,YAAY/E,oBAAoB,IACpC+E,IAAI,YAAYhF,wBAAwB,CACzC,EACD;UACA,MAAMtB,IAAI,CAACmF,QAAQ,CAAC,sBAAsBR,MAAM,CAACxB,IAAI,EAAE,CAAC;QAC1D;QAEA,OAAOmD,IAAI,CAAC0D,6BAA6B,CAAC,CAAC,CAACxF,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MAClE,CAAC,CAAC;IACJ,CAAC;IAEDjC,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,wCAAwC;MAC9C2F,OAAO,EAAEiB,oBAAoB;MAC7BtH,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH;UACV,CAAC;QACH;MACF;IACF,CAAC,CAAC;IAEFU,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,wDAAwD;MAC9D2F,OAAO,EAAEiB,oBAAoB;MAC7BtH,OAAO,EAAE;QACP,GAAGyG,eAAe;QAClBH,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH;UACV,CAAC;QACH;MACF;IACF,CAAC,CAAC;;IAEF;IACA,MAAMmI,qBAAqB,GAAGA,CAC5BzF,OAA2B,EAC3BC,CAA6C,KAC1C;MACH,MAAM;QAAEE;MAAO,CAAC,GAAGH,OAAO;MAE1B,OAAO4B,qBAAqB,CAAC5B,OAAO,EAAEC,CAAC,EAAE,CAAC6B,IAAI,EAAErF,OAAO,KAAK;QAC1D,MAAM;UAAEgG;QAAc,CAAC,GAAGhG,OAAO;QAEjC,IACEgG,aAAa,IACb,EACEX,IAAI,YAAY/E,oBAAoB,IACpC+E,IAAI,YAAYhF,wBAAwB,CACzC,EACD;UACA,MAAMtB,IAAI,CAACmF,QAAQ,CAAC,sBAAsBR,MAAM,CAACxB,IAAI,EAAE,CAAC;QAC1D;QAEA,OAAOmD,IAAI,CAAC4D,8BAA8B,CAAC,CAAC,CAAC1F,OAAO,EAAEvD,OAAO,EAAEwD,CAAC,CAAC;MACnE,CAAC,CAAC;IACJ,CAAC;IAEDjC,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,wCAAwC;MAC9C2F,OAAO,EAAEmB,qBAAqB;MAC9BxH,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBrE,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH;UACV,CAAC,CAAC;UACFgG,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H,YAAY;YACpBwI,OAAO,EAAEvI;UACX,CAAC,CAAC,CACD8H,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;IAEFlH,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,MAAM;MACdzF,IAAI,EAAE,wDAAwD;MAC9D2F,OAAO,EAAEmB,qBAAqB;MAC9BxH,OAAO,EAAE;QACP,GAAG4G,gBAAgB;QACnBN,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBnE,KAAK,EAAE9C,WAAW;YAClB4C,IAAI,EAAE7E,UAAU;YAChBoD,IAAI,EAAEpB,UAAU;YAChBoH,MAAM,EAAErH;UACV,CAAC,CAAC;UACFgG,OAAO,EAAE3H,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;YACJM,KAAK,EAAE1H,WAAW;YAClB2H,MAAM,EAAE7H,YAAY;YACpBwI,OAAO,EAAEvI;UACX,CAAC,CAAC,CACD8H,QAAQ,CAAC;QACd;MACF;IACF,CAAC,CAAC;IAEFlH,MAAM,CAACqG,KAAK,CAAC;MACXD,MAAM,EAAE,KAAK;MACbzF,IAAI,EAAE,2BAA2B;MACjC2F,OAAO,EAAE,MAAAA,CACPtE,OAAoB,EACpBC,CAAoC,KACjC;QACH,IAAI;UACF,MAAM;YAAE2F;UAAS,CAAC,GAAG5F,OAAO,CAACG,MAE5B;UACD,MAAM0F,MAAM,GAAG,MAAM3I,eAAe,CAAC0I,QAAQ,CAAC;UAE9C,IAAI,CAACC,MAAM,EAAE;YACX,OAAO5F,CAAC,CAACuD,QAAQ,CAAC;cAAEsC,KAAK,EAAE;YAAsB,CAAC,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC;UAC/D;UAEA,OAAO9F,CAAC,CAACuD,QAAQ,CAACqC,MAAM,CAAC;QAC3B,CAAC,CAAC,OAAOC,KAAK,EAAE;UACd9F,OAAO,CAACgB,MAAM,CAAC8E,KAAK,CAClB,CAAC,eAAe,CAAC,EACjB,4BAA4B,EAC5BA,KACF,CAAC;UACD,OAAO7F,CAAC,CAACuD,QAAQ,CAAC;YAAEsC,KAAK,EAAE;UAAqB,CAAC,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC;QAC9D;MACF,CAAC;MACD9H,OAAO,EAAE;QACP+H,OAAO,EAAE;UACPjB,KAAK,EAAE;QACT,CAAC;QACDR,QAAQ,EAAE;UACRpE,MAAM,EAAExE,GAAG,CAAC6I,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;YACxBmB,QAAQ,EAAEjK,GAAG,CAACsK,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC,CAAChB,QAAQ,CAAC;UACzC,CAAC;QACH;MACF;IACF,CAAC,CAAC;EACJ;AACF,CAAiC","ignoreList":[]}
package/README.md CHANGED
@@ -4,6 +4,33 @@ The `@defra/forms-engine-plugin` is a [plugin](https://hapi.dev/tutorials/plugin
4
4
 
5
5
  It is designed to be embedded in the frontend of a digital service and provide a convenient, configuration driven approach to building forms that are aligned to [GDS Design System](https://design-system.service.gov.uk/) guidelines.
6
6
 
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [Dependencies](#dependencies)
11
+ - [Setup](#setup)
12
+ - [Form Config](#form-config)
13
+ - [Static Assets and Styles](#static-assets-and-styles)
14
+ - [Example](#example)
15
+ - [Environment Variables](#environment-variables)
16
+ - [Options](#options)
17
+ - [Services](#services)
18
+ - [Custom Controllers](#custom-controllers)
19
+ - [Custom Filters](#custom-filters)
20
+ - [Custom Cache](#custom-cache)
21
+ - [Exemplar](#exemplar)
22
+ - [Templates](#templates)
23
+ - [Template Data](#template-data)
24
+ - [Liquid Filters](#liquid-filters)
25
+ - [Examples](#examples)
26
+ - [Templates and Views: Extending the Default Layout](#templates-and-views-extending-the-default-layout)
27
+ - [Publishing the Package](#publishing-the-package)
28
+ - [Semantic Versioning Control](#semantic-versioning-control)
29
+ - [Major-Version Release Branches](#major-version-release-branches)
30
+ - [Manual Workflow Triggers](#manual-workflow-triggers)
31
+ - [Workflow Triggers](#workflow-triggers)
32
+ - [Safety and Consistency](#safety-and-consistency)
33
+
7
34
  ## Installation
8
35
 
9
36
  `npm install @defra/forms-engine-plugin --save`
@@ -209,7 +236,7 @@ The following elements support [LiquidJS templates](https://liquidjs.com/):
209
236
  - Html (guidance) component **content**
210
237
  - Summary component **row** key title (check answers and repeater summary)
211
238
 
212
- ### Template data
239
+ ### Template Data
213
240
 
214
241
  The data the templates are evaluated against is the raw answers the user has provided up to the page they're currently on.
215
242
  For example, given a YesNoField component called `TKsWbP`, the template `{{ TKsWbP }}` would render "true" or "false" depending on how the user answered the question.
@@ -280,7 +307,9 @@ There are a number of `LiquidJS` filters available to you from within the templa
280
307
  ]
281
308
  ```
282
309
 
283
- ## Templates and views: Extending the default layout
310
+ ## Templates and views
311
+
312
+ ### Extending the default layout
284
313
 
285
314
  TODO
286
315
 
@@ -296,3 +325,45 @@ The `forms-engine-plugin` path to add can be imported from:
296
325
  Which can then be appended to the `node_modules` path `node_modules/@defra/forms-engine`.
297
326
 
298
327
  The main template layout is `govuk-frontend`'s `template.njk` file, this also needs to be added to the `path`s that nunjucks can look in.
328
+
329
+ ### Custom page view
330
+
331
+ ## Publishing the Package
332
+
333
+ Our GitHub Actions workflow (`publish.yml`) is set up to make publishing a breeze, using semantic versioning and a variety of release strategies. Here's how you can make the most of it:
334
+
335
+ ### Semantic Versioning Control
336
+
337
+ - **Patch Versioning**: This happens automatically whenever you merge code changes into `main` or any release branch.
338
+ - **Minor and Major Bumps**: You can control these by making empty commits with specific hashtags:
339
+ - Use `#minor` for a minor version bump.
340
+ - Use `#major` for a major version bump.
341
+
342
+ **Example Commands**:
343
+
344
+ ```bash
345
+ git commit --allow-empty -m "chore(release): #minor" # Minor bump
346
+ git commit --allow-empty -m "chore(release): #major" # Major bump
347
+ ```
348
+
349
+ ### Major-Version Release Branches
350
+
351
+ - **Branch Naming**: Stick to `release/vX` (like `release/v1`, `release/v2`).
352
+ - **Independent Versioning**: Each branch has its own versioning and publishes to npm with a unique dist-tag (like `2x` for `release/v2`).
353
+
354
+ ### Manual Workflow Triggers
355
+
356
+ - **Customizable Options**: You can choose the type of version bump, specify custom npm tags, and use dry run mode for testing. Dry-run is enabled by default.
357
+ - **Special Releases**: Perfect for beta releases or when you need to publish outside the usual process.
358
+
359
+ ### Workflow Triggers
360
+
361
+ 1. **Standard Development Flow**: Merging PRs to `main` automatically triggers a patch bump and publishes with the `beta` tag.
362
+ 2. **Backporting**: Cherry-pick fixes to release branches for patch bumps with specific tags (like `2x`).
363
+ 3. **Version Bumps**: Use empty commits for minor or major bumps.
364
+ 4. **Manual Releases**: Trigger these manually for special cases like beta or release candidates.
365
+
366
+ ### Safety and Consistency
367
+
368
+ - **Build Process**: Every publishing scenario includes a full build to ensure everything is in top shape, except for files like tests and lint rules.
369
+ - **Tagging Safety**: We prevent overwriting the `beta` tag by enforcing custom tags for non-standard branches. The default is set to `beta`. For release branches, the tag will be picked up from the release branch itself.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "0.0.6",
3
+ "version": "0.1.1",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "main": ".server/server/plugins/engine/index.js",
@@ -9,6 +9,14 @@
9
9
  ".public",
10
10
  "src"
11
11
  ],
12
+ "exports": {
13
+ ".": {
14
+ "sass": "./.server/client/stylesheets/application.scss",
15
+ "import": "./.server/client/javascripts/application.js",
16
+ "default": "./.server/client/javascripts/application.js"
17
+ },
18
+ "./file-upload.js": "./.server/client/javascripts/file-upload.js"
19
+ },
12
20
  "scripts": {
13
21
  "build": "npm run build:server && npm run build:client",
14
22
  "build:client": "NODE_ENV=${NODE_ENV:-production} webpack",
@@ -4,13 +4,13 @@ import {
4
4
  type Plugin,
5
5
  type ResponseObject,
6
6
  type ResponseToolkit,
7
- type RouteOptions
7
+ type RouteOptions,
8
+ type Server
8
9
  } from '@hapi/hapi'
9
- // import vision from '@hapi/vision'
10
+ import vision from '@hapi/vision'
10
11
  import { isEqual } from 'date-fns'
11
12
  import Joi from 'joi'
12
- import { type Environment } from 'nunjucks'
13
- // import nunjucks, { type Environment } from 'nunjucks'
13
+ import nunjucks, { type Environment } from 'nunjucks'
14
14
 
15
15
  import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
16
16
  import {
@@ -24,12 +24,12 @@ import {
24
24
  proceed,
25
25
  redirectPath
26
26
  } from '~/src/server/plugins/engine/helpers.js'
27
- // import {
28
- // PLUGIN_PATH,
29
- // VIEW_PATH,
30
- // context,
31
- // prepareNunjucksEnvironment
32
- // } from '~/src/server/plugins/engine/index.js'
27
+ import {
28
+ PLUGIN_PATH,
29
+ VIEW_PATH,
30
+ context,
31
+ prepareNunjucksEnvironment
32
+ } from '~/src/server/plugins/engine/index.js'
33
33
  import {
34
34
  FormModel,
35
35
  SummaryViewModel
@@ -69,70 +69,83 @@ export interface PluginOptions {
69
69
  services?: Services
70
70
  controllers?: Record<string, typeof PageController>
71
71
  cacheName?: string
72
- pluginPath?: string
72
+ viewPaths?: string[]
73
73
  filters?: Record<string, FilterFunction>
74
+ pluginPath?: string
74
75
  }
75
76
 
76
77
  export const plugin = {
77
78
  name: '@defra/forms-engine-plugin',
78
79
  dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
79
80
  multiple: true,
80
- register(server, options) {
81
+ async register(server: Server, options: PluginOptions) {
81
82
  const {
82
83
  model,
83
84
  services = defaultServices,
84
85
  controllers,
85
- cacheName
86
- // pluginPath = PLUGIN_PATH,
87
- // filters
86
+ cacheName,
87
+ viewPaths,
88
+ filters,
89
+ pluginPath = PLUGIN_PATH
88
90
  } = options
89
91
  const { formsService } = services
90
92
  const cacheService = new CacheService(server, cacheName)
91
93
 
92
94
  // Paths array to tell `vision` and `nunjucks` where template files are stored.
93
- // const path = [`${pluginPath}/${VIEW_PATH}`]
94
-
95
- // await server.register({
96
- // plugin: vision,
97
- // options: {
98
- // engines: {
99
- // html: {
100
- // compile: (
101
- // path: string,
102
- // compileOptions: { environment: Environment }
103
- // ) => {
104
- // const template = nunjucks.compile(
105
- // path,
106
- // compileOptions.environment
107
- // )
108
-
109
- // return (context: object | undefined) => {
110
- // return template.render(context)
111
- // }
112
- // },
113
- // prepare: (options: EngineConfigurationObject, next) => {
114
- // // Nunjucks also needs an additional path configuration
115
- // // to use the templates and macros from `govuk-frontend`
116
- // const environment = nunjucks.configure([
117
- // ...path,
118
- // 'node_modules/govuk-frontend/dist'
119
- // ])
120
-
121
- // // Applies custom filters and globals for nunjucks
122
- // // that are required by the `forms-engine-plugin`
123
- // prepareNunjucksEnvironment(environment, filters)
124
-
125
- // options.compileOptions.environment = environment
126
-
127
- // next()
128
- // }
129
- // }
130
- // },
131
- // path,
132
- // // Provides global context used with all templates
133
- // context
134
- // }
135
- // })
95
+ // We need to include `VIEW_PATH` in addition the runtime path (node_modules)
96
+ // to keep the local tests working
97
+ const path = [`${pluginPath}/${VIEW_PATH}`, VIEW_PATH]
98
+
99
+ // Include any additional user provided view paths so our internal views engine
100
+ // can find any files they provide from the consumer side if using custom `page.view`s
101
+ if (Array.isArray(viewPaths) && viewPaths.length) {
102
+ path.push(...viewPaths)
103
+ }
104
+
105
+ await server.register({
106
+ plugin: vision,
107
+ options: {
108
+ engines: {
109
+ html: {
110
+ compile: (
111
+ path: string,
112
+ compileOptions: { environment: Environment }
113
+ ) => {
114
+ const template = nunjucks.compile(
115
+ path,
116
+ compileOptions.environment
117
+ )
118
+
119
+ return (context: object | undefined) => {
120
+ return template.render(context)
121
+ }
122
+ },
123
+ prepare: (
124
+ options: EngineConfigurationObject,
125
+ next: (err?: Error) => void
126
+ ) => {
127
+ // Nunjucks also needs an additional path configuration
128
+ // to use the templates and macros from `govuk-frontend`
129
+ const environment = nunjucks.configure([
130
+ ...path,
131
+ 'node_modules/govuk-frontend/dist'
132
+ ])
133
+
134
+ // Applies custom filters and globals for nunjucks
135
+ // that are required by the `forms-engine-plugin`
136
+ prepareNunjucksEnvironment(environment, filters)
137
+
138
+ options.compileOptions.environment = environment
139
+
140
+ next()
141
+ }
142
+ }
143
+ },
144
+ path,
145
+ // Provides global context used with all templates
146
+ context
147
+ }
148
+ })
136
149
 
137
150
  server.expose('cacheService', cacheService)
138
151
 
@@ -711,9 +724,14 @@ export const plugin = {
711
724
  server.route({
712
725
  method: 'get',
713
726
  path: '/upload-status/{uploadId}',
714
- handler: async (request, h) => {
727
+ handler: async (
728
+ request: FormRequest,
729
+ h: Pick<ResponseToolkit, 'response'>
730
+ ) => {
715
731
  try {
716
- const { uploadId } = request.params as { uploadId: string }
732
+ const { uploadId } = request.params as unknown as {
733
+ uploadId: string
734
+ }
717
735
  const status = await getUploadStatus(uploadId)
718
736
 
719
737
  if (!status) {