UNPKG

@dwp/govuk-casa

Version:

A framework for building GOVUK Collect-And-Submit-Applications

402 lines (359 loc) 14.2 kB
// NOTE: Any changes made here must be reflected in `scripts/esm-wrapper.js` import configure from "./lib/configure.js"; import validators from "./lib/validators/index.js"; import field from "./lib/field.js"; import Plan from "./lib/Plan.js"; import JourneyContext from "./lib/JourneyContext.js"; import ValidatorFactory from "./lib/ValidatorFactory.js"; import ValidationError from "./lib/ValidationError.js"; import MutableRouter from "./lib/MutableRouter.js"; import waypointUrl from "./lib/waypoint-url.js"; import endSession from "./lib/end-session.js"; import { generateGovukErrors } from "./routes/journey.js"; import * as nunjucksFilters from "./lib/nunjucks-filters.js"; import * as constants from "./lib/constants.js"; import * as contextIdGenerators from "./lib/context-id-generators.js"; import * as corePlugins from "./core-plugins/index.js"; /** @module @dwp/govuk-casa */ export { configure, validators, field, Plan, JourneyContext, ValidatorFactory, ValidationError, MutableRouter, // Utilities waypointUrl, endSession, generateGovukErrors, // Nunjucks filters nunjucksFilters, // Constants constants, // Context ID generators contextIdGenerators, // Core plugins corePlugins, }; /* ----------------------------------------------------------------- Typedefs */ // These exist here so that consumer can import CASA's internal types /** @typedef {import("./lib/field").PageField} PageField */ /** * @typedef {object} ContextEventHandlerOptions * @property {JourneyContext} journeyContext Context including changes * @property {JourneyContext} previousContext Context prior to changes * @property {object} session Request session object * @property {ContextEventUserInfo} userInfo User-space information pass-through */ /** * @callback ContextEventHandler * @param {ContextEventHandlerOptions} opts Options * @returns {void} */ /** * @typedef {object} ContextEventUserInfo * @property {symbol} [casaRequestPhase] Request phase at which event is * triggered */ /** * @typedef {object} ContextEvent * @property {string} waypoint Waypoint to watch for changes * @property {string} [field] Field to watch for changes * @property {ContextEventHandler} handler Handler to invoke when change happens */ /** * @typedef {object} Page Page configuration. A Page is the interactive * representation of a waypoint * @property {string} waypoint The waypoint with which this page is associated * @property {string} view Template path * @property {PageHook[]} [hooks=[]] Page-specific hooks (optional, default []). * Default is `[]` * @property {PageField[]} [fields=[]] Fields to be managed on this page * (optional, default []). Default is `[]` */ /** * @typedef {object} I18nOptions * @property {string[]} dirs Directories to search for locale dictionaries * @property {string | false} [fallbackLng=false] Fallback language to use if * translations aren't available for a given key. Default is `false` * @property {string[]} [locales=['en', 'cy']] Supported locales. Default is * `['en', 'cy']` */ /** * @typedef {object} GlobalHook Hook configuration * @property {string} hook Hook name in format `<router>.<hook>` * @property {Function} middleware Middleware function to insert at the hook * point * @property {string | RegExp} [path=undefined] Only run if route path matches * this string/regexp. Default is `undefined` */ /** * @typedef {object} PageHook (extends GlobalHook) * @property {string} hook Hook name (without a scope prefix) * @property {Function} middleware Middleware function to insert at the hook * point */ /** * @typedef {object} SessionOptions * @property {string} [name=casasession] Session name. Default is `casasession` * @property {string} [secret=secret] Encryption secret. Default is `secret` * @property {number} [ttl=3600] Session ttl (seconds). Default is `3600` * @property {boolean} [secure=false] Whether to use secure session cookies. * Default is `false` * @property {boolean | string} [cookieSameSite=true] SameSite (true = Strict). * Default is `true` * @property {object} [store] Session store (default MemoryStore) * @property {string} [cookiePath] The URL path on which the session cookie is * valid (defaults to '/') */ /** * @typedef {object} IPlugin Plugin interface * @property {PluginConfigureFunction} [configure] Modify the app config * @property {PluginBootstrapFunction} [bootstrap] Modify post-configuration * artifacts */ /** * @callback PluginConfigureFunction * @param {ConfigurationOptions} config Options */ /** * @callback PluginBootstrapFunction * @param {ConfigureResult} config Options */ /** * @callback HelmetConfigurator * @param {object} config A default Helmet configuration provided by CASA * @returns {object} The modified configuration object */ /** * Mounting function. * * This will mount all of the routes and middleware in the correct order on the * given ExpressJS app. * * Once this is called, you will not be able to modify any of the routers as * they will be "sealed". * * @callback Mounter * @param {import("express").Express} app Express application * @param {object} opts Mounting options * @param {string} [opts.route='/'] Optional route to attach all * middleware/routers too. Default is `'/'` * @returns {import("express").Express} The prepared ExpressJS app instance */ /** * Configure some middleware for use in creating a new CASA app. * * @typedef {object} ConfigurationOptions Configuration options * @property {string} [mountUrl] Prefix for all URLS in browser address bar * @property {string[]} [views=[]] Template directories. Default is `[]` * @property {SessionOptions} [session] Session configuration * @property {Page[]} [pages=[]] Pages the represent waypoints. Default is `[]` * @property {GlobalHook[]} [hooks=[]] Hooks to apply. Default is `[]` * @property {IPlugin[]} [plugins=[]] Plugins. Default is `[]` * @property {I18nOptions} [i18n] I18n configuration * @property {Plan} [plan] CASA Plan * @property {ContextEvent[]} [events=[]] Handlers for JourneyContext events. * Default is `[]` * @property {HelmetConfigurator} [helmetConfigurator] Helmet configuration * manipulator function * @property {number} [formMaxParams=25] Max number of form parameters to * ingest. Default is `25` * @property {number | string} [formMaxBytes="50KB"] Max total form payload size * to ingest. Default is `"50KB"` * @property {ContextIdGenerator} [contextIdGenerator] Custom context ID * generator * @property {symbol | Function} [errorVisibility] Option to keep page errors * active on GET request * @property {boolean} [govukRebrand=false] Sets whether you wish to use the * govuk rebrand. Default is `false` */ /** * @typedef {object} ConfigureResult Result of a call to configure() function * @property {import("nunjucks").Environment} nunjucksEnv Nunjucks environment * @property {MutableRouter} staticRouter Router handling all static assets * @property {MutableRouter} ancillaryRouter Router handling ancillary routes * @property {MutableRouter} journeyRouter Router handling all waypoint requests * @property {import("express").RequestHandler[]} preMiddleware Middleware * mounted before everything * @property {import("express").RequestHandler[]} postMiddleware Middleware * mounted after everything * @property {import("express").RequestHandler[]} csrfMiddleware CSRF get/set * form middleware * @property {import("express").RequestHandler} sessionMiddleware Session * middleware * @property {import("express").RequestHandler[]} cookieParserMiddleware * Cookie-parsing middleware * @property {import("express").RequestHandler[]} i18nMiddleware I18n * preparation middleware * @property {import("express").RequestHandler} bodyParserMiddleware Body * parsing middleware * @property {Mounter} mount Function used to mount all CASA artifacts onto an * ExpressJS app * @property {ConfigurationOptions} config Ingested config supplied to * `configure()` */ /** * Configuration for generating a ValidationError. i.e. `new * ValidationError(configObject)` <br/><br/> * * The `fieldKeySuffix` is used to differentiate errors attached to the same * field name. For example, given these fields inputs ...<pre> <input * name="dateOfBirth[dd]" /> <input name="dateOfBirth[mm]" /> <input * name="dateOfBirth[yyyy]" /> </pre> * * If we wanted to generate an error specifically for the `dd` element, then * we'd include `{ fieldKeySuffix: '[dd]' }` in this config. <br/><br/> * * We can also use `focusSuffix` to control which properties of an object field * should be highlighted with a red border when in error. Looking again at the * `dateOfBirth` example above, if we did not specify any `focusSuffix`, then * all three inputs would be highlighted. However, if we use `{ focusSuffix: * ['[dd]', '[yyyy]'] }` then only the `[dd]` and `[yyyy]` inputs would be * highlighted. <br/><br/> * * The `fieldHref` and `field` properties are strictly for internal use only and * public access may be removed at any point. * * @typedef {object} ErrorMessageConfigObject * @property {string} summary Summary message * @property {string} [inline] Inline message (@deprecated now uses summary * everywhere) * @property {string | string[]} [focusSuffix] String(s) to append to URL hash * for focusing inputs * @property {string} [fieldKeySuffix] Object fields may use this to show errors * per sub-property * @property {object | ErrorMessageVariablesGenerator} [variables] * Interpolation variables * @property {string} [validator] Name of the validator * @property {string} [fieldHref] (internal) URL hash to link to field in UI, * i.e `#f-..` * @property {string} [field] (internal) Field name, including any focus suffix */ /** * Function to generate interpolation variables for injecting into the error * message string. * * @callback ErrorMessageVariablesGenerator * @param {ValidateContext} dataContext Data context * @returns {object} Variables name:value hash */ /** * @callback ErrorMessageConfigGenerator * @param {ValidateContext} dataContext Data context * @returns {string | ErrorMessageConfigObject} Compiled error message config */ /** * @typedef {string * | ErrorMessageConfigObject * | ErrorMessageConfigGenerator * | Error} ErrorMessageConfig */ /** * @typedef {object} ValidateContext Context passed to validate function * @property {JourneyContext} journeyContext Journey context * @property {string} waypoint Waypoint * @property {string} fieldName Name of field being processed * @property {any} [fieldValue] Current value of the field being validated * @property {string} [validator] Name of the validator */ /** * @callback ValidateFunction * @param {any} value * @param {ValidateContext} context Validation context * @returns {ValidationError[]} */ /** * @callback FieldProcessorFunction * @param {any} value Value to be processed * @returns {any} */ /** * @typedef {object} Validator * @property {ValidateFunction} validate Validation function * @property {FieldProcessorFunction} sanitise Sanitise a given value prior to * validation * @property {object} config Configuration * @property {string} name Validator name */ /** * @typedef {object} ValidatorConditionFunctionParams * @property {string} fieldName Field name * @property {any} fieldValue Field value * @property {string} waypoint Waypoint * @property {JourneyContext} journeyContext Journey Context */ /** * Condition functions are executed unbound. * * @callback ValidatorConditionFunction * @param {ValidatorConditionFunctionParams} context Value to be processed * @returns {boolean} True if the validators should be run */ /** * @typedef {object} PlanRoute * @property {string} source Source waypoint * @property {string} target Target waypoint * @property {string} name Name * @property {string} label Label */ /** * @callback PlanRouteCondition * @param {PlanRoute} route Route metadata * @param {JourneyContext} context Journey Context * @returns {boolean} Returns true is route should be followed */ /** * @typedef PlanTraverseOptions * @property {string} [startWaypoint] Waypoint from which to start (defaults to * first in list) * @property {string} routeName Follow routes matching this name (next | prev) * @property {Map} history Used to detect loops in traversal (INTERNAL USE ONLY) * @property {Function} [stopCondition] If true, traversal will be stopped * (useful for performance) * @property {string | PlanArbiter} [arbiter] Multiple target routes found, this * decides which to use */ /** * @typedef {object} PlanArbiterParams * @property {PlanRoute[]} targets Potential target routes that need arbitration * @property {JourneyContext} journeyContext Journey Context * @property {PlanTraverseOptions} traverseOptions Original traverse options * passed to `traverse()` */ /** * @callback PlanArbiter * @param {PlanArbiterParams} route Route metadata * @returns {PlanRoute[]} Returns all routes, excluding those that the arbiter * could eliminate */ /** * @typedef {object} JourneyContextObject Journey Context Object * @property {Record<string, any>} [data] Data * @property {any} [validation] Validation state * @property {any} [nav] Navigation meta * @property {any} [identity] Identity meta */ /** * @typedef ContextIdGeneratorParams * @property {object} args Arguments * @property {import("express").Request} args.req Request * @property {[string]} args.reservedIds List of IDs already in use in session * or request */ /** * Generates a GUID for use as a journey context ID. This ID must not clash with * any other context IDs in the given session. * * The resulting ID must match these criteria: * * - A string * - Between 1 and 64 characters * - Contain only the characters a-z, 0-9, - * * @callback ContextIdGenerator * @param {ContextIdGeneratorParams} params Parameters * @returns {string} A newly generated GUID */