@defra/forms-engine-plugin 0.1.9 → 0.1.11

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 (121) hide show
  1. package/.public/stylesheets/application.min.css +1 -1
  2. package/.public/stylesheets/application.min.css.map +1 -1
  3. package/.server/client/stylesheets/application.scss +10 -0
  4. package/.server/config/index.js +3 -14
  5. package/.server/config/index.js.map +1 -1
  6. package/.server/server/devserver/dxt-devtool-baselayout.html +71 -0
  7. package/.server/server/forms/register-as-a-unicorn-breeder.json +393 -0
  8. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +251 -0
  9. package/.server/server/index.js +11 -16
  10. package/.server/server/index.js.map +1 -1
  11. package/.server/server/plugins/engine/configureEnginePlugin.js +16 -2
  12. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  13. package/.server/server/plugins/engine/plugin.js +27 -16
  14. package/.server/server/plugins/engine/plugin.js.map +1 -1
  15. package/.server/server/plugins/engine/services/formsService.js +15 -29
  16. package/.server/server/plugins/engine/services/formsService.js.map +1 -1
  17. package/.server/server/plugins/engine/services/localFormsService.js +52 -0
  18. package/.server/server/plugins/engine/services/localFormsService.js.map +1 -0
  19. package/.server/server/plugins/engine/views/confirmation.html +1 -1
  20. package/.server/server/plugins/engine/views/file-upload.html +1 -1
  21. package/.server/server/plugins/engine/views/index.html +1 -1
  22. package/.server/server/plugins/engine/views/item-delete.html +1 -1
  23. package/.server/server/plugins/engine/views/repeat-list-summary.html +1 -1
  24. package/.server/server/plugins/engine/views/summary.html +1 -1
  25. package/.server/server/plugins/errorPages.js +4 -26
  26. package/.server/server/plugins/errorPages.js.map +1 -1
  27. package/.server/server/plugins/nunjucks/context.js +37 -28
  28. package/.server/server/plugins/nunjucks/context.js.map +1 -1
  29. package/.server/server/plugins/nunjucks/context.test.js +23 -28
  30. package/.server/server/plugins/nunjucks/context.test.js.map +1 -1
  31. package/.server/server/plugins/nunjucks/types.js +3 -4
  32. package/.server/server/plugins/nunjucks/types.js.map +1 -1
  33. package/.server/server/routes/index.js +0 -1
  34. package/.server/server/routes/index.js.map +1 -1
  35. package/.server/server/utils/file-form-service.js +134 -0
  36. package/.server/server/utils/file-form-service.js.map +1 -0
  37. package/.server/typings/hapi/index.d.js.map +1 -1
  38. package/README.md +3 -1
  39. package/package.json +7 -3
  40. package/src/client/stylesheets/application.scss +10 -0
  41. package/src/config/index.ts +4 -17
  42. package/src/server/devserver/dxt-devtool-baselayout.html +71 -0
  43. package/src/server/forms/register-as-a-unicorn-breeder.json +393 -0
  44. package/src/server/forms/register-as-a-unicorn-breeder.yaml +251 -0
  45. package/src/server/index.test.ts +4 -37
  46. package/src/server/index.ts +13 -16
  47. package/src/server/plugins/engine/components/AutocompleteField.test.ts +5 -5
  48. package/src/server/plugins/engine/components/CheckboxesField.test.ts +7 -7
  49. package/src/server/plugins/engine/components/List.test.ts +3 -0
  50. package/src/server/plugins/engine/components/RadiosField.test.ts +5 -5
  51. package/src/server/plugins/engine/components/SelectField.test.ts +5 -5
  52. package/src/server/plugins/engine/configureEnginePlugin.ts +19 -1
  53. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +4 -0
  54. package/src/server/plugins/engine/plugin.ts +43 -17
  55. package/src/server/plugins/engine/services/formsService.js +17 -35
  56. package/src/server/plugins/engine/services/localFormsService.js +49 -0
  57. package/src/server/plugins/engine/views/confirmation.html +1 -1
  58. package/src/server/plugins/engine/views/file-upload.html +1 -1
  59. package/src/server/plugins/engine/views/index.html +1 -1
  60. package/src/server/plugins/engine/views/item-delete.html +1 -1
  61. package/src/server/plugins/engine/views/repeat-list-summary.html +1 -1
  62. package/src/server/plugins/engine/views/summary.html +1 -1
  63. package/src/server/plugins/errorPages.ts +4 -26
  64. package/src/server/plugins/nunjucks/context.js +41 -31
  65. package/src/server/plugins/nunjucks/context.test.js +24 -27
  66. package/src/server/plugins/nunjucks/types.js +3 -4
  67. package/src/server/routes/index.ts +0 -1
  68. package/src/server/utils/file-form-service.js +144 -0
  69. package/src/typings/hapi/index.d.ts +3 -9
  70. package/.server/common/cookies.js +0 -55
  71. package/.server/common/cookies.js.map +0 -1
  72. package/.server/common/cookies.test.js +0 -15
  73. package/.server/common/cookies.test.js.map +0 -1
  74. package/.server/common/types.js +0 -6
  75. package/.server/common/types.js.map +0 -1
  76. package/.server/server/forms/README.md +0 -10
  77. package/.server/server/forms/report-a-terrorist.json +0 -270
  78. package/.server/server/forms/runner-components-test.json +0 -365
  79. package/.server/server/forms/test.json +0 -581
  80. package/.server/server/plugins/blankie.js +0 -29
  81. package/.server/server/plugins/blankie.js.map +0 -1
  82. package/.server/server/plugins/engine/services/formsService.test.js +0 -71
  83. package/.server/server/plugins/engine/services/formsService.test.js.map +0 -1
  84. package/.server/server/plugins/engine/views/layout.html +0 -199
  85. package/.server/server/plugins/router.js +0 -169
  86. package/.server/server/plugins/router.js.map +0 -1
  87. package/.server/server/routes/health.js +0 -15
  88. package/.server/server/routes/health.js.map +0 -1
  89. package/.server/server/routes/health.test.js +0 -32
  90. package/.server/server/routes/health.test.js.map +0 -1
  91. package/.server/server/views/404.html +0 -16
  92. package/.server/server/views/500.html +0 -19
  93. package/.server/server/views/help/accessibility-statement.html +0 -58
  94. package/.server/server/views/help/cookie-preferences.html +0 -57
  95. package/.server/server/views/help/cookies.html +0 -71
  96. package/.server/server/views/help/get-support.html +0 -37
  97. package/.server/server/views/help/privacy-notice.html +0 -68
  98. package/.server/server/views/help/terms-and-conditions.html +0 -83
  99. package/src/common/cookies.js +0 -58
  100. package/src/common/cookies.test.js +0 -23
  101. package/src/common/types.js +0 -5
  102. package/src/server/forms/README.md +0 -10
  103. package/src/server/forms/report-a-terrorist.json +0 -270
  104. package/src/server/forms/runner-components-test.json +0 -365
  105. package/src/server/forms/test.json +0 -581
  106. package/src/server/plugins/blankie.test.ts +0 -73
  107. package/src/server/plugins/blankie.ts +0 -48
  108. package/src/server/plugins/engine/services/formsService.test.js +0 -90
  109. package/src/server/plugins/engine/views/layout.html +0 -199
  110. package/src/server/plugins/router.ts +0 -201
  111. package/src/server/routes/health.js +0 -13
  112. package/src/server/routes/health.test.js +0 -35
  113. package/src/server/routes/index.test.ts +0 -125
  114. package/src/server/views/404.html +0 -16
  115. package/src/server/views/500.html +0 -19
  116. package/src/server/views/help/accessibility-statement.html +0 -58
  117. package/src/server/views/help/cookie-preferences.html +0 -57
  118. package/src/server/views/help/cookies.html +0 -71
  119. package/src/server/views/help/get-support.html +0 -37
  120. package/src/server/views/help/privacy-notice.html +0 -68
  121. package/src/server/views/help/terms-and-conditions.html +0 -83
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":["readFileSync","basename","join","Boom","StatusCodes","pkg","type","parseCookieConsent","config","createLogger","PREVIEW_PATH_PREFIX","encodeUrl","safeGenerateCrumb","logger","webpackManifest","context","request","manifestPath","get","JSON","parse","error","params","path","query","response","state","isForceAccess","isPreviewMode","startsWith","isResponseOK","isBoom","statusCode","OK","ctx","appVersion","version","assetPath","cdpEnvironment","designerUrl","feedbackLink","phaseTag","serviceBannerText","serviceName","serviceVersion","crumb","cspNonce","plugins","blankie","nonces","script","currentPath","url","search","undefined","previewMode","slug","getAssetPath","asset","googleAnalyticsTrackingId","cookieConsent"],"sources":["../../../../src/server/plugins/nunjucks/context.js"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { basename, join } from 'node:path'\n\nimport Boom from '@hapi/boom'\nimport { StatusCodes } from 'http-status-codes'\n\nimport pkg from '~/package.json' with { type: 'json' }\nimport { parseCookieConsent } from '~/src/common/cookies.js'\nimport { config } from '~/src/config/index.js'\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n encodeUrl,\n safeGenerateCrumb\n} from '~/src/server/plugins/engine/helpers.js'\n\nconst logger = createLogger()\n\n/** @type {Record<string, string> | undefined} */\nlet webpackManifest\n\n/**\n * @param {FormRequest | FormRequestPayload | null} request\n */\nexport function context(request) {\n const manifestPath = join(config.get('publicDir'), 'assets-manifest.json')\n\n if (!webpackManifest) {\n try {\n // eslint-disable-next-line -- Allow JSON type 'any'\n webpackManifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))\n } catch {\n logger.error(`Webpack ${basename(manifestPath)} not found`)\n }\n }\n\n const { params, path, query = {}, response, state } = request ?? {}\n\n const isForceAccess = 'force' in query\n const isPreviewMode = path?.startsWith(PREVIEW_PATH_PREFIX)\n\n // Only add the slug in to the context if the response is OK.\n // Footer meta links are not rendered when the slug is missing.\n const isResponseOK =\n !Boom.isBoom(response) && response?.statusCode === StatusCodes.OK\n\n /** @type {ViewContext} */\n const ctx = {\n appVersion: pkg.version,\n assetPath: '/assets',\n config: {\n cdpEnvironment: config.get('cdpEnvironment'),\n designerUrl: config.get('designerUrl'),\n feedbackLink: encodeUrl(config.get('feedbackLink')),\n phaseTag: config.get('phaseTag'),\n serviceBannerText: config.get('serviceBannerText'),\n serviceName: config.get('serviceName'),\n serviceVersion: config.get('serviceVersion')\n },\n crumb: safeGenerateCrumb(request),\n cspNonce: request?.plugins.blankie?.nonces?.script,\n currentPath: request ? `${request.path}${request.url.search}` : undefined,\n previewMode: isPreviewMode ? params?.state : undefined,\n slug: isResponseOK ? params?.slug : undefined,\n\n getAssetPath: (asset = '') => {\n return `/${webpackManifest?.[asset] ?? asset}`\n }\n }\n\n if (!isForceAccess) {\n ctx.config.googleAnalyticsTrackingId = config.get(\n 'googleAnalyticsTrackingId'\n )\n\n if (typeof state?.cookieConsent === 'string') {\n ctx.cookieConsent = parseCookieConsent(state.cookieConsent)\n }\n }\n\n return ctx\n}\n\n/**\n * @import { CookieConsent } from '~/src/common/types.js'\n * @import { ViewContext } from '~/src/server/plugins/nunjucks/types.js'\n * @import { FormRequest, FormRequestPayload } from '~/src/server/routes/types.js'\n */\n"],"mappings":"AAAA,SAASA,YAAY,QAAQ,SAAS;AACtC,SAASC,QAAQ,EAAEC,IAAI,QAAQ,WAAW;AAE1C,OAAOC,IAAI,MAAM,YAAY;AAC7B,SAASC,WAAW,QAAQ,mBAAmB;AAE/C,OAAOC,GAAG,wCAA8BC,IAAI,EAAE,MAAM;AACpD,SAASC,kBAAkB;AAC3B,SAASC,MAAM;AACf,SAASC,YAAY;AACrB,SAASC,mBAAmB;AAC5B,SACEC,SAAS,EACTC,iBAAiB;AAGnB,MAAMC,MAAM,GAAGJ,YAAY,CAAC,CAAC;;AAE7B;AACA,IAAIK,eAAe;;AAEnB;AACA;AACA;AACA,OAAO,SAASC,OAAOA,CAACC,OAAO,EAAE;EAC/B,MAAMC,YAAY,GAAGf,IAAI,CAACM,MAAM,CAACU,GAAG,CAAC,WAAW,CAAC,EAAE,sBAAsB,CAAC;EAE1E,IAAI,CAACJ,eAAe,EAAE;IACpB,IAAI;MACF;MACAA,eAAe,GAAGK,IAAI,CAACC,KAAK,CAACpB,YAAY,CAACiB,YAAY,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC,CAAC,MAAM;MACNJ,MAAM,CAACQ,KAAK,CAAC,WAAWpB,QAAQ,CAACgB,YAAY,CAAC,YAAY,CAAC;IAC7D;EACF;EAEA,MAAM;IAAEK,MAAM;IAAEC,IAAI;IAAEC,KAAK,GAAG,CAAC,CAAC;IAAEC,QAAQ;IAAEC;EAAM,CAAC,GAAGV,OAAO,IAAI,CAAC,CAAC;EAEnE,MAAMW,aAAa,GAAG,OAAO,IAAIH,KAAK;EACtC,MAAMI,aAAa,GAAGL,IAAI,EAAEM,UAAU,CAACnB,mBAAmB,CAAC;;EAE3D;EACA;EACA,MAAMoB,YAAY,GAChB,CAAC3B,IAAI,CAAC4B,MAAM,CAACN,QAAQ,CAAC,IAAIA,QAAQ,EAAEO,UAAU,KAAK5B,WAAW,CAAC6B,EAAE;;EAEnE;EACA,MAAMC,GAAG,GAAG;IACVC,UAAU,EAAE9B,GAAG,CAAC+B,OAAO;IACvBC,SAAS,EAAE,SAAS;IACpB7B,MAAM,EAAE;MACN8B,cAAc,EAAE9B,MAAM,CAACU,GAAG,CAAC,gBAAgB,CAAC;MAC5CqB,WAAW,EAAE/B,MAAM,CAACU,GAAG,CAAC,aAAa,CAAC;MACtCsB,YAAY,EAAE7B,SAAS,CAACH,MAAM,CAACU,GAAG,CAAC,cAAc,CAAC,CAAC;MACnDuB,QAAQ,EAAEjC,MAAM,CAACU,GAAG,CAAC,UAAU,CAAC;MAChCwB,iBAAiB,EAAElC,MAAM,CAACU,GAAG,CAAC,mBAAmB,CAAC;MAClDyB,WAAW,EAAEnC,MAAM,CAACU,GAAG,CAAC,aAAa,CAAC;MACtC0B,cAAc,EAAEpC,MAAM,CAACU,GAAG,CAAC,gBAAgB;IAC7C,CAAC;IACD2B,KAAK,EAAEjC,iBAAiB,CAACI,OAAO,CAAC;IACjC8B,QAAQ,EAAE9B,OAAO,EAAE+B,OAAO,CAACC,OAAO,EAAEC,MAAM,EAAEC,MAAM;IAClDC,WAAW,EAAEnC,OAAO,GAAG,GAAGA,OAAO,CAACO,IAAI,GAAGP,OAAO,CAACoC,GAAG,CAACC,MAAM,EAAE,GAAGC,SAAS;IACzEC,WAAW,EAAE3B,aAAa,GAAGN,MAAM,EAAEI,KAAK,GAAG4B,SAAS;IACtDE,IAAI,EAAE1B,YAAY,GAAGR,MAAM,EAAEkC,IAAI,GAAGF,SAAS;IAE7CG,YAAY,EAAEA,CAACC,KAAK,GAAG,EAAE,KAAK;MAC5B,OAAO,IAAI5C,eAAe,GAAG4C,KAAK,CAAC,IAAIA,KAAK,EAAE;IAChD;EACF,CAAC;EAED,IAAI,CAAC/B,aAAa,EAAE;IAClBO,GAAG,CAAC1B,MAAM,CAACmD,yBAAyB,GAAGnD,MAAM,CAACU,GAAG,CAC/C,2BACF,CAAC;IAED,IAAI,OAAOQ,KAAK,EAAEkC,aAAa,KAAK,QAAQ,EAAE;MAC5C1B,GAAG,CAAC0B,aAAa,GAAGrD,kBAAkB,CAACmB,KAAK,CAACkC,aAAa,CAAC;IAC7D;EACF;EAEA,OAAO1B,GAAG;AACZ;;AAEA;AACA;AACA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"context.js","names":["readFileSync","basename","join","Boom","StatusCodes","pkg","type","config","createLogger","PREVIEW_PATH_PREFIX","encodeUrl","safeGenerateCrumb","logger","webpackManifest","context","request","params","path","response","isPreviewMode","startsWith","isResponseOK","isBoom","statusCode","OK","pluginStorage","server","plugins","consumerViewContext","Error","baseLayoutPath","viewContext","ctx","appVersion","version","cdpEnvironment","get","designerUrl","feedbackLink","phaseTag","serviceName","serviceVersion","crumb","currentPath","url","search","previewMode","state","undefined","slug","devtoolContext","manifestPath","JSON","parse","error","assetPath","getDxtAssetPath","asset"],"sources":["../../../../src/server/plugins/nunjucks/context.js"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { basename, join } from 'node:path'\n\nimport Boom from '@hapi/boom'\nimport { StatusCodes } from 'http-status-codes'\n\nimport pkg from '~/package.json' with { type: 'json' }\nimport { config } from '~/src/config/index.js'\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n encodeUrl,\n safeGenerateCrumb\n} from '~/src/server/plugins/engine/helpers.js'\n\nconst logger = createLogger()\n\n/** @type {Record<string, string> | undefined} */\nlet webpackManifest\n\n/**\n * @param {FormRequest | FormRequestPayload | null} request\n */\nexport function context(request) {\n const { params, path, response } = request ?? {}\n\n const isPreviewMode = path?.startsWith(PREVIEW_PATH_PREFIX)\n\n // Only add the slug in to the context if the response is OK.\n // Footer meta links are not rendered when the slug is missing.\n const isResponseOK =\n !Boom.isBoom(response) && response?.statusCode === StatusCodes.OK\n\n const pluginStorage = request?.server.plugins['forms-engine-plugin']\n let consumerViewContext = {}\n\n if (!pluginStorage) {\n throw Error('context called before plugin registered')\n }\n\n if (!pluginStorage.baseLayoutPath) {\n throw Error('Missing baseLayoutPath in plugin.options.nunjucks')\n }\n\n if ('viewContext' in pluginStorage) {\n consumerViewContext = pluginStorage.viewContext(request)\n }\n\n /** @type {ViewContext} */\n const ctx = {\n // take consumers props first so we can override it\n ...consumerViewContext,\n baseLayoutPath: pluginStorage.baseLayoutPath,\n appVersion: pkg.version,\n config: {\n cdpEnvironment: config.get('cdpEnvironment'),\n designerUrl: config.get('designerUrl'),\n feedbackLink: encodeUrl(config.get('feedbackLink')),\n phaseTag: config.get('phaseTag'),\n serviceName: config.get('serviceName'),\n serviceVersion: config.get('serviceVersion')\n },\n crumb: safeGenerateCrumb(request),\n currentPath: `${request.path}${request.url.search}`,\n previewMode: isPreviewMode ? params?.state : undefined,\n slug: isResponseOK ? params?.slug : undefined\n }\n\n return ctx\n}\n\n/**\n * Returns the context for the devtool. Consumers won't have access to this.\n */\nexport function devtoolContext() {\n const manifestPath = join(config.get('publicDir'), 'assets-manifest.json')\n\n if (!webpackManifest) {\n try {\n // eslint-disable-next-line -- Allow JSON type 'any'\n webpackManifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))\n } catch {\n logger.error(`Webpack ${basename(manifestPath)} not found`)\n }\n }\n\n return {\n assetPath: '/assets',\n getDxtAssetPath: (asset = '') => {\n return `/${webpackManifest?.[asset] ?? asset}`\n }\n }\n}\n\n/**\n * @import { ViewContext } from '~/src/server/plugins/nunjucks/types.js'\n * @import { FormRequest, FormRequestPayload } from '~/src/server/routes/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,24 +1,22 @@
1
1
  import { tmpdir } from 'node:os';
2
- import { config } from "../../../config/index.js";
3
- import { encodeUrl } from "../engine/helpers.js";
4
- import { context } from "./context.js";
2
+ import { context, devtoolContext } from "./context.js";
5
3
  describe('Nunjucks context', () => {
6
4
  beforeEach(() => jest.resetModules());
7
5
  describe('Asset path', () => {
8
6
  it("should include 'assetPath' for GOV.UK Frontend icons", () => {
9
7
  const {
10
8
  assetPath
11
- } = context(null);
9
+ } = devtoolContext();
12
10
  expect(assetPath).toBe('/assets');
13
11
  });
14
12
  });
15
13
  describe('Asset helper', () => {
16
14
  it("should locate 'assets-manifest.json' assets", () => {
17
15
  const {
18
- getAssetPath
19
- } = context(null);
20
- expect(getAssetPath('example.scss')).toBe('/stylesheets/example.xxxxxxx.min.css');
21
- expect(getAssetPath('example.mjs')).toBe('/javascripts/example.xxxxxxx.min.js');
16
+ getDxtAssetPath
17
+ } = devtoolContext();
18
+ expect(getDxtAssetPath('example.scss')).toBe('/stylesheets/example.xxxxxxx.min.css');
19
+ expect(getDxtAssetPath('example.mjs')).toBe('/javascripts/example.xxxxxxx.min.js');
22
20
  });
23
21
  it("should return path when 'assets-manifest.json' is missing", async () => {
24
22
  await jest.isolateModulesAsync(async () => {
@@ -28,41 +26,32 @@ describe('Nunjucks context', () => {
28
26
 
29
27
  // Import when isolated to avoid cache
30
28
  const {
31
- context
29
+ devtoolContext
32
30
  } = await import("./context.js");
33
31
 
34
32
  // Update config for missing manifest
35
33
  config.set('publicDir', tmpdir());
36
34
  const {
37
- getAssetPath
38
- } = context(null);
35
+ getDxtAssetPath
36
+ } = devtoolContext();
39
37
 
40
38
  // Uses original paths when missing
41
- expect(getAssetPath('example.scss')).toBe('/example.scss');
42
- expect(getAssetPath('example.mjs')).toBe('/example.mjs');
39
+ expect(getDxtAssetPath('example.scss')).toBe('/example.scss');
40
+ expect(getDxtAssetPath('example.mjs')).toBe('/example.mjs');
43
41
  });
44
42
  });
45
43
  it('should return path to unknown assets', () => {
46
44
  const {
47
- getAssetPath
48
- } = context(null);
49
- expect(getAssetPath()).toBe('/');
50
- expect(getAssetPath('example.jpg')).toBe('/example.jpg');
51
- expect(getAssetPath('example.gif')).toBe('/example.gif');
45
+ getDxtAssetPath
46
+ } = devtoolContext();
47
+ expect(getDxtAssetPath()).toBe('/');
48
+ expect(getDxtAssetPath('example.jpg')).toBe('/example.jpg');
49
+ expect(getDxtAssetPath('example.gif')).toBe('/example.gif');
52
50
  });
53
51
  });
54
52
  describe('Config', () => {
55
53
  it('should include environment, phase tag and service info', () => {
56
- const ctx = context(null);
57
- expect(ctx.config).toEqual(expect.objectContaining({
58
- cdpEnvironment: config.get('cdpEnvironment'),
59
- feedbackLink: encodeUrl(config.get('feedbackLink')),
60
- googleAnalyticsTrackingId: config.get('googleAnalyticsTrackingId'),
61
- phaseTag: config.get('phaseTag'),
62
- serviceBannerText: config.get('serviceBannerText'),
63
- serviceName: config.get('serviceName'),
64
- serviceVersion: config.get('serviceVersion')
65
- }));
54
+ expect(() => context(null)).toThrow('context called before plugin registered');
66
55
  });
67
56
  });
68
57
  describe('Crumb', () => {
@@ -75,6 +64,9 @@ describe('Nunjucks context', () => {
75
64
  plugins: {
76
65
  crumb: {
77
66
  generate: jest.fn()
67
+ },
68
+ 'forms-engine-plugin': {
69
+ baseLayoutPath: 'randomValue'
78
70
  }
79
71
  }
80
72
  },
@@ -104,6 +96,9 @@ describe('Nunjucks context', () => {
104
96
  plugins: {
105
97
  crumb: {
106
98
  generate: jest.fn().mockReturnValue(mockCrumb)
99
+ },
100
+ 'forms-engine-plugin': {
101
+ baseLayoutPath: 'randomValue'
107
102
  }
108
103
  }
109
104
  },
@@ -1 +1 @@
1
- {"version":3,"file":"context.test.js","names":["tmpdir","config","encodeUrl","context","describe","beforeEach","jest","resetModules","it","assetPath","expect","toBe","getAssetPath","isolateModulesAsync","set","ctx","toEqual","objectContaining","cdpEnvironment","get","feedbackLink","googleAnalyticsTrackingId","phaseTag","serviceBannerText","serviceName","serviceVersion","malformedRequest","server","plugins","crumb","generate","fn","route","settings","path","url","search","toBeUndefined","not","toHaveBeenCalled","mockCrumb","validRequest","mockReturnValue","state","toHaveBeenCalledWith"],"sources":["../../../../src/server/plugins/nunjucks/context.test.js"],"sourcesContent":["import { tmpdir } from 'node:os'\n\nimport { config } from '~/src/config/index.js'\nimport { encodeUrl } from '~/src/server/plugins/engine/helpers.js'\nimport { context } from '~/src/server/plugins/nunjucks/context.js'\n\ndescribe('Nunjucks context', () => {\n beforeEach(() => jest.resetModules())\n\n describe('Asset path', () => {\n it(\"should include 'assetPath' for GOV.UK Frontend icons\", () => {\n const { assetPath } = context(null)\n expect(assetPath).toBe('/assets')\n })\n })\n\n describe('Asset helper', () => {\n it(\"should locate 'assets-manifest.json' assets\", () => {\n const { getAssetPath } = context(null)\n\n expect(getAssetPath('example.scss')).toBe(\n '/stylesheets/example.xxxxxxx.min.css'\n )\n\n expect(getAssetPath('example.mjs')).toBe(\n '/javascripts/example.xxxxxxx.min.js'\n )\n })\n\n it(\"should return path when 'assets-manifest.json' is missing\", async () => {\n await jest.isolateModulesAsync(async () => {\n const { config } = await import('~/src/config/index.js')\n\n // Import when isolated to avoid cache\n const { context } = await import(\n '~/src/server/plugins/nunjucks/context.js'\n )\n\n // Update config for missing manifest\n config.set('publicDir', tmpdir())\n const { getAssetPath } = context(null)\n\n // Uses original paths when missing\n expect(getAssetPath('example.scss')).toBe('/example.scss')\n expect(getAssetPath('example.mjs')).toBe('/example.mjs')\n })\n })\n\n it('should return path to unknown assets', () => {\n const { getAssetPath } = context(null)\n\n expect(getAssetPath()).toBe('/')\n expect(getAssetPath('example.jpg')).toBe('/example.jpg')\n expect(getAssetPath('example.gif')).toBe('/example.gif')\n })\n })\n\n describe('Config', () => {\n it('should include environment, phase tag and service info', () => {\n const ctx = context(null)\n\n expect(ctx.config).toEqual(\n expect.objectContaining({\n cdpEnvironment: config.get('cdpEnvironment'),\n feedbackLink: encodeUrl(config.get('feedbackLink')),\n googleAnalyticsTrackingId: config.get('googleAnalyticsTrackingId'),\n phaseTag: config.get('phaseTag'),\n serviceBannerText: config.get('serviceBannerText'),\n serviceName: config.get('serviceName'),\n serviceVersion: config.get('serviceVersion')\n })\n )\n })\n })\n\n describe('Crumb', () => {\n it('should handle malformed requests with missing state', () => {\n // While state should always exist in a valid Hapi request (it holds cookies),\n // we've seen malformed requests in production where it's missing\n const malformedRequest = /** @type {FormRequest} */ (\n /** @type {unknown} */ ({\n server: {\n plugins: {\n crumb: {\n generate: jest.fn()\n }\n }\n },\n plugins: {},\n route: {\n settings: {\n plugins: {}\n }\n },\n path: '/test',\n url: { search: '' }\n // state intentionally omitted to test real malformed requests\n })\n )\n\n const { crumb } = context(malformedRequest)\n expect(crumb).toBeUndefined()\n expect(\n malformedRequest.server.plugins.crumb.generate\n ).not.toHaveBeenCalled()\n })\n\n it('should generate crumb when state exists', () => {\n const mockCrumb = 'generated-crumb-value'\n const validRequest = /** @type {FormRequest} */ (\n /** @type {unknown} */ ({\n server: {\n plugins: {\n crumb: {\n generate: jest.fn().mockReturnValue(mockCrumb)\n }\n }\n },\n plugins: {},\n route: {\n settings: {\n plugins: {}\n }\n },\n path: '/test',\n url: { search: '' },\n state: {}\n })\n )\n\n const { crumb } = context(validRequest)\n expect(crumb).toBe(mockCrumb)\n expect(validRequest.server.plugins.crumb.generate).toHaveBeenCalledWith(\n validRequest\n )\n })\n })\n})\n\n/**\n * @import { FormRequest } from '~/src/server/routes/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"context.test.js","names":["tmpdir","context","devtoolContext","describe","beforeEach","jest","resetModules","it","assetPath","expect","toBe","getDxtAssetPath","isolateModulesAsync","config","set","toThrow","malformedRequest","server","plugins","crumb","generate","fn","baseLayoutPath","route","settings","path","url","search","toBeUndefined","not","toHaveBeenCalled","mockCrumb","validRequest","mockReturnValue","state","toHaveBeenCalledWith"],"sources":["../../../../src/server/plugins/nunjucks/context.test.js"],"sourcesContent":["import { tmpdir } from 'node:os'\n\nimport {\n context,\n devtoolContext\n} from '~/src/server/plugins/nunjucks/context.js'\n\ndescribe('Nunjucks context', () => {\n beforeEach(() => jest.resetModules())\n\n describe('Asset path', () => {\n it(\"should include 'assetPath' for GOV.UK Frontend icons\", () => {\n const { assetPath } = devtoolContext()\n expect(assetPath).toBe('/assets')\n })\n })\n\n describe('Asset helper', () => {\n it(\"should locate 'assets-manifest.json' assets\", () => {\n const { getDxtAssetPath } = devtoolContext()\n\n expect(getDxtAssetPath('example.scss')).toBe(\n '/stylesheets/example.xxxxxxx.min.css'\n )\n\n expect(getDxtAssetPath('example.mjs')).toBe(\n '/javascripts/example.xxxxxxx.min.js'\n )\n })\n\n it(\"should return path when 'assets-manifest.json' is missing\", async () => {\n await jest.isolateModulesAsync(async () => {\n const { config } = await import('~/src/config/index.js')\n\n // Import when isolated to avoid cache\n const { devtoolContext } = await import(\n '~/src/server/plugins/nunjucks/context.js'\n )\n\n // Update config for missing manifest\n config.set('publicDir', tmpdir())\n const { getDxtAssetPath } = devtoolContext()\n\n // Uses original paths when missing\n expect(getDxtAssetPath('example.scss')).toBe('/example.scss')\n expect(getDxtAssetPath('example.mjs')).toBe('/example.mjs')\n })\n })\n\n it('should return path to unknown assets', () => {\n const { getDxtAssetPath } = devtoolContext()\n\n expect(getDxtAssetPath()).toBe('/')\n expect(getDxtAssetPath('example.jpg')).toBe('/example.jpg')\n expect(getDxtAssetPath('example.gif')).toBe('/example.gif')\n })\n })\n\n describe('Config', () => {\n it('should include environment, phase tag and service info', () => {\n expect(() => context(null)).toThrow(\n 'context called before plugin registered'\n )\n })\n })\n\n describe('Crumb', () => {\n it('should handle malformed requests with missing state', () => {\n // While state should always exist in a valid Hapi request (it holds cookies),\n // we've seen malformed requests in production where it's missing\n const malformedRequest = /** @type {FormRequest} */ (\n /** @type {unknown} */ ({\n server: {\n plugins: {\n crumb: {\n generate: jest.fn()\n },\n 'forms-engine-plugin': {\n baseLayoutPath: 'randomValue'\n }\n }\n },\n plugins: {},\n route: {\n settings: {\n plugins: {}\n }\n },\n path: '/test',\n url: { search: '' }\n // state intentionally omitted to test real malformed requests\n })\n )\n\n const { crumb } = context(malformedRequest)\n expect(crumb).toBeUndefined()\n expect(\n malformedRequest.server.plugins.crumb.generate\n ).not.toHaveBeenCalled()\n })\n\n it('should generate crumb when state exists', () => {\n const mockCrumb = 'generated-crumb-value'\n const validRequest = /** @type {FormRequest} */ (\n /** @type {unknown} */ ({\n server: {\n plugins: {\n crumb: {\n generate: jest.fn().mockReturnValue(mockCrumb)\n },\n 'forms-engine-plugin': {\n baseLayoutPath: 'randomValue'\n }\n }\n },\n plugins: {},\n route: {\n settings: {\n plugins: {}\n }\n },\n path: '/test',\n url: { search: '' },\n state: {}\n })\n )\n\n const { crumb } = context(validRequest)\n expect(crumb).toBe(mockCrumb)\n expect(validRequest.server.plugins.crumb.generate).toHaveBeenCalledWith(\n validRequest\n )\n })\n })\n})\n\n/**\n * @import { FormRequest } from '~/src/server/routes/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -12,16 +12,15 @@
12
12
  /**
13
13
  * @typedef {object} ViewContext - Nunjucks view context
14
14
  * @property {string} appVersion - Application version
15
- * @property {string} assetPath - Asset path
15
+ * @property {string} [baseLayoutPath] - Base layout path
16
16
  * @property {Partial<Config>} config - Application config properties
17
- * @property {CookieConsent} [cookieConsent] - Cookie consent preferences
18
17
  * @property {string} [crumb] - Cross-Site Request Forgery (CSRF) token
19
18
  * @property {string} [cspNonce] - Content Security Policy (CSP) nonce
20
19
  * @property {string} [currentPath] - Current path
21
20
  * @property {string} [previewMode] - Preview mode
22
21
  * @property {string} [slug] - Form slug
23
- * @property {(asset?: string) => string} getAssetPath - Asset path resolver
24
22
  * @property {FormContext} [context] - the current form context
23
+ * @property {PluginOptions['viewContext']} [injectedViewContext] - the current form context
25
24
  */
26
25
 
27
26
  /**
@@ -34,8 +33,8 @@
34
33
  */
35
34
 
36
35
  /**
37
- * @import { CookieConsent } from '~/src/common/types.js'
38
36
  * @import { config } from '~/src/config/index.js'
39
37
  * @import { FormContext } from '~/src/server/plugins/engine/types.js'
38
+ * @import { PluginOptions } from '~/src/server/plugins/engine/plugin.js'
40
39
  */
41
40
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/nunjucks/types.js"],"sourcesContent":["/**\n * @typedef {object} MacroOptions\n * @property {string} [callBlock] - Nunjucks call block content\n * @property {object} [params] - Nunjucks macro params\n */\n\n/**\n * @typedef {object} RenderOptions\n * @property {object} [context] - Nunjucks render context\n */\n\n/**\n * @typedef {object} ViewContext - Nunjucks view context\n * @property {string} appVersion - Application version\n * @property {string} assetPath - Asset path\n * @property {Partial<Config>} config - Application config properties\n * @property {CookieConsent} [cookieConsent] - Cookie consent preferences\n * @property {string} [crumb] - Cross-Site Request Forgery (CSRF) token\n * @property {string} [cspNonce] - Content Security Policy (CSP) nonce\n * @property {string} [currentPath] - Current path\n * @property {string} [previewMode] - Preview mode\n * @property {string} [slug] - Form slug\n * @property {(asset?: string) => string} getAssetPath - Asset path resolver\n * @property {FormContext} [context] - the current form context\n */\n\n/**\n * @typedef {ReturnType<typeof config['getProperties']>} Config - Application config properties\n */\n\n/**\n * @typedef NunjucksContext\n * @property {ViewContext} ctx - the current nunjucks view context\n */\n\n/**\n * @import { CookieConsent } from '~/src/common/types.js'\n * @import { config } from '~/src/config/index.js'\n * @import { FormContext } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/nunjucks/types.js"],"sourcesContent":["/**\n * @typedef {object} MacroOptions\n * @property {string} [callBlock] - Nunjucks call block content\n * @property {object} [params] - Nunjucks macro params\n */\n\n/**\n * @typedef {object} RenderOptions\n * @property {object} [context] - Nunjucks render context\n */\n\n/**\n * @typedef {object} ViewContext - Nunjucks view context\n * @property {string} appVersion - Application version\n * @property {string} [baseLayoutPath] - Base layout path\n * @property {Partial<Config>} config - Application config properties\n * @property {string} [crumb] - Cross-Site Request Forgery (CSRF) token\n * @property {string} [cspNonce] - Content Security Policy (CSP) nonce\n * @property {string} [currentPath] - Current path\n * @property {string} [previewMode] - Preview mode\n * @property {string} [slug] - Form slug\n * @property {FormContext} [context] - the current form context\n * @property {PluginOptions['viewContext']} [injectedViewContext] - the current form context\n */\n\n/**\n * @typedef {ReturnType<typeof config['getProperties']>} Config - Application config properties\n */\n\n/**\n * @typedef NunjucksContext\n * @property {ViewContext} ctx - the current nunjucks view context\n */\n\n/**\n * @import { config } from '~/src/config/index.js'\n * @import { FormContext } from '~/src/server/plugins/engine/types.js'\n * @import { PluginOptions } from '~/src/server/plugins/engine/plugin.js'\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA","ignoreList":[]}
@@ -1,3 +1,2 @@
1
1
  export { default as publicRoutes } from "./public.js";
2
- export { default as healthRoute } from "./health.js";
3
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["default","publicRoutes","healthRoute"],"sources":["../../../src/server/routes/index.ts"],"sourcesContent":["export { default as publicRoutes } from '~/src/server/routes/public.js'\nexport { default as healthRoute } from '~/src/server/routes/health.js'\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,YAAY;AAChC,SAASD,OAAO,IAAIE,WAAW","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["default","publicRoutes"],"sources":["../../../src/server/routes/index.ts"],"sourcesContent":["export { default as publicRoutes } from '~/src/server/routes/public.js'\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,YAAY","ignoreList":[]}
@@ -0,0 +1,134 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'node:path';
3
+ import YAML from 'yaml';
4
+
5
+ /**
6
+ * FileFormService class
7
+ */
8
+ export class FileFormService {
9
+ /**
10
+ * The map of form metadatas by slug
11
+ * @type {Map<string, FormMetadata>}
12
+ */
13
+ #metadata = new Map();
14
+
15
+ /**
16
+ * The map of form definitions by id
17
+ * @type {Map<string, FormDefinition>}
18
+ */
19
+ #definition = new Map();
20
+
21
+ /**
22
+ * Add form from a file
23
+ * @param {string} filepath - the file path
24
+ * @param {FormMetadata} metadata - the metadata to use for this form
25
+ * @returns {Promise<FormDefinition>}
26
+ */
27
+ async addForm(filepath, metadata) {
28
+ const definition = await this.readForm(filepath);
29
+ this.#metadata.set(metadata.slug, metadata);
30
+ this.#definition.set(metadata.id, definition);
31
+ return definition;
32
+ }
33
+
34
+ /**
35
+ * Read the form definition from file
36
+ * @param {string} filepath - the file path
37
+ * @returns {Promise<FormDefinition>}
38
+ */
39
+ async readForm(filepath) {
40
+ const ext = path.extname(filepath).toLowerCase();
41
+ switch (ext) {
42
+ case '.json':
43
+ return this.readJsonForm(filepath);
44
+ case '.yaml':
45
+ return this.readYamlForm(filepath);
46
+ default:
47
+ throw new Error(`Invalid file extension '${ext}'`);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Read the form definition from a json file
53
+ * @param {string} filepath - the file path
54
+ * @returns {Promise<FormDefinition>}
55
+ */
56
+ async readJsonForm(filepath) {
57
+ /**
58
+ * @type {FormDefinition}
59
+ */
60
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
61
+ const definition = JSON.parse(await fs.readFile(filepath, 'utf8'));
62
+ return definition;
63
+ }
64
+
65
+ /**
66
+ * Read the form definition from a yaml file
67
+ * @param {string} filepath - the file path
68
+ * @returns {Promise<FormDefinition>}
69
+ */
70
+ async readYamlForm(filepath) {
71
+ /**
72
+ * @type {FormDefinition}
73
+ */
74
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
75
+ const definition = YAML.parse(await fs.readFile(filepath, 'utf8'));
76
+ return definition;
77
+ }
78
+
79
+ /**
80
+ * Get the form metadata by slug
81
+ * @param {string} slug - the form slug
82
+ * @returns {FormMetadata}
83
+ */
84
+ getFormMetadata(slug) {
85
+ const metadata = this.#metadata.get(slug);
86
+ if (!metadata) {
87
+ throw new Error(`Form metadata '${slug}' not found`);
88
+ }
89
+ return metadata;
90
+ }
91
+
92
+ /**
93
+ * Get the form defintion by id
94
+ * @param {string} id - the form id
95
+ * @returns {FormDefinition}
96
+ */
97
+ getFormDefinition(id) {
98
+ const definition = this.#definition.get(id);
99
+ if (!definition) {
100
+ throw new Error(`Form definition '${id}' not found`);
101
+ }
102
+ return definition;
103
+ }
104
+
105
+ /**
106
+ * Returns a FormsService compliant interface
107
+ * @returns {import('~/src/server/types.js').FormsService}
108
+ */
109
+ toFormsService() {
110
+ return {
111
+ /**
112
+ * Get the form metadata by slug
113
+ * @param {string} slug
114
+ * @returns {Promise<FormMetadata>}
115
+ */
116
+ getFormMetadata: slug => {
117
+ return Promise.resolve(this.getFormMetadata(slug));
118
+ },
119
+ /**
120
+ * Get the form defintion by id
121
+ * @param {string} id
122
+ * @returns {Promise<FormDefinition>}
123
+ */
124
+ getFormDefinition: id => {
125
+ return Promise.resolve(this.getFormDefinition(id));
126
+ }
127
+ };
128
+ }
129
+ }
130
+
131
+ /**
132
+ * @import { FormMetadata, FormDefinition } from '@defra/forms-model'
133
+ */
134
+ //# sourceMappingURL=file-form-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-form-service.js","names":["fs","path","YAML","FileFormService","metadata","Map","definition","addForm","filepath","readForm","set","slug","id","ext","extname","toLowerCase","readJsonForm","readYamlForm","Error","JSON","parse","readFile","getFormMetadata","get","getFormDefinition","toFormsService","Promise","resolve"],"sources":["../../../src/server/utils/file-form-service.js"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'node:path'\n\nimport YAML from 'yaml'\n\n/**\n * FileFormService class\n */\nexport class FileFormService {\n /**\n * The map of form metadatas by slug\n * @type {Map<string, FormMetadata>}\n */\n #metadata = new Map()\n\n /**\n * The map of form definitions by id\n * @type {Map<string, FormDefinition>}\n */\n #definition = new Map()\n\n /**\n * Add form from a file\n * @param {string} filepath - the file path\n * @param {FormMetadata} metadata - the metadata to use for this form\n * @returns {Promise<FormDefinition>}\n */\n async addForm(filepath, metadata) {\n const definition = await this.readForm(filepath)\n\n this.#metadata.set(metadata.slug, metadata)\n this.#definition.set(metadata.id, definition)\n\n return definition\n }\n\n /**\n * Read the form definition from file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readForm(filepath) {\n const ext = path.extname(filepath).toLowerCase()\n\n switch (ext) {\n case '.json':\n return this.readJsonForm(filepath)\n case '.yaml':\n return this.readYamlForm(filepath)\n default:\n throw new Error(`Invalid file extension '${ext}'`)\n }\n }\n\n /**\n * Read the form definition from a json file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readJsonForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = JSON.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Read the form definition from a yaml file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readYamlForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = YAML.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Get the form metadata by slug\n * @param {string} slug - the form slug\n * @returns {FormMetadata}\n */\n getFormMetadata(slug) {\n const metadata = this.#metadata.get(slug)\n\n if (!metadata) {\n throw new Error(`Form metadata '${slug}' not found`)\n }\n\n return metadata\n }\n\n /**\n * Get the form defintion by id\n * @param {string} id - the form id\n * @returns {FormDefinition}\n */\n getFormDefinition(id) {\n const definition = this.#definition.get(id)\n\n if (!definition) {\n throw new Error(`Form definition '${id}' not found`)\n }\n\n return definition\n }\n\n /**\n * Returns a FormsService compliant interface\n * @returns {import('~/src/server/types.js').FormsService}\n */\n toFormsService() {\n return {\n /**\n * Get the form metadata by slug\n * @param {string} slug\n * @returns {Promise<FormMetadata>}\n */\n getFormMetadata: (slug) => {\n return Promise.resolve(this.getFormMetadata(slug))\n },\n\n /**\n * Get the form defintion by id\n * @param {string} id\n * @returns {Promise<FormDefinition>}\n */\n getFormDefinition: (id) => {\n return Promise.resolve(this.getFormDefinition(id))\n }\n }\n }\n}\n\n/**\n * @import { FormMetadata, FormDefinition } from '@defra/forms-model'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/hapi/index.d.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unified-signatures */\n\nimport { type Plugin } from '@hapi/hapi'\nimport { type ServerYar, type Yar } from '@hapi/yar'\nimport { type Logger } from 'pino'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/index.js'\n\ndeclare module '@hapi/hapi' {\n // Here we are decorating Hapi interface types with\n // props from plugins which doesn't export @types\n interface PluginProperties {\n crumb: {\n generate?: (request: Request | FormRequest | FormRequestPayload) => string\n }\n 'forms-engine-plugin': {\n cacheService: CacheService\n }\n }\n\n interface PluginsStates {\n blankie?: {\n nonces?: {\n script?: string\n style?: string\n }\n }\n }\n\n interface Request {\n logger: Logger\n yar: Yar\n }\n\n interface RequestApplicationState {\n model?: FormModel\n }\n\n interface Server {\n logger: Logger\n yar: ServerYar\n }\n\n interface ServerApplicationState {\n model?: FormModel\n models: Map<string, { model: FormModel; updatedAt: Date }>\n }\n}\n\ndeclare module '@hapi/scooter' {\n declare const hapiScooter: {\n plugin: Plugin\n }\n\n export = hapiScooter\n}\n\ndeclare module 'blankie' {\n declare const blankie: {\n plugin: Plugin<Record<string, boolean | string | string[]>>\n }\n\n export = blankie\n}\n\ndeclare module 'blipp' {\n declare const blipp: {\n plugin: Plugin\n }\n\n export = blipp\n}\n\ndeclare module 'hapi-pulse' {\n declare const hapiPulse: {\n plugin: Plugin<{\n timeout: number\n }>\n }\n\n export = hapiPulse\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"index.d.js","names":[],"sources":["../../../src/typings/hapi/index.d.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unified-signatures */\n\nimport { type Plugin } from '@hapi/hapi'\nimport { type ServerYar, type Yar } from '@hapi/yar'\nimport { type Logger } from 'pino'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type context } from '~/src/server/plugins/engine/nunjucks.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/index.js'\n\ndeclare module '@hapi/hapi' {\n // Here we are decorating Hapi interface types with\n // props from plugins which doesn't export @types\n interface PluginProperties {\n crumb: {\n generate?: (request: Request | FormRequest | FormRequestPayload) => string\n }\n 'forms-engine-plugin': {\n baseLayoutPath: string\n cacheService: CacheService\n viewContext: context\n }\n }\n\n interface Request {\n logger: Logger\n yar: Yar\n }\n\n interface RequestApplicationState {\n model?: FormModel\n }\n\n interface Server {\n logger: Logger\n yar: ServerYar\n }\n\n interface ServerApplicationState {\n model?: FormModel\n models: Map<string, { model: FormModel; updatedAt: Date }>\n }\n}\n\ndeclare module '@hapi/scooter' {\n declare const hapiScooter: {\n plugin: Plugin\n }\n\n export = hapiScooter\n}\n\ndeclare module 'blankie' {\n declare const blankie: {\n plugin: Plugin<Record<string, boolean | string | string[]>>\n }\n\n export = blankie\n}\n\ndeclare module 'blipp' {\n declare const blipp: {\n plugin: Plugin\n }\n\n export = blipp\n}\n\ndeclare module 'hapi-pulse' {\n declare const hapiPulse: {\n plugin: Plugin<{\n timeout: number\n }>\n }\n\n export = hapiPulse\n}\n"],"mappings":"","ignoreList":[]}
package/README.md CHANGED
@@ -18,7 +18,9 @@ It is designed to be embedded in the frontend of a digital service and provide a
18
18
 
19
19
  ## Demo of DXT
20
20
 
21
- TODO: Link to CDP exemplar
21
+ If you are within the Defra network, [see a live demo](https://forms-engine-plugin-example-ui.dev.cdp-int.defra.cloud/example-form).
22
+
23
+ If you aren't within the Defra network, [see our example UI and run it locally](https://github.com/DEFRA/forms-engine-plugin-example-ui).
22
24
 
23
25
  ## Installation
24
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -21,8 +21,10 @@
21
21
  "./file-upload.min.js": "./.public/javascripts/file-upload.min.js",
22
22
  "./file-upload.min.js.map": "./.public/javascripts/file-upload.min.js.map",
23
23
  "./application.min.css": "./.public/stylesheets/application.min.css",
24
+ "./file-form-service.js": "./.server/server/utils/file-form-service.js",
24
25
  "./controllers/*": "./.server/server/plugins/engine/pageControllers/*",
25
26
  "./services/*": "./.server/server/plugins/engine/services/*",
27
+ "./engine/*": "./.server/server/plugins/engine/*",
26
28
  "./package.json": "./package.json"
27
29
  },
28
30
  "scripts": {
@@ -33,6 +35,7 @@
33
35
  "dev:debug": "concurrently \"npm run client:watch\" \"npm run server:watch:debug\" --kill-others --names \"client,server\" --prefix-colors \"red.dim,blue.dim\"",
34
36
  "format": "npm run format:check -- --write",
35
37
  "format:check": "prettier --cache --cache-location .cache/prettier --cache-strategy content --check \"**/*.{cjs,js,json,md,mjs,scss,ts}\"",
38
+ "generate-schema-docs": "node scripts/generate-schema-docs.js",
36
39
  "postinstall": "npm run setup:husky",
37
40
  "lint": "npm run lint:editorconfig && npm run lint:js && npm run lint:types",
38
41
  "lint:editorconfig": "editorconfig-checker",
@@ -60,7 +63,7 @@
60
63
  },
61
64
  "license": "SEE LICENSE IN LICENSE",
62
65
  "dependencies": {
63
- "@defra/forms-model": "^3.0.432",
66
+ "@defra/forms-model": "^3.0.438",
64
67
  "@defra/hapi-tracing": "^1.0.0",
65
68
  "@elastic/ecs-pino-format": "^1.5.0",
66
69
  "@hapi/boom": "^10.0.1",
@@ -102,7 +105,8 @@
102
105
  "pino": "^9.6.0",
103
106
  "pino-pretty": "^13.0.0",
104
107
  "proxy-agent": "^6.5.0",
105
- "resolve": "^1.22.10"
108
+ "resolve": "^1.22.10",
109
+ "yaml": "^2.7.1"
106
110
  },
107
111
  "devDependencies": {
108
112
  "@babel/cli": "^7.26.4",
@@ -12,3 +12,13 @@
12
12
  .autocomplete__option {
13
13
  @include govuk-typography-common;
14
14
  }
15
+
16
+ // An example of some user-supplied styling
17
+ // Not great practice but it illustrates the point
18
+ .govuk-header {
19
+ background: #008531;
20
+ }
21
+
22
+ .govuk-header__container {
23
+ border-bottom: 10px solid #003d16;
24
+ }
@@ -179,12 +179,6 @@ export const config = convict({
179
179
  /**
180
180
  * API integrations
181
181
  */
182
- managerUrl: {
183
- format: String,
184
- default: 'http://localhost:3001',
185
- env: 'MANAGER_URL'
186
- } as SchemaObj<string>,
187
-
188
182
  designerUrl: {
189
183
  format: String,
190
184
  default: 'http://localhost:3000',
@@ -253,19 +247,12 @@ export const config = convict({
253
247
  env: 'STAGING_PREFIX'
254
248
  },
255
249
 
256
- serviceBannerText: {
257
- doc: 'Service banner text used to show a maintenance message on all pages when set',
258
- format: String,
259
- default: '',
260
- env: 'SERVICE_BANNER_TEXT'
261
- },
262
-
263
- googleAnalyticsTrackingId: {
264
- doc: 'Google analytics tracking ID to be used when a user has opted in to additional cookies',
250
+ submissionEmailAddress: {
251
+ doc: 'Email address to send the form to (local devtool only)',
265
252
  format: String,
266
253
  default: '',
267
- env: 'GOOGLE_ANALYTICS_TRACKING_ID'
268
- }
254
+ env: 'SUBMISSION_EMAIL_ADDRESS'
255
+ } as SchemaObj<string>
269
256
  })
270
257
 
271
258
  config.validate({ allowed: 'strict' })
@@ -0,0 +1,71 @@
1
+ {% extends "govuk/template.njk" %}
2
+
3
+ {% from "govuk/components/back-link/macro.njk" import govukBackLink -%}
4
+ {% from "govuk/components/footer/macro.njk" import govukFooter -%}
5
+ {% from "govuk/components/phase-banner/macro.njk" import govukPhaseBanner -%}
6
+ {% from "govuk/components/skip-link/macro.njk" import govukSkipLink -%}
7
+ {% from "govuk/macros/attributes.njk" import govukAttributes -%}
8
+ {% from "components/service-banner/macro.njk" import appServiceBanner -%}
9
+ {% from "components/tag-env/macro.njk" import appTagEnv -%}
10
+ {% from "govuk/components/cookie-banner/macro.njk" import govukCookieBanner -%}
11
+ {% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner -%}
12
+
13
+ {% set productName %}
14
+ {{ appTagEnv({ env: "devtool" }) }}
15
+ {% endset %}
16
+
17
+ {% block head %}
18
+ <link rel="preload" as="font" href="{{ assetPath }}/fonts/bold-b542beb274-v2.woff2" type="font/woff2" crossorigin="anonymous">
19
+ <link rel="preload" as="font" href="{{ assetPath }}/fonts/light-94a07e06a1-v2.woff2" type="font/woff2" crossorigin="anonymous">
20
+ <link rel="stylesheet" href="{{ getDxtAssetPath("stylesheets/application.scss") }}">
21
+ {% endblock %}
22
+
23
+ {% block pageTitle -%}
24
+ {{ "Error: " if errors | length }}{{ pageTitle | evaluate }} - {{ name if name else config.serviceName }} - GOV.UK
25
+ {%- endblock %}
26
+
27
+ {% block skipLink %}
28
+ {{ govukSkipLink({
29
+ href: '#main-content',
30
+ text: 'Skip to main content'
31
+ }) }}
32
+ {% endblock %}
33
+
34
+ {% block header %}
35
+ {{ govukHeader({
36
+ homepageUrl: currentPath if context.isForceAccess else "https://defra.github.io/forms-engine-plugin/",
37
+ containerClasses: "govuk-width-container",
38
+ productName: productName | safe | trim,
39
+ serviceName: "Digital Express Toolkit",
40
+ serviceUrl: currentPath if context.isForceAccess else serviceUrl
41
+ }) }}
42
+ {% endblock %}
43
+
44
+ {% block beforeContent %}
45
+ {% if backLink %}
46
+ {{ govukBackLink(backLink) }}
47
+ {% endif %}
48
+ {% endblock %}
49
+
50
+ {% block content %}
51
+ <h1 class="govuk-heading-l">Default page template</h1>
52
+ {% endblock %}
53
+
54
+ {% block bodyEnd %}
55
+ <script type="module" nonce="{{ cspNonce }}" src="{{ getDxtAssetPath("application.js") }}"></script>
56
+ {% endblock %}
57
+
58
+ {% block footer %}
59
+ {% set meta = {
60
+ items: [
61
+ {
62
+ href: 'https://defra.github.io/forms-engine-plugin/',
63
+ text: 'DXT documentation'
64
+ }
65
+ ]
66
+ } if slug %}
67
+
68
+ {% if not context.isForceAccess %}
69
+ {{ govukFooter({ meta: meta }) }}
70
+ {% endif %}
71
+ {% endblock %}