@dwp/govuk-casa 7.0.3 → 7.0.8
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.
- package/CHANGELOG.md +22 -0
- package/dist/assets/css/casa-ie8.css +1 -0
- package/dist/assets/css/casa.css +1 -0
- package/dist/casa/css/casa-ie8.css +1 -1
- package/dist/casa/css/casa.css +1 -1
- package/dist/casa.d.ts +11 -0
- package/dist/casa.js +46 -0
- package/dist/lib/CasaTemplateLoader.d.ts +29 -0
- package/dist/lib/CasaTemplateLoader.js +74 -0
- package/dist/lib/JourneyContext.d.ts +297 -0
- package/dist/lib/JourneyContext.js +581 -0
- package/dist/lib/MutableRouter.d.ts +155 -0
- package/dist/lib/MutableRouter.js +277 -0
- package/dist/lib/Plan.d.ts +154 -0
- package/dist/lib/Plan.js +442 -0
- package/dist/lib/ValidationError.d.ts +74 -0
- package/dist/lib/ValidationError.js +159 -0
- package/dist/lib/ValidatorFactory.d.ts +83 -0
- package/dist/lib/ValidatorFactory.js +106 -0
- package/dist/lib/configuration-ingestor.d.ts +262 -0
- package/dist/lib/configuration-ingestor.js +490 -0
- package/dist/lib/configure.d.ts +90 -0
- package/dist/lib/configure.js +192 -0
- package/dist/lib/dirname.cjs +1 -0
- package/dist/lib/dirname.d.cts +2 -0
- package/dist/lib/end-session.d.ts +13 -0
- package/dist/lib/end-session.js +43 -0
- package/dist/lib/field.d.ts +77 -0
- package/dist/lib/field.js +255 -0
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +18 -0
- package/dist/lib/nunjucks-filters.d.ts +26 -0
- package/dist/lib/nunjucks-filters.js +90 -0
- package/dist/lib/nunjucks.d.ts +23 -0
- package/dist/lib/nunjucks.js +49 -0
- package/dist/lib/utils.d.ts +48 -0
- package/dist/lib/utils.js +111 -0
- package/dist/lib/validators/dateObject.d.ts +4 -0
- package/dist/lib/validators/dateObject.js +135 -0
- package/dist/lib/validators/email.d.ts +4 -0
- package/dist/lib/validators/email.js +46 -0
- package/dist/lib/validators/inArray.d.ts +4 -0
- package/dist/lib/validators/inArray.js +60 -0
- package/dist/lib/validators/index.d.ts +21 -0
- package/dist/lib/validators/index.js +47 -0
- package/dist/lib/validators/nino.d.ts +4 -0
- package/dist/lib/validators/nino.js +46 -0
- package/dist/lib/validators/postalAddressObject.d.ts +4 -0
- package/dist/lib/validators/postalAddressObject.js +123 -0
- package/dist/lib/validators/regex.d.ts +4 -0
- package/dist/lib/validators/regex.js +40 -0
- package/dist/lib/validators/required.d.ts +4 -0
- package/dist/lib/validators/required.js +56 -0
- package/dist/lib/validators/strlen.d.ts +4 -0
- package/dist/lib/validators/strlen.js +51 -0
- package/dist/lib/validators/wordCount.d.ts +5 -0
- package/dist/lib/validators/wordCount.js +54 -0
- package/dist/lib/waypoint-url.d.ts +23 -0
- package/dist/lib/waypoint-url.js +52 -0
- package/dist/middleware/body-parser.d.ts +1 -0
- package/dist/middleware/body-parser.js +24 -0
- package/dist/middleware/csrf.d.ts +1 -0
- package/dist/middleware/csrf.js +31 -0
- package/dist/middleware/data.d.ts +5 -0
- package/dist/middleware/data.js +53 -0
- package/dist/middleware/dirname.cjs +1 -0
- package/dist/middleware/dirname.d.cts +2 -0
- package/dist/middleware/gather-fields.d.ts +6 -0
- package/dist/middleware/gather-fields.js +48 -0
- package/dist/middleware/i18n.d.ts +4 -0
- package/dist/middleware/i18n.js +88 -0
- package/dist/middleware/post.d.ts +3 -0
- package/dist/middleware/post.js +57 -0
- package/dist/middleware/pre.d.ts +3 -0
- package/dist/middleware/pre.js +51 -0
- package/dist/middleware/progress-journey.d.ts +6 -0
- package/dist/middleware/progress-journey.js +80 -0
- package/dist/middleware/sanitise-fields.d.ts +5 -0
- package/dist/middleware/sanitise-fields.js +53 -0
- package/dist/middleware/session.d.ts +11 -0
- package/dist/middleware/session.js +121 -0
- package/dist/middleware/skip-waypoint.d.ts +5 -0
- package/dist/middleware/skip-waypoint.js +43 -0
- package/dist/middleware/steer-journey.d.ts +7 -0
- package/dist/middleware/steer-journey.js +62 -0
- package/dist/middleware/validate-fields.d.ts +7 -0
- package/dist/middleware/validate-fields.js +67 -0
- package/dist/mjs/esm-wrapper.js +11 -0
- package/dist/mjs/package.json +3 -0
- package/dist/package.json +3 -0
- package/dist/routes/ancillary.d.ts +11 -0
- package/dist/routes/ancillary.js +27 -0
- package/dist/routes/dirname.cjs +1 -0
- package/dist/routes/dirname.d.cts +2 -0
- package/dist/routes/journey.d.ts +8 -0
- package/dist/routes/journey.js +127 -0
- package/dist/routes/static.d.ts +26 -0
- package/dist/routes/static.js +68 -0
- package/lib/JourneyContext.js +1 -1
- package/lib/utils/sanitise.js +1 -1
- package/lib/validation/SimpleField.d.ts +1 -0
- package/lib/validation/SimpleField.js +1 -1
- package/lib/validation/rules/ValidationRules.d.ts +11 -19
- package/package.json +22 -22
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const debug_1 = __importDefault(require("debug"));
|
|
7
|
+
const casaDebugger = (0, debug_1.default)('casa');
|
|
8
|
+
exports.default = (ns) => {
|
|
9
|
+
const logger = casaDebugger.extend(ns);
|
|
10
|
+
return {
|
|
11
|
+
trace: logger.extend('trace'),
|
|
12
|
+
debug: logger.extend('debug'),
|
|
13
|
+
info: logger.extend('info'),
|
|
14
|
+
warn: logger.extend('warn'),
|
|
15
|
+
error: logger.extend('error'),
|
|
16
|
+
fatal: logger.extend('fatal'),
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function mergeObjects(...objects: any[]): object;
|
|
2
|
+
export function includes(source?: any[], search?: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Format a given date.
|
|
5
|
+
*
|
|
6
|
+
* Requires NodeJS >= 14 to make use of bundled date locale data.
|
|
7
|
+
*
|
|
8
|
+
* `date` may be any of the following types:
|
|
9
|
+
* object - {dd:'', mm:'', yyyy:''}
|
|
10
|
+
*
|
|
11
|
+
* @param {object} date Date (see supported formats above)
|
|
12
|
+
* @param {object} config Holds locale
|
|
13
|
+
* @returns {string} Formatted date
|
|
14
|
+
*/
|
|
15
|
+
export function formatDateObject(date: object, config?: object): string;
|
|
16
|
+
/**
|
|
17
|
+
* Attribute values will be HTML/attribute escaped.
|
|
18
|
+
*
|
|
19
|
+
* Example:
|
|
20
|
+
* Given: {class: 'basic', 'data-ga': 3}
|
|
21
|
+
* Output: class="basic" data-ga="3"
|
|
22
|
+
*
|
|
23
|
+
* @param {object} attrsObject Attributes object (in name:value pairs)
|
|
24
|
+
* @returns {string} Formatted
|
|
25
|
+
*/
|
|
26
|
+
export function renderAsAttributes(attrsObject: object): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderAsAttributes = exports.formatDateObject = exports.includes = exports.mergeObjects = void 0;
|
|
7
|
+
const deepmerge_1 = __importDefault(require("deepmerge"));
|
|
8
|
+
const luxon_1 = require("luxon");
|
|
9
|
+
const nunjucks_1 = __importDefault(require("nunjucks"));
|
|
10
|
+
const { all: deepmergeAll } = deepmerge_1.default;
|
|
11
|
+
// Arrays will be merged such that elements at the same index will be merged
|
|
12
|
+
// into each other
|
|
13
|
+
// ref: https://www.npmjs.com/package/deepmerge
|
|
14
|
+
const combineMerge = (target, source, options) => {
|
|
15
|
+
const destination = target.slice();
|
|
16
|
+
source.forEach((item, index) => {
|
|
17
|
+
if (typeof destination[index] === 'undefined') {
|
|
18
|
+
destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
|
|
19
|
+
}
|
|
20
|
+
else if (options.isMergeableObject(item)) {
|
|
21
|
+
destination[index] = (0, deepmerge_1.default)(target[index], item, options);
|
|
22
|
+
}
|
|
23
|
+
else if (target.indexOf(item) === -1) {
|
|
24
|
+
destination.push(item);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return destination;
|
|
28
|
+
};
|
|
29
|
+
function mergeObjects(...objects) {
|
|
30
|
+
return deepmergeAll([Object.create(null), ...objects], { arrayMerge: combineMerge });
|
|
31
|
+
}
|
|
32
|
+
exports.mergeObjects = mergeObjects;
|
|
33
|
+
function includes(source = [], search = '') {
|
|
34
|
+
return source.includes(search);
|
|
35
|
+
}
|
|
36
|
+
exports.includes = includes;
|
|
37
|
+
/**
|
|
38
|
+
* Format a given date.
|
|
39
|
+
*
|
|
40
|
+
* Requires NodeJS >= 14 to make use of bundled date locale data.
|
|
41
|
+
*
|
|
42
|
+
* `date` may be any of the following types:
|
|
43
|
+
* object - {dd:'', mm:'', yyyy:''}
|
|
44
|
+
*
|
|
45
|
+
* @param {object} date Date (see supported formats above)
|
|
46
|
+
* @param {object} config Holds locale
|
|
47
|
+
* @returns {string} Formatted date
|
|
48
|
+
*/
|
|
49
|
+
function formatDateObject(date, config = {}) {
|
|
50
|
+
const { locale = 'en' } = config;
|
|
51
|
+
if (Object.prototype.toString.call(date) === '[object Object]'
|
|
52
|
+
&& 'yyyy' in date
|
|
53
|
+
&& 'mm' in date
|
|
54
|
+
&& 'dd' in date) {
|
|
55
|
+
return luxon_1.DateTime.fromObject({
|
|
56
|
+
year: Math.max(0, parseInt(date.yyyy, 10)),
|
|
57
|
+
month: Math.max(0, parseInt(date.mm, 10)),
|
|
58
|
+
day: Math.max(1, parseInt(date.dd, 10)),
|
|
59
|
+
}).setLocale(locale).toFormat('d MMMM yyyy');
|
|
60
|
+
}
|
|
61
|
+
return 'INVALID DATE OBJECT';
|
|
62
|
+
}
|
|
63
|
+
exports.formatDateObject = formatDateObject;
|
|
64
|
+
/**
|
|
65
|
+
* Attribute values will be HTML/attribute escaped.
|
|
66
|
+
*
|
|
67
|
+
* Example:
|
|
68
|
+
* Given: {class: 'basic', 'data-ga': 3}
|
|
69
|
+
* Output: class="basic" data-ga="3"
|
|
70
|
+
*
|
|
71
|
+
* @param {object} attrsObject Attributes object (in name:value pairs)
|
|
72
|
+
* @returns {string} Formatted
|
|
73
|
+
*/
|
|
74
|
+
function renderAsAttributes(attrsObject) {
|
|
75
|
+
const attrsList = [];
|
|
76
|
+
if (typeof attrsObject === 'object') {
|
|
77
|
+
Object.keys(attrsObject).forEach((key) => {
|
|
78
|
+
const value = String(attrsObject[key]).replace(/[<>"'&]/g, (m) => ({
|
|
79
|
+
'<': '<',
|
|
80
|
+
'>': '>',
|
|
81
|
+
'"': '"',
|
|
82
|
+
'\'': ''',
|
|
83
|
+
'&': '&',
|
|
84
|
+
}[m]));
|
|
85
|
+
attrsList.push(`${key}="${value}"`);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return new nunjucks_1.default.runtime.SafeString(attrsList.join(' '));
|
|
89
|
+
}
|
|
90
|
+
exports.renderAsAttributes = renderAsAttributes;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} NunjucksOptions
|
|
3
|
+
* @property {string} [mountUrl=/] Mount URL (optional, default /)
|
|
4
|
+
* @property {string[]} [views=[]] Template file directories (optional, default [])
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Create a Nunjucks environment.
|
|
8
|
+
*
|
|
9
|
+
* @param {NunjucksOptions} options Nunjucks options
|
|
10
|
+
* @returns {Environment} Nunjucks Environment instance
|
|
11
|
+
*/
|
|
12
|
+
export default function nunjucksConfig({ mountUrl, views, }: NunjucksOptions): Environment;
|
|
13
|
+
export type NunjucksOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* )
|
|
16
|
+
*/
|
|
17
|
+
mountUrl?: string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Template file directories (optional, default [])
|
|
20
|
+
*/
|
|
21
|
+
views?: string[] | undefined;
|
|
22
|
+
};
|
|
23
|
+
import { Environment } from "nunjucks";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const nunjucks_1 = require("nunjucks");
|
|
9
|
+
const dirname_cjs_1 = __importDefault(require("./dirname.cjs"));
|
|
10
|
+
const CasaTemplateLoader_js_1 = __importDefault(require("./CasaTemplateLoader.js"));
|
|
11
|
+
const nunjucks_filters_js_1 = require("./nunjucks-filters.js");
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {object} NunjucksOptions
|
|
14
|
+
* @property {string} [mountUrl=/] Mount URL (optional, default /)
|
|
15
|
+
* @property {string[]} [views=[]] Template file directories (optional, default [])
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Create a Nunjucks environment.
|
|
19
|
+
*
|
|
20
|
+
* @param {NunjucksOptions} options Nunjucks options
|
|
21
|
+
* @returns {Environment} Nunjucks Environment instance
|
|
22
|
+
*/
|
|
23
|
+
function nunjucksConfig({ mountUrl = '/', views = [], }) {
|
|
24
|
+
// Prepare a single Nunjucks environment for all responses to use. Note that
|
|
25
|
+
// we cannot prepare response-specific global functions/filters if we use a
|
|
26
|
+
// single environment, but the performance gains of doing so are significant.
|
|
27
|
+
const loader = new CasaTemplateLoader_js_1.default(views, {
|
|
28
|
+
watch: false,
|
|
29
|
+
noCache: false,
|
|
30
|
+
});
|
|
31
|
+
const env = new nunjucks_1.Environment(loader, {
|
|
32
|
+
autoescape: true,
|
|
33
|
+
throwOnUndefined: false,
|
|
34
|
+
trimBlocks: false,
|
|
35
|
+
lstripBlocks: false,
|
|
36
|
+
});
|
|
37
|
+
// Enhancement to expose loader functions
|
|
38
|
+
env.modifyBlock = loader.modifyBlock.bind(loader);
|
|
39
|
+
// Globals
|
|
40
|
+
// These can't be modified once set. But they can be overridden by res.locals.
|
|
41
|
+
env.addGlobal('assetPath', `${mountUrl}govuk/assets`); // Required by govuk-frontend layout template
|
|
42
|
+
env.addGlobal('casaVersion', JSON.parse((0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../package.json'))).version);
|
|
43
|
+
env.addGlobal('mergeObjects', nunjucks_filters_js_1.mergeObjects);
|
|
44
|
+
env.addGlobal('includes', nunjucks_filters_js_1.includes);
|
|
45
|
+
env.addGlobal('formatDateObject', nunjucks_filters_js_1.formatDateObject);
|
|
46
|
+
env.addGlobal('renderAsAttributes', nunjucks_filters_js_1.renderAsAttributes);
|
|
47
|
+
return env;
|
|
48
|
+
}
|
|
49
|
+
exports.default = nunjucksConfig;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./configuration-ingestor').GlobalHook} GlobalHook
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('./configuration-ingestor').PageHook} PageHook
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {GlobalHook | PageHook} Hook
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Test is a value can be stringifed (numbers or strings)
|
|
12
|
+
*
|
|
13
|
+
* @param {any} value Item to test
|
|
14
|
+
* @returns {boolean} Whether the value is stringable or not
|
|
15
|
+
*/
|
|
16
|
+
export function isStringable(value: any): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Coerce an input to a string.
|
|
19
|
+
*
|
|
20
|
+
* @param {any} input Input to be stringified
|
|
21
|
+
* @param {string} fallback Fallback to use if input can't be stringified
|
|
22
|
+
* @returns {string} The stringified input
|
|
23
|
+
*/
|
|
24
|
+
export function stringifyInput(input: any, fallback: string, ...args: any[]): string;
|
|
25
|
+
/**
|
|
26
|
+
* Determine if value is empty. Recurse over objects.
|
|
27
|
+
*
|
|
28
|
+
* @param {any} val Value to check
|
|
29
|
+
* @returns {boolean} True if the object is empty
|
|
30
|
+
*/
|
|
31
|
+
export function isEmpty(val: any): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extract the middleware functions that are relevant for the given hook and
|
|
34
|
+
* path.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} hookName Hook name (including scope prefix)
|
|
37
|
+
* @param {string} path URL path to match (relative to mountUrl)
|
|
38
|
+
* @param {Hook[]} hooks Hooks to be applied at the page level
|
|
39
|
+
* @returns {Function[]} An array of middleware that should be applied
|
|
40
|
+
*/
|
|
41
|
+
export function resolveMiddlewareHooks(hookName: string, path: string, hooks?: Hook[]): Function[];
|
|
42
|
+
export function validateWaypoint(waypoint: any): void;
|
|
43
|
+
export function validateView(view: any): void;
|
|
44
|
+
export function validateHookName(hookName: any): void;
|
|
45
|
+
export function validateHookPath(path: any): void;
|
|
46
|
+
export type GlobalHook = import('./configuration-ingestor').GlobalHook;
|
|
47
|
+
export type PageHook = import('./configuration-ingestor').PageHook;
|
|
48
|
+
export type Hook = GlobalHook | PageHook;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('./configuration-ingestor').GlobalHook} GlobalHook
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateHookPath = exports.validateHookName = exports.validateView = exports.validateWaypoint = exports.resolveMiddlewareHooks = exports.isEmpty = exports.stringifyInput = exports.isStringable = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./configuration-ingestor').PageHook} PageHook
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {GlobalHook | PageHook} Hook
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Test is a value can be stringifed (numbers or strings)
|
|
15
|
+
*
|
|
16
|
+
* @param {any} value Item to test
|
|
17
|
+
* @returns {boolean} Whether the value is stringable or not
|
|
18
|
+
*/
|
|
19
|
+
function isStringable(value) {
|
|
20
|
+
return typeof value === 'string' || typeof value === 'number';
|
|
21
|
+
}
|
|
22
|
+
exports.isStringable = isStringable;
|
|
23
|
+
/**
|
|
24
|
+
* Coerce an input to a string.
|
|
25
|
+
*
|
|
26
|
+
* @param {any} input Input to be stringified
|
|
27
|
+
* @param {string} fallback Fallback to use if input can't be stringified
|
|
28
|
+
* @returns {string} The stringified input
|
|
29
|
+
*/
|
|
30
|
+
function stringifyInput(input, fallback) {
|
|
31
|
+
// Not using param defaults here as the fallback may be explicitly "undefined"
|
|
32
|
+
const fb = arguments.length === 2 && (isStringable(fallback) || fallback === undefined) ? fallback : '';
|
|
33
|
+
return isStringable(input) ? String(input) : fb;
|
|
34
|
+
}
|
|
35
|
+
exports.stringifyInput = stringifyInput;
|
|
36
|
+
/**
|
|
37
|
+
* Determine if value is empty. Recurse over objects.
|
|
38
|
+
*
|
|
39
|
+
* @param {any} val Value to check
|
|
40
|
+
* @returns {boolean} True if the object is empty
|
|
41
|
+
*/
|
|
42
|
+
function isEmpty(val) {
|
|
43
|
+
if (val === null
|
|
44
|
+
|| typeof val === 'undefined'
|
|
45
|
+
|| (typeof val === 'string' && val === '')) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(val) || typeof val === 'object') {
|
|
49
|
+
return Object.keys(val).filter((k) => !isEmpty(val[k])).length === 0;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
exports.isEmpty = isEmpty;
|
|
54
|
+
/**
|
|
55
|
+
* Extract the middleware functions that are relevant for the given hook and
|
|
56
|
+
* path.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} hookName Hook name (including scope prefix)
|
|
59
|
+
* @param {string} path URL path to match (relative to mountUrl)
|
|
60
|
+
* @param {Hook[]} hooks Hooks to be applied at the page level
|
|
61
|
+
* @returns {Function[]} An array of middleware that should be applied
|
|
62
|
+
*/
|
|
63
|
+
function resolveMiddlewareHooks(hookName, path, hooks = []) {
|
|
64
|
+
/* eslint-disable-next-line max-len */
|
|
65
|
+
const pathMatch = (h) => h.path === undefined || (h.path instanceof RegExp && h.path.test(path)) || h.path === path;
|
|
66
|
+
return hooks.filter((h) => h.hook === hookName).filter(pathMatch).map((h) => h.middleware);
|
|
67
|
+
}
|
|
68
|
+
exports.resolveMiddlewareHooks = resolveMiddlewareHooks;
|
|
69
|
+
/* ------------------------------------------------ validation / sanitisation */
|
|
70
|
+
function validateWaypoint(waypoint) {
|
|
71
|
+
if (typeof waypoint !== 'string') {
|
|
72
|
+
throw new TypeError('Waypoint must be a string');
|
|
73
|
+
}
|
|
74
|
+
if (!waypoint.length) {
|
|
75
|
+
throw new SyntaxError('Waypoint must not be empty');
|
|
76
|
+
}
|
|
77
|
+
if (waypoint.match(/[^/a-z0-9_-]/)) {
|
|
78
|
+
throw new SyntaxError('Waypoint must contain only a-z, 0-9, -, _ and / characters');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.validateWaypoint = validateWaypoint;
|
|
82
|
+
function validateView(view) {
|
|
83
|
+
if (typeof view !== 'string') {
|
|
84
|
+
throw new TypeError('View must be a string');
|
|
85
|
+
}
|
|
86
|
+
if (!view.length) {
|
|
87
|
+
throw new SyntaxError('View must not be empty');
|
|
88
|
+
}
|
|
89
|
+
if (!view.match(/^[a-z0-9/_-]+\.njk$/i)) {
|
|
90
|
+
throw new SyntaxError('View must contain only a-z, 0-9, -, _ and / characters, and end in .njk');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.validateView = validateView;
|
|
94
|
+
function validateHookName(hookName) {
|
|
95
|
+
if (typeof hookName !== 'string') {
|
|
96
|
+
throw new TypeError('Hook name must be a string');
|
|
97
|
+
}
|
|
98
|
+
if (!hookName.length) {
|
|
99
|
+
throw new SyntaxError('Hook name must not be empty');
|
|
100
|
+
}
|
|
101
|
+
if (!hookName.match(/^([a-z]+\.|)[a-z]+$/i)) {
|
|
102
|
+
throw new SyntaxError('Hook name must match either <scope>.<hookname> or <hookname> formats');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.validateHookName = validateHookName;
|
|
106
|
+
function validateHookPath(path) {
|
|
107
|
+
if (typeof path !== 'string' && !(path instanceof RegExp)) {
|
|
108
|
+
throw new TypeError('Hook path must be a string or RegExp');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.validateHookPath = validateHookPath;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable class-methods-use-this */
|
|
7
|
+
/**
|
|
8
|
+
* Date object format:
|
|
9
|
+
* {
|
|
10
|
+
* dd: <string>,
|
|
11
|
+
* mm: <string>,
|
|
12
|
+
* yyyy: <string>
|
|
13
|
+
* }.
|
|
14
|
+
*
|
|
15
|
+
* Note that the time part of any injected "DateTime" objects will be zero'ed, as
|
|
16
|
+
* we are only interested in the date component (minimum day resolution).
|
|
17
|
+
*
|
|
18
|
+
* Config options:
|
|
19
|
+
* string|object errorMsg = Error message to use on validation failure
|
|
20
|
+
* object|luxon.Duration afterOffsetFromNow = Date must be after offset from now
|
|
21
|
+
* string|object errorMsgAfterOffset = Error for afterOffsetFromNow failure
|
|
22
|
+
* object|luxon.Duration beforeOffsetFromNow = Date must be before offset from now
|
|
23
|
+
* string|object errorMsgBeforeOffset = Error for beforeOffsetFromNow failure
|
|
24
|
+
* bool allowMonthNames = Allow "Jan", "January", etc (default = false)
|
|
25
|
+
* bool allowSingleDigitDay = Allow "1" rather than "01" (default = false)
|
|
26
|
+
* bool allowSingleDigitMonth = Allow "1" rather than "01" (default = false)
|
|
27
|
+
* luxon.DateTime now = Override the notion of "now" (useful for testing)
|
|
28
|
+
*/
|
|
29
|
+
const luxon_1 = require("luxon");
|
|
30
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
31
|
+
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
32
|
+
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
33
|
+
const utils_js_1 = require("../utils.js");
|
|
34
|
+
const { isPlainObject } = lodash_1.default;
|
|
35
|
+
class DateObject extends ValidatorFactory_js_1.default {
|
|
36
|
+
constructor() {
|
|
37
|
+
super(...arguments);
|
|
38
|
+
this.name = 'dateObject';
|
|
39
|
+
}
|
|
40
|
+
validate(value, dataContext = {}) {
|
|
41
|
+
const config = Object.assign({ errorMsg: {
|
|
42
|
+
inline: 'validation:rule.dateObject.inline',
|
|
43
|
+
summary: 'validation:rule.dateObject.summary',
|
|
44
|
+
}, errorMsgAfterOffset: {
|
|
45
|
+
inline: 'validation:rule.dateObject.afterOffset.inline',
|
|
46
|
+
summary: 'validation:rule.dateObject.afterOffset.summary',
|
|
47
|
+
}, errorMsgBeforeOffset: {
|
|
48
|
+
inline: 'validation:rule.dateObject.beforeOffset.inline',
|
|
49
|
+
summary: 'validation:rule.dateObject.beforeOffset.summary',
|
|
50
|
+
}, now: luxon_1.DateTime.local(), allowSingleDigitDay: false, allowSingleDigitMonth: false, allowMonthNames: false, afterOffsetFromNow: undefined, beforeOffsetFromNow: undefined }, this.config);
|
|
51
|
+
let valid = false;
|
|
52
|
+
let { errorMsg } = config;
|
|
53
|
+
let luxonDate;
|
|
54
|
+
const NOW = config.now.startOf('day');
|
|
55
|
+
// Accepted formats
|
|
56
|
+
let formats = ['dd-MM-yyyy'];
|
|
57
|
+
const formatTests = [{
|
|
58
|
+
flags: [config.allowSingleDigitDay],
|
|
59
|
+
formats: ['d-MM-yyyy'],
|
|
60
|
+
}, {
|
|
61
|
+
flags: [config.allowSingleDigitDay, config.allowSingleDigitMonth],
|
|
62
|
+
formats: ['d-M-yyyy'],
|
|
63
|
+
}, {
|
|
64
|
+
flags: [config.allowSingleDigitDay, config.allowMonthNames],
|
|
65
|
+
formats: ['d-MMM-yyyy', 'd-MMMM-yyyy'],
|
|
66
|
+
}, {
|
|
67
|
+
flags: [config.allowSingleDigitMonth],
|
|
68
|
+
formats: ['dd-M-yyyy'],
|
|
69
|
+
}, {
|
|
70
|
+
flags: [config.allowMonthNames],
|
|
71
|
+
formats: ['dd-MMM-yyyy', 'dd-MMMM-yyyy'],
|
|
72
|
+
}];
|
|
73
|
+
formatTests.forEach((test) => {
|
|
74
|
+
if (test.flags.every((v) => v === true)) {
|
|
75
|
+
formats = [...formats, ...test.formats];
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
if (typeof value === 'object') {
|
|
79
|
+
formats.find((format) => {
|
|
80
|
+
luxonDate = luxon_1.DateTime.fromFormat([value.dd, value.mm, value.yyyy].join('-'), format).startOf('day');
|
|
81
|
+
valid = luxonDate.isValid;
|
|
82
|
+
return valid;
|
|
83
|
+
});
|
|
84
|
+
if (luxonDate) {
|
|
85
|
+
// Check date is after the specified duration from now.
|
|
86
|
+
// Need to use UTC() otherwise DST shifts can affect the calculated offset
|
|
87
|
+
if (config.afterOffsetFromNow) {
|
|
88
|
+
const offsetDate = NOW.plus(config.afterOffsetFromNow).startOf('day');
|
|
89
|
+
if (luxonDate <= offsetDate) {
|
|
90
|
+
valid = false;
|
|
91
|
+
errorMsg = config.errorMsgAfterOffset;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Check date is before the specified duration from now
|
|
95
|
+
// Need to use UTC() otherwise DST shifts can affect the calculated offset
|
|
96
|
+
if (config.beforeOffsetFromNow) {
|
|
97
|
+
const offsetDate = NOW.plus(config.beforeOffsetFromNow).startOf('day');
|
|
98
|
+
if (luxonDate >= offsetDate) {
|
|
99
|
+
valid = false;
|
|
100
|
+
errorMsg = config.errorMsgBeforeOffset;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Check presence of each object component (dd, mm, yyyy) in order to log
|
|
105
|
+
// which specific parts are in error
|
|
106
|
+
errorMsg.focusSuffix = [];
|
|
107
|
+
if (!Object.prototype.hasOwnProperty.call(value, 'dd') || !value.dd) {
|
|
108
|
+
errorMsg.focusSuffix.push('[dd]');
|
|
109
|
+
}
|
|
110
|
+
if (!Object.prototype.hasOwnProperty.call(value, 'mm') || !value.mm) {
|
|
111
|
+
errorMsg.focusSuffix.push('[mm]');
|
|
112
|
+
}
|
|
113
|
+
if (!Object.prototype.hasOwnProperty.call(value, 'yyyy') || !value.yyyy) {
|
|
114
|
+
errorMsg.focusSuffix.push('[yyyy]');
|
|
115
|
+
}
|
|
116
|
+
// If the date is invalid, but not specific parts have been highighted in
|
|
117
|
+
// error, then highlight all inputs, focusing on the [dd] first
|
|
118
|
+
if (!valid && !errorMsg.focusSuffix.length) {
|
|
119
|
+
errorMsg.focusSuffix = ['[dd]', '[mm]', '[yyyy]'];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return valid ? [] : [ValidationError_js_1.default.make({ errorMsg, dataContext })];
|
|
123
|
+
}
|
|
124
|
+
sanitise(value) {
|
|
125
|
+
if (value !== undefined) {
|
|
126
|
+
return isPlainObject(value) ? {
|
|
127
|
+
dd: (0, utils_js_1.stringifyInput)(value.dd),
|
|
128
|
+
mm: (0, utils_js_1.stringifyInput)(value.mm),
|
|
129
|
+
yyyy: (0, utils_js_1.stringifyInput)(value.yyyy),
|
|
130
|
+
} : Object.create(null);
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.default = DateObject;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable class-methods-use-this */
|
|
3
|
+
/**
|
|
4
|
+
* Email address.
|
|
5
|
+
*
|
|
6
|
+
* This is not an exhaustive validation, and is permissive.
|
|
7
|
+
*
|
|
8
|
+
* Config options:
|
|
9
|
+
* string|object errorMsg = Error message to use on validation failure
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const validator_1 = __importDefault(require("validator"));
|
|
16
|
+
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
17
|
+
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
18
|
+
const utils_js_1 = require("../utils.js");
|
|
19
|
+
const { isEmail } = validator_1.default; // CommonJS
|
|
20
|
+
class Email extends ValidatorFactory_js_1.default {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.name = 'email';
|
|
24
|
+
}
|
|
25
|
+
validate(value, dataContext = {}) {
|
|
26
|
+
let isValid;
|
|
27
|
+
try {
|
|
28
|
+
isValid = isEmail(value);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
isValid = false;
|
|
32
|
+
}
|
|
33
|
+
const errorMsg = this.config.errorMsg || {
|
|
34
|
+
summary: 'validation:rule.email.summary',
|
|
35
|
+
inline: 'validation:rule.email.inline',
|
|
36
|
+
};
|
|
37
|
+
return isValid ? [] : [ValidationError_js_1.default.make({ errorMsg, dataContext })];
|
|
38
|
+
}
|
|
39
|
+
sanitise(value) {
|
|
40
|
+
if (value !== undefined) {
|
|
41
|
+
return (0, utils_js_1.stringifyInput)(value);
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.default = Email;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable class-methods-use-this */
|
|
7
|
+
/**
|
|
8
|
+
* Test if a value is present in an array.
|
|
9
|
+
*
|
|
10
|
+
* Config options:
|
|
11
|
+
* Array source = Array of values to test against
|
|
12
|
+
*
|
|
13
|
+
* If the value itself is an array, all values within that array must be present
|
|
14
|
+
* in the `source` array in order to pass validation.
|
|
15
|
+
*/
|
|
16
|
+
const ValidationError_js_1 = __importDefault(require("../ValidationError.js"));
|
|
17
|
+
const ValidatorFactory_js_1 = __importDefault(require("../ValidatorFactory.js"));
|
|
18
|
+
const utils_js_1 = require("../utils.js");
|
|
19
|
+
class InArray extends ValidatorFactory_js_1.default {
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.name = 'inArray';
|
|
23
|
+
}
|
|
24
|
+
validate(value, dataContext = {}) {
|
|
25
|
+
let valid = false;
|
|
26
|
+
const source = this.config.source || [];
|
|
27
|
+
const errorMsg = this.config.errorMsg || {
|
|
28
|
+
inline: 'validation:rule.inArray.inline',
|
|
29
|
+
summary: 'validation:rule.inArray.summary',
|
|
30
|
+
};
|
|
31
|
+
if (value !== null && typeof value !== 'undefined') {
|
|
32
|
+
const search = Array.isArray(value) ? value : [value];
|
|
33
|
+
for (let i = 0, l = search.length; i < l; i += 1) {
|
|
34
|
+
if (source.indexOf(search[i]) > -1) {
|
|
35
|
+
valid = true;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
valid = false;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return valid ? [] : [ValidationError_js_1.default.make({ errorMsg, dataContext })];
|
|
44
|
+
}
|
|
45
|
+
sanitise(value) {
|
|
46
|
+
const coerce = (val) => ((0, utils_js_1.stringifyInput)(val, undefined));
|
|
47
|
+
// Basic stringable
|
|
48
|
+
if ((0, utils_js_1.isStringable)(value)) {
|
|
49
|
+
return (0, utils_js_1.stringifyInput)(value);
|
|
50
|
+
}
|
|
51
|
+
// Coerce all elements to Strings.
|
|
52
|
+
// This only supports one dimensional array, with stringable element.
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
return value.map(coerce);
|
|
55
|
+
}
|
|
56
|
+
// Unsupported value
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.default = InArray;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
export { dateObject };
|
|
3
|
+
export { email };
|
|
4
|
+
export { inArray };
|
|
5
|
+
export { nino };
|
|
6
|
+
export { postalAddressObject };
|
|
7
|
+
export { regex };
|
|
8
|
+
export { required };
|
|
9
|
+
export { strlen };
|
|
10
|
+
export { wordCount };
|
|
11
|
+
}
|
|
12
|
+
export default _default;
|
|
13
|
+
import dateObject from "./dateObject.js";
|
|
14
|
+
import email from "./email.js";
|
|
15
|
+
import inArray from "./inArray.js";
|
|
16
|
+
import nino from "./nino.js";
|
|
17
|
+
import postalAddressObject from "./postalAddressObject.js";
|
|
18
|
+
import regex from "./regex.js";
|
|
19
|
+
import required from "./required.js";
|
|
20
|
+
import strlen from "./strlen.js";
|
|
21
|
+
import wordCount from "./wordCount.js";
|