@centralping/ergo 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/http/accepts.js +69 -0
- package/http/authorization.js +65 -0
- package/http/body.js +311 -0
- package/http/cache-control.js +123 -0
- package/http/compress.js +157 -0
- package/http/cookie.js +39 -0
- package/http/cors.js +79 -0
- package/http/csrf.js +76 -0
- package/http/handler.js +74 -0
- package/http/index.js +13 -0
- package/http/json-api-query.js +53 -0
- package/http/logger.js +167 -0
- package/http/main.js +140 -0
- package/http/precondition.js +53 -0
- package/http/prefer.js +36 -0
- package/http/rate-limit.js +66 -0
- package/http/security-headers.js +62 -0
- package/http/send.js +399 -0
- package/http/timeout.js +47 -0
- package/http/url.js +47 -0
- package/http/validate.js +84 -0
- package/lib/accepts.js +49 -0
- package/lib/attach-instance.js +23 -0
- package/lib/authorization.js +187 -0
- package/lib/body/multiparse.js +173 -0
- package/lib/body/multipart/headers.js +69 -0
- package/lib/body/writer.js +73 -0
- package/lib/cookie/cookie.js +192 -0
- package/lib/cookie/index.js +14 -0
- package/lib/cookie/jar.js +106 -0
- package/lib/cookie/parse.js +101 -0
- package/lib/cors.js +191 -0
- package/lib/csrf.js +96 -0
- package/lib/from-connect.js +69 -0
- package/lib/json-api-query/index.js +25 -0
- package/lib/json-api-query/schema.json +105 -0
- package/lib/json-api-query/validate.js +56 -0
- package/lib/link.js +96 -0
- package/lib/prefer.js +52 -0
- package/lib/query.js +113 -0
- package/lib/rate-limit.js +115 -0
- package/lib/sanitize-quoted-string.js +28 -0
- package/lib/security-headers.js +125 -0
- package/lib/validate.js +80 -0
- package/lib/vary.js +40 -0
- package/package.json +158 -0
- package/types/http/accepts.d.ts +8 -0
- package/types/http/authorization.d.ts +8 -0
- package/types/http/body.d.ts +20 -0
- package/types/http/cache-control.d.ts +16 -0
- package/types/http/compress.d.ts +5 -0
- package/types/http/cookie.d.ts +2 -0
- package/types/http/cors.d.ts +9 -0
- package/types/http/csrf.d.ts +9 -0
- package/types/http/handler.d.ts +2 -0
- package/types/http/index.d.ts +1 -0
- package/types/http/json-api-query.d.ts +2 -0
- package/types/http/logger.d.ts +9 -0
- package/types/http/main.d.ts +142 -0
- package/types/http/precondition.d.ts +44 -0
- package/types/http/prefer.d.ts +2 -0
- package/types/http/rate-limit.d.ts +17 -0
- package/types/http/security-headers.d.ts +10 -0
- package/types/http/send.d.ts +8 -0
- package/types/http/timeout.d.ts +5 -0
- package/types/http/url.d.ts +2 -0
- package/types/http/validate.d.ts +6 -0
- package/types/lib/accepts.d.ts +7 -0
- package/types/lib/attach-instance.d.ts +19 -0
- package/types/lib/authorization.d.ts +6 -0
- package/types/lib/body/multiparse.d.ts +9 -0
- package/types/lib/body/multipart/headers.d.ts +2 -0
- package/types/lib/body/writer.d.ts +2 -0
- package/types/lib/cookie/cookie.d.ts +32 -0
- package/types/lib/cookie/index.d.ts +2 -0
- package/types/lib/cookie/jar.d.ts +8 -0
- package/types/lib/cookie/parse.d.ts +19 -0
- package/types/lib/cors.d.ts +9 -0
- package/types/lib/csrf.d.ts +32 -0
- package/types/lib/from-connect.d.ts +47 -0
- package/types/lib/json-api-query/index.d.ts +123 -0
- package/types/lib/json-api-query/validate.d.ts +5 -0
- package/types/lib/link.d.ts +37 -0
- package/types/lib/prefer.d.ts +36 -0
- package/types/lib/query.d.ts +6 -0
- package/types/lib/rate-limit.d.ts +76 -0
- package/types/lib/sanitize-quoted-string.d.ts +19 -0
- package/types/lib/security-headers.d.ts +24 -0
- package/types/lib/validate.d.ts +16 -0
- package/types/lib/vary.d.ts +17 -0
- package/types/utils/attempt.d.ts +2 -0
- package/types/utils/buffers/index.d.ts +2 -0
- package/types/utils/buffers/match.d.ts +10 -0
- package/types/utils/buffers/split.d.ts +10 -0
- package/types/utils/compose-with.d.ts +40 -0
- package/types/utils/compose.d.ts +83 -0
- package/types/utils/flat-array.d.ts +2 -0
- package/types/utils/get.d.ts +5 -0
- package/types/utils/http-errors.d.ts +22 -0
- package/types/utils/iterables/buffer-split.d.ts +2 -0
- package/types/utils/iterables/chain.d.ts +2 -0
- package/types/utils/iterables/exec-all.d.ts +2 -0
- package/types/utils/iterables/filter.d.ts +2 -0
- package/types/utils/iterables/for-each.d.ts +2 -0
- package/types/utils/iterables/from-stream.d.ts +2 -0
- package/types/utils/iterables/index.d.ts +10 -0
- package/types/utils/iterables/map.d.ts +2 -0
- package/types/utils/iterables/range.d.ts +24 -0
- package/types/utils/iterables/reduce.d.ts +2 -0
- package/types/utils/iterables/take.d.ts +2 -0
- package/types/utils/observables/buffer-split.d.ts +2 -0
- package/types/utils/observables/chain.d.ts +2 -0
- package/types/utils/observables/index.d.ts +4 -0
- package/types/utils/observables/map.d.ts +2 -0
- package/types/utils/observables/take.d.ts +2 -0
- package/types/utils/pick.d.ts +2 -0
- package/types/utils/set.d.ts +2 -0
- package/types/utils/streams/index.d.ts +2 -0
- package/types/utils/streams/meter.d.ts +5 -0
- package/types/utils/streams/tee.d.ts +2 -0
- package/types/utils/type.d.ts +2 -0
- package/utils/attempt.js +37 -0
- package/utils/buffers/index.js +13 -0
- package/utils/buffers/match.js +96 -0
- package/utils/buffers/split.js +55 -0
- package/utils/compose-with.js +232 -0
- package/utils/compose.js +165 -0
- package/utils/flat-array.js +24 -0
- package/utils/get.js +39 -0
- package/utils/http-errors.js +113 -0
- package/utils/iterables/buffer-split.js +117 -0
- package/utils/iterables/chain.js +32 -0
- package/utils/iterables/exec-all.js +42 -0
- package/utils/iterables/filter.js +35 -0
- package/utils/iterables/for-each.js +33 -0
- package/utils/iterables/from-stream.js +29 -0
- package/utils/iterables/index.js +21 -0
- package/utils/iterables/map.js +47 -0
- package/utils/iterables/range.js +34 -0
- package/utils/iterables/reduce.js +43 -0
- package/utils/iterables/take.js +36 -0
- package/utils/observables/buffer-split.js +109 -0
- package/utils/observables/chain.js +33 -0
- package/utils/observables/index.js +19 -0
- package/utils/observables/map.js +34 -0
- package/utils/observables/take.js +40 -0
- package/utils/pick.js +41 -0
- package/utils/set.js +38 -0
- package/utils/streams/index.js +11 -0
- package/utils/streams/meter.js +98 -0
- package/utils/streams/tee.js +84 -0
- package/utils/type.js +47 -0
package/lib/validate.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview JSON Schema validation factory using AJV 8.
|
|
3
|
+
*
|
|
4
|
+
* Compiles a JSON Schema once at creation time and returns a validator function.
|
|
5
|
+
* The validator throws `422 Unprocessable Entity` with structured error details
|
|
6
|
+
* when validation fails, returning the input data unchanged on success.
|
|
7
|
+
*
|
|
8
|
+
* Used by `http/validate.js` as the pure-logic backing implementation.
|
|
9
|
+
*
|
|
10
|
+
* @module lib/validate
|
|
11
|
+
* @version 0.1.0
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
* @requires ajv
|
|
14
|
+
* @requires ../utils/http-errors.js
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* import createValidator from 'ergo/lib/validate';
|
|
18
|
+
*
|
|
19
|
+
* const validate = createValidator({
|
|
20
|
+
* type: 'object',
|
|
21
|
+
* properties: {name: {type: 'string'}},
|
|
22
|
+
* required: ['name']
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* validate({name: 'Alice'}); // returns {name: 'Alice'}
|
|
26
|
+
* validate({}); // throws 422 with details
|
|
27
|
+
*/
|
|
28
|
+
import Ajv from 'ajv';
|
|
29
|
+
import httpErrors from '../utils/http-errors.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compiles a JSON Schema and returns a validating function.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} schema - JSON Schema 2020-12 or draft-07 object
|
|
35
|
+
* @param {object} [options] - Validator options
|
|
36
|
+
* @param {boolean} [options.allErrors=true] - Report all errors instead of stopping at the first
|
|
37
|
+
* @param {boolean} [options.coerceTypes=false] - Coerce input values to match schema types
|
|
38
|
+
* @param {object} [options.ajv] - Additional AJV constructor options
|
|
39
|
+
* @returns {function} - `validateData(data)` — returns `data` on success, throws 422 on failure
|
|
40
|
+
* @throws {Error} 422 with `details` array if schema validation fails
|
|
41
|
+
*/
|
|
42
|
+
export default function createValidator(schema, options = {}) {
|
|
43
|
+
const ajv = new Ajv({
|
|
44
|
+
allErrors: options.allErrors !== false,
|
|
45
|
+
coerceTypes: options.coerceTypes ?? false,
|
|
46
|
+
...options.ajv
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const validate = ajv.compile(schema);
|
|
50
|
+
|
|
51
|
+
return function validateData(data) {
|
|
52
|
+
const valid = validate(data);
|
|
53
|
+
|
|
54
|
+
if (!valid) {
|
|
55
|
+
throw httpErrors(422, {
|
|
56
|
+
message: 'Validation failed',
|
|
57
|
+
details: validate.errors.map(formatError)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return data;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Formats an AJV error object into a concise detail record.
|
|
67
|
+
*
|
|
68
|
+
* @param {object} err - AJV validation error (from `ajv.errors`)
|
|
69
|
+
* @param {string} err.instancePath - JSON Pointer to the failing field
|
|
70
|
+
* @param {string} err.message - Human-readable error message
|
|
71
|
+
* @param {object} err.params - Additional error parameters
|
|
72
|
+
* @returns {{path: string, message: string, params: object}} - Formatted error detail
|
|
73
|
+
*/
|
|
74
|
+
function formatError(err) {
|
|
75
|
+
return {
|
|
76
|
+
path: err.instancePath || '/',
|
|
77
|
+
message: err.message,
|
|
78
|
+
params: err.params
|
|
79
|
+
};
|
|
80
|
+
}
|
package/lib/vary.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared Vary header utility.
|
|
3
|
+
*
|
|
4
|
+
* Appends tokens to the `Vary` response header without duplicating existing tokens.
|
|
5
|
+
* Uses Set-based deduplication with case-insensitive comparison.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/vary
|
|
8
|
+
* @version 0.1.0
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Append a Vary token (or comma-separated tokens) to the response, avoiding duplicates.
|
|
14
|
+
*
|
|
15
|
+
* @param {import('node:http').ServerResponse} res - HTTP response object
|
|
16
|
+
* @param {string} value - Token(s) to append (e.g. "Accept-Encoding" or "Accept, Accept-Encoding")
|
|
17
|
+
*/
|
|
18
|
+
export default function appendVary(res, value) {
|
|
19
|
+
const existing = res.getHeader('Vary');
|
|
20
|
+
|
|
21
|
+
if (!existing) {
|
|
22
|
+
res.setHeader('Vary', value);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const tokens = new Set(
|
|
27
|
+
String(existing)
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.split(/,\s*/)
|
|
30
|
+
.map(s => s.trim())
|
|
31
|
+
);
|
|
32
|
+
const toAdd = value
|
|
33
|
+
.split(',')
|
|
34
|
+
.map(s => s.trim())
|
|
35
|
+
.filter(t => !tokens.has(t.toLowerCase()));
|
|
36
|
+
|
|
37
|
+
if (toAdd.length) {
|
|
38
|
+
res.setHeader('Vary', `${existing}, ${toAdd.join(', ')}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@centralping/ergo",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "A Fast Fail REST API toolkit for Node.js -- composable middleware with structured Negotiation, Authorization, Validation, and Execution stages.",
|
|
5
|
+
"main": "http/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "types/http/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./types/http/index.d.ts",
|
|
11
|
+
"default": "./http/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./http": {
|
|
14
|
+
"types": "./types/http/index.d.ts",
|
|
15
|
+
"default": "./http/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./http/*": {
|
|
18
|
+
"types": "./types/http/*.d.ts",
|
|
19
|
+
"default": "./http/*.js"
|
|
20
|
+
},
|
|
21
|
+
"./lib/cookie": {
|
|
22
|
+
"types": "./types/lib/cookie/index.d.ts",
|
|
23
|
+
"default": "./lib/cookie/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./lib/json-api-query": {
|
|
26
|
+
"types": "./types/lib/json-api-query/index.d.ts",
|
|
27
|
+
"default": "./lib/json-api-query/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./lib/*": {
|
|
30
|
+
"types": "./types/lib/*.d.ts",
|
|
31
|
+
"default": "./lib/*.js"
|
|
32
|
+
},
|
|
33
|
+
"./utils/buffers": {
|
|
34
|
+
"types": "./types/utils/buffers/index.d.ts",
|
|
35
|
+
"default": "./utils/buffers/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./utils/iterables": {
|
|
38
|
+
"types": "./types/utils/iterables/index.d.ts",
|
|
39
|
+
"default": "./utils/iterables/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./utils/observables": {
|
|
42
|
+
"types": "./types/utils/observables/index.d.ts",
|
|
43
|
+
"default": "./utils/observables/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./utils/streams": {
|
|
46
|
+
"types": "./types/utils/streams/index.d.ts",
|
|
47
|
+
"default": "./utils/streams/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./utils/*": {
|
|
50
|
+
"types": "./types/utils/*.d.ts",
|
|
51
|
+
"default": "./utils/*.js"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"files": [
|
|
55
|
+
"http/",
|
|
56
|
+
"lib/",
|
|
57
|
+
"utils/",
|
|
58
|
+
"types/",
|
|
59
|
+
"!**/*.spec.*.js",
|
|
60
|
+
"LICENSE",
|
|
61
|
+
"README.md",
|
|
62
|
+
"CHANGELOG.md"
|
|
63
|
+
],
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=22"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"lint": "eslint .",
|
|
69
|
+
"format": "prettier --write \"**/*.js\"",
|
|
70
|
+
"format:check": "prettier --check \"**/*.js\"",
|
|
71
|
+
"pretest": "npm run lint && npm run format:check",
|
|
72
|
+
"test": "c8 node --test \"**/*.spec.unit.js\" \"**/*.spec.func.js\"",
|
|
73
|
+
"test:watch": "node --test --watch \"**/*.spec.unit.js\" \"**/*.spec.func.js\"",
|
|
74
|
+
"preversion": "npm test",
|
|
75
|
+
"types": "tsc",
|
|
76
|
+
"prepublishOnly": "npm test && npm run types",
|
|
77
|
+
"prepare": "simple-git-hooks"
|
|
78
|
+
},
|
|
79
|
+
"simple-git-hooks": {
|
|
80
|
+
"pre-commit": "npx lint-staged"
|
|
81
|
+
},
|
|
82
|
+
"lint-staged": {
|
|
83
|
+
"*.js": [
|
|
84
|
+
"eslint --fix",
|
|
85
|
+
"prettier --write"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"repository": {
|
|
89
|
+
"type": "git",
|
|
90
|
+
"url": "git+https://github.com/CentralPing/ergo.git"
|
|
91
|
+
},
|
|
92
|
+
"keywords": [
|
|
93
|
+
"fast-fail",
|
|
94
|
+
"rest-api",
|
|
95
|
+
"json-api",
|
|
96
|
+
"http",
|
|
97
|
+
"middleware",
|
|
98
|
+
"streaming",
|
|
99
|
+
"security",
|
|
100
|
+
"security-headers",
|
|
101
|
+
"owasp",
|
|
102
|
+
"api-security"
|
|
103
|
+
],
|
|
104
|
+
"author": "Jason Cust <jason@centralping.com>",
|
|
105
|
+
"license": "MIT",
|
|
106
|
+
"bugs": {
|
|
107
|
+
"url": "https://github.com/CentralPing/ergo/issues"
|
|
108
|
+
},
|
|
109
|
+
"publishConfig": {
|
|
110
|
+
"access": "public"
|
|
111
|
+
},
|
|
112
|
+
"homepage": "https://github.com/CentralPing/ergo",
|
|
113
|
+
"funding": {
|
|
114
|
+
"type": "github",
|
|
115
|
+
"url": "https://github.com/sponsors/jasoncust"
|
|
116
|
+
},
|
|
117
|
+
"dependencies": {
|
|
118
|
+
"ajv": "^8.20.0",
|
|
119
|
+
"content-type": "^2.0.0",
|
|
120
|
+
"etag": "^1.8.1",
|
|
121
|
+
"negotiator": "^1.0.0"
|
|
122
|
+
},
|
|
123
|
+
"devDependencies": {
|
|
124
|
+
"@eslint/js": "^10.0.1",
|
|
125
|
+
"c8": "^11.0.0",
|
|
126
|
+
"eslint": "^10.0.3",
|
|
127
|
+
"eslint-config-prettier": "^10.1.8",
|
|
128
|
+
"globals": "^17.4.0",
|
|
129
|
+
"lint-staged": "^17.0.3",
|
|
130
|
+
"prettier": "^3.8.1",
|
|
131
|
+
"simple-git-hooks": "^2.12.1",
|
|
132
|
+
"typescript": "^6.0.3",
|
|
133
|
+
"undici": "^8.0.1"
|
|
134
|
+
},
|
|
135
|
+
"c8": {
|
|
136
|
+
"include": [
|
|
137
|
+
"http/**/*.js",
|
|
138
|
+
"lib/**/*.js",
|
|
139
|
+
"utils/**/*.js"
|
|
140
|
+
],
|
|
141
|
+
"exclude": [
|
|
142
|
+
"**/*.spec.*.js",
|
|
143
|
+
"**/node_modules/**",
|
|
144
|
+
"**/coverage/**",
|
|
145
|
+
"benchmarks/**",
|
|
146
|
+
"eslint.config.js"
|
|
147
|
+
],
|
|
148
|
+
"branches": 80,
|
|
149
|
+
"functions": 100,
|
|
150
|
+
"lines": 80,
|
|
151
|
+
"statements": 80,
|
|
152
|
+
"reporter": [
|
|
153
|
+
"text",
|
|
154
|
+
"lcov"
|
|
155
|
+
],
|
|
156
|
+
"all": true
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare function _default({ throwIfFail, ...options }?: {
|
|
2
|
+
throwIfFail?: boolean | undefined;
|
|
3
|
+
types?: string[] | undefined;
|
|
4
|
+
languages?: string[] | undefined;
|
|
5
|
+
charsets?: string[] | undefined;
|
|
6
|
+
encodings?: string[] | undefined;
|
|
7
|
+
}): Function;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare function _default({ limit, decompressedLimit, types, charset }?: {
|
|
2
|
+
limit?: number | undefined;
|
|
3
|
+
decompressedLimit?: number | undefined;
|
|
4
|
+
types?: string[] | undefined;
|
|
5
|
+
charset?: string | undefined;
|
|
6
|
+
}): (req: any) => Promise<{
|
|
7
|
+
type: string;
|
|
8
|
+
charset: string;
|
|
9
|
+
encoding: any;
|
|
10
|
+
length: number | undefined;
|
|
11
|
+
received: number;
|
|
12
|
+
boundary: string | undefined;
|
|
13
|
+
raw: string;
|
|
14
|
+
} | {
|
|
15
|
+
response: {
|
|
16
|
+
statusCode: any;
|
|
17
|
+
detail: any;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare function _default({ directives, public: isPublic, private: isPrivate, noCache, noStore, noTransform, mustRevalidate, proxyRevalidate, immutable, maxAge, sMaxAge, staleWhileRevalidate, staleIfError }?: {
|
|
2
|
+
directives?: string | undefined;
|
|
3
|
+
public?: boolean | undefined;
|
|
4
|
+
private?: boolean | undefined;
|
|
5
|
+
noCache?: boolean | undefined;
|
|
6
|
+
noStore?: boolean | undefined;
|
|
7
|
+
noTransform?: boolean | undefined;
|
|
8
|
+
mustRevalidate?: boolean | undefined;
|
|
9
|
+
proxyRevalidate?: boolean | undefined;
|
|
10
|
+
immutable?: boolean | undefined;
|
|
11
|
+
maxAge?: number | undefined;
|
|
12
|
+
sMaxAge?: number | undefined;
|
|
13
|
+
staleWhileRevalidate?: number | undefined;
|
|
14
|
+
staleIfError?: number | undefined;
|
|
15
|
+
}): Function;
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function _default(options?: {
|
|
2
|
+
origins?: string | Function | RegExp | string[] | undefined;
|
|
3
|
+
allowMethods?: string[] | undefined;
|
|
4
|
+
allowHeaders?: string | Function | RegExp | string[] | undefined;
|
|
5
|
+
exposeHeaders?: string | string[] | undefined;
|
|
6
|
+
allowCredentials?: boolean | undefined;
|
|
7
|
+
maxAge?: number | undefined;
|
|
8
|
+
}): Function;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function _default({ cookieTokenName, headerTokenName, cookieUuidName, secret, encoding, cookieOptions }?: {
|
|
2
|
+
cookieTokenName?: string | undefined;
|
|
3
|
+
headerTokenName?: string | undefined;
|
|
4
|
+
cookieUuidName?: string | undefined;
|
|
5
|
+
secret: string;
|
|
6
|
+
encoding?: string | undefined;
|
|
7
|
+
cookieOptions?: object | undefined;
|
|
8
|
+
}): object;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./main.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function _default({ log, error: logError, uuid, headerRequestIdName, headerRequestIpName, redactHeaders }?: {
|
|
2
|
+
log?: Function | undefined;
|
|
3
|
+
error?: Function | undefined;
|
|
4
|
+
uuid?: Function | undefined;
|
|
5
|
+
headerRequestIdName?: string | undefined;
|
|
6
|
+
headerRequestIpName?: string | undefined;
|
|
7
|
+
redactHeaders?: Set<string> | undefined;
|
|
8
|
+
}): object;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
compose: {
|
|
3
|
+
(...ops: (Function | any[])[]): Function;
|
|
4
|
+
all(...ops: (Function | any[])[]): Function;
|
|
5
|
+
};
|
|
6
|
+
handler: (pipeline: Function, sendOptions?: object) => Function;
|
|
7
|
+
accepts: ({ throwIfFail, ...options }?: {
|
|
8
|
+
throwIfFail?: boolean | undefined;
|
|
9
|
+
types?: string[] | undefined;
|
|
10
|
+
languages?: string[] | undefined;
|
|
11
|
+
charsets?: string[] | undefined;
|
|
12
|
+
encodings?: string[] | undefined;
|
|
13
|
+
}) => Function;
|
|
14
|
+
authorization: ({ strategies }?: {
|
|
15
|
+
strategies?: {
|
|
16
|
+
type: string;
|
|
17
|
+
attributes?: object;
|
|
18
|
+
authorizer: Function;
|
|
19
|
+
}[] | undefined;
|
|
20
|
+
}) => Function;
|
|
21
|
+
body: ({ limit, decompressedLimit, types, charset }?: {
|
|
22
|
+
limit?: number | undefined;
|
|
23
|
+
decompressedLimit?: number | undefined;
|
|
24
|
+
types?: string[] | undefined;
|
|
25
|
+
charset?: string | undefined;
|
|
26
|
+
}) => (req: any) => Promise<{
|
|
27
|
+
type: string;
|
|
28
|
+
charset: string;
|
|
29
|
+
encoding: any;
|
|
30
|
+
length: number | undefined;
|
|
31
|
+
received: number;
|
|
32
|
+
boundary: string | undefined;
|
|
33
|
+
raw: string;
|
|
34
|
+
} | {
|
|
35
|
+
response: {
|
|
36
|
+
statusCode: any;
|
|
37
|
+
detail: any;
|
|
38
|
+
};
|
|
39
|
+
}>;
|
|
40
|
+
cacheControl: ({ directives, public: isPublic, private: isPrivate, noCache, noStore, noTransform, mustRevalidate, proxyRevalidate, immutable, maxAge, sMaxAge, staleWhileRevalidate, staleIfError }?: {
|
|
41
|
+
directives?: string | undefined;
|
|
42
|
+
public?: boolean | undefined;
|
|
43
|
+
private?: boolean | undefined;
|
|
44
|
+
noCache?: boolean | undefined;
|
|
45
|
+
noStore?: boolean | undefined;
|
|
46
|
+
noTransform?: boolean | undefined;
|
|
47
|
+
mustRevalidate?: boolean | undefined;
|
|
48
|
+
proxyRevalidate?: boolean | undefined;
|
|
49
|
+
immutable?: boolean | undefined;
|
|
50
|
+
maxAge?: number | undefined;
|
|
51
|
+
sMaxAge?: number | undefined;
|
|
52
|
+
staleWhileRevalidate?: number | undefined;
|
|
53
|
+
staleIfError?: number | undefined;
|
|
54
|
+
}) => Function;
|
|
55
|
+
compress: ({ threshold, encodings }?: {
|
|
56
|
+
threshold?: number | undefined;
|
|
57
|
+
encodings?: string[] | undefined;
|
|
58
|
+
}) => Function;
|
|
59
|
+
cookie: (options?: object) => Function;
|
|
60
|
+
cors: (options?: {
|
|
61
|
+
origins?: string | Function | RegExp | string[] | undefined;
|
|
62
|
+
allowMethods?: string[] | undefined;
|
|
63
|
+
allowHeaders?: string | Function | RegExp | string[] | undefined;
|
|
64
|
+
exposeHeaders?: string | string[] | undefined;
|
|
65
|
+
allowCredentials?: boolean | undefined;
|
|
66
|
+
maxAge?: number | undefined;
|
|
67
|
+
}) => Function;
|
|
68
|
+
csrf: ({ cookieTokenName, headerTokenName, cookieUuidName, secret, encoding, cookieOptions }?: {
|
|
69
|
+
cookieTokenName?: string | undefined;
|
|
70
|
+
headerTokenName?: string | undefined;
|
|
71
|
+
cookieUuidName?: string | undefined;
|
|
72
|
+
secret: string;
|
|
73
|
+
encoding?: string | undefined;
|
|
74
|
+
cookieOptions?: object | undefined;
|
|
75
|
+
}) => object;
|
|
76
|
+
fromConnect: typeof fromConnect;
|
|
77
|
+
httpErrors: typeof httpErrors;
|
|
78
|
+
jsonApiQuery: (...options: any[]) => Function;
|
|
79
|
+
logger: ({ log, error: logError, uuid, headerRequestIdName, headerRequestIpName, redactHeaders }?: {
|
|
80
|
+
log?: Function | undefined;
|
|
81
|
+
error?: Function | undefined;
|
|
82
|
+
uuid?: Function | undefined;
|
|
83
|
+
headerRequestIdName?: string | undefined;
|
|
84
|
+
headerRequestIpName?: string | undefined;
|
|
85
|
+
redactHeaders?: Set<string> | undefined;
|
|
86
|
+
}) => object;
|
|
87
|
+
prefer: () => Function;
|
|
88
|
+
precondition: typeof precondition;
|
|
89
|
+
rateLimit: typeof rateLimit;
|
|
90
|
+
securityHeaders: (options?: {
|
|
91
|
+
contentSecurityPolicy?: string | false | undefined;
|
|
92
|
+
strictTransportSecurity?: string | false | undefined;
|
|
93
|
+
xContentTypeOptions?: string | false | undefined;
|
|
94
|
+
xFrameOptions?: string | false | undefined;
|
|
95
|
+
referrerPolicy?: string | false | undefined;
|
|
96
|
+
xXssProtection?: string | false | undefined;
|
|
97
|
+
permissionsPolicy?: string | undefined;
|
|
98
|
+
}) => Function;
|
|
99
|
+
url: () => Function;
|
|
100
|
+
send: ({ prettify, vary, etag, prefer, envelope }?: {
|
|
101
|
+
prettify?: boolean | undefined;
|
|
102
|
+
vary?: string[] | undefined;
|
|
103
|
+
etag?: boolean | undefined;
|
|
104
|
+
prefer?: boolean | undefined;
|
|
105
|
+
envelope?: boolean | Function | undefined;
|
|
106
|
+
}) => Function;
|
|
107
|
+
timeout: ({ ms, statusCode }?: {
|
|
108
|
+
ms?: number | undefined;
|
|
109
|
+
statusCode?: number | undefined;
|
|
110
|
+
}) => Function;
|
|
111
|
+
validate: (schemas?: {
|
|
112
|
+
body?: object | undefined;
|
|
113
|
+
query?: object | undefined;
|
|
114
|
+
params?: object | undefined;
|
|
115
|
+
}, options?: object) => Function;
|
|
116
|
+
};
|
|
117
|
+
export default _default;
|
|
118
|
+
import compose from '../utils/compose-with.js';
|
|
119
|
+
import { createResponseAcc } from '../utils/compose-with.js';
|
|
120
|
+
import { mergeResponse } from '../utils/compose-with.js';
|
|
121
|
+
import handler from './handler.js';
|
|
122
|
+
import accepts from './accepts.js';
|
|
123
|
+
import authorization from './authorization.js';
|
|
124
|
+
import body from './body.js';
|
|
125
|
+
import cacheControl from './cache-control.js';
|
|
126
|
+
import compress from './compress.js';
|
|
127
|
+
import cookie from './cookie.js';
|
|
128
|
+
import cors from './cors.js';
|
|
129
|
+
import csrf from './csrf.js';
|
|
130
|
+
import fromConnect from '../lib/from-connect.js';
|
|
131
|
+
import httpErrors from '../utils/http-errors.js';
|
|
132
|
+
import jsonApiQuery from './json-api-query.js';
|
|
133
|
+
import logger from './logger.js';
|
|
134
|
+
import prefer from './prefer.js';
|
|
135
|
+
import precondition from './precondition.js';
|
|
136
|
+
import rateLimit from './rate-limit.js';
|
|
137
|
+
import securityHeaders from './security-headers.js';
|
|
138
|
+
import url from './url.js';
|
|
139
|
+
import send from './send.js';
|
|
140
|
+
import timeout from './timeout.js';
|
|
141
|
+
import validate from './validate.js';
|
|
142
|
+
export { compose, createResponseAcc, mergeResponse, handler, accepts, authorization, body, cacheControl, compress, cookie, cors, csrf, fromConnect, httpErrors, jsonApiQuery, logger, prefer, precondition, rateLimit, securityHeaders, url, send, timeout, validate };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Precondition Required middleware (RFC 6585 §3).
|
|
3
|
+
*
|
|
4
|
+
* Enforces that unsafe requests include a conditional header (`If-Match` or
|
|
5
|
+
* `If-Unmodified-Since`) before the pipeline proceeds. This prevents "lost update"
|
|
6
|
+
* problems where a client overwrites changes made by another client without first
|
|
7
|
+
* fetching the current resource state.
|
|
8
|
+
*
|
|
9
|
+
* Placed in Stage 1 (Negotiation) for Fast Fail — the check is a cheap header
|
|
10
|
+
* inspection that short-circuits before authorization, body parsing, or execution.
|
|
11
|
+
*
|
|
12
|
+
* @module http/precondition
|
|
13
|
+
* @version 0.1.0
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* import {compose, precondition} from 'ergo';
|
|
18
|
+
*
|
|
19
|
+
* // Enforce on all requests (method scoping handled by pipeline builder)
|
|
20
|
+
* const pipeline = compose(
|
|
21
|
+
* [precondition(), 'precondition'],
|
|
22
|
+
* (req, res, acc) => ({response: {statusCode: 200, body: {updated: true}}})
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* // Enforce only on specific methods (standalone usage)
|
|
26
|
+
* const pipeline = compose(
|
|
27
|
+
* [precondition({methods: ['PUT', 'PATCH']}), 'precondition'],
|
|
28
|
+
* (req, res, acc) => ({response: {statusCode: 200, body: {updated: true}}})
|
|
29
|
+
* );
|
|
30
|
+
*
|
|
31
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc6585#section-3 RFC 6585 Section 3 - 428 Precondition Required}
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Create a precondition enforcement middleware.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} [options]
|
|
37
|
+
* @param {string[]|Set<string>} [options.methods] - HTTP methods to enforce on.
|
|
38
|
+
* When omitted, enforces unconditionally (the pipeline builder handles method scoping).
|
|
39
|
+
* When provided, only activates for the specified methods.
|
|
40
|
+
* @returns {function} - Middleware `(req) => void` that returns `{response: {statusCode: 428}}` if no conditional header is present
|
|
41
|
+
*/
|
|
42
|
+
export default function precondition({ methods }?: {
|
|
43
|
+
methods?: string[] | Set<string> | undefined;
|
|
44
|
+
}): Function;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a rate limiting middleware.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} [options]
|
|
5
|
+
* @param {number} [options.max=100] - Maximum requests per window
|
|
6
|
+
* @param {number} [options.windowMs=60000] - Window size in milliseconds (default: 1 minute)
|
|
7
|
+
* @param {object} [options.store] - Pluggable store (must implement `hit(key, windowMs)`)
|
|
8
|
+
* @param {function} [options.keyGenerator] - `(req) => string` client identifier (default: remote IP)
|
|
9
|
+
* @returns {function} - Middleware `(req) => {response}` that returns rate-limit header tuples on allowed
|
|
10
|
+
* requests and `{response: {statusCode: 429, retryAfter}}` when the limit is exceeded
|
|
11
|
+
*/
|
|
12
|
+
export default function rateLimit({ max, windowMs, store, keyGenerator }?: {
|
|
13
|
+
max?: number | undefined;
|
|
14
|
+
windowMs?: number | undefined;
|
|
15
|
+
store?: object | undefined;
|
|
16
|
+
keyGenerator?: Function | undefined;
|
|
17
|
+
}): Function;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare function _default(options?: {
|
|
2
|
+
contentSecurityPolicy?: string | false | undefined;
|
|
3
|
+
strictTransportSecurity?: string | false | undefined;
|
|
4
|
+
xContentTypeOptions?: string | false | undefined;
|
|
5
|
+
xFrameOptions?: string | false | undefined;
|
|
6
|
+
referrerPolicy?: string | false | undefined;
|
|
7
|
+
xXssProtection?: string | false | undefined;
|
|
8
|
+
permissionsPolicy?: string | undefined;
|
|
9
|
+
}): Function;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare function _default({ prettify, vary, etag, prefer, envelope }?: {
|
|
2
|
+
prettify?: boolean | undefined;
|
|
3
|
+
vary?: string[] | undefined;
|
|
4
|
+
etag?: boolean | undefined;
|
|
5
|
+
prefer?: boolean | undefined;
|
|
6
|
+
envelope?: boolean | Function | undefined;
|
|
7
|
+
}): Function;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare function _default({ types, languages, charsets, encodings }?: {
|
|
2
|
+
types?: string | string[] | undefined;
|
|
3
|
+
languages?: string | string[] | undefined;
|
|
4
|
+
charsets?: string | string[] | undefined;
|
|
5
|
+
encodings?: string | string[] | undefined;
|
|
6
|
+
}): Function;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared RFC 9457 instance injection helper.
|
|
3
|
+
*
|
|
4
|
+
* Auto-populates the `instance` property on an error from the response's
|
|
5
|
+
* `x-request-id` header, formatted as a `urn:uuid:` URI.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/attach-instance
|
|
8
|
+
* @version 0.1.0
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Set `err.instance` from the response's `x-request-id` header if not already set.
|
|
13
|
+
*
|
|
14
|
+
* @param {Error & {instance?: string}} err - Error to annotate
|
|
15
|
+
* @param {import('node:http').ServerResponse} res - HTTP response
|
|
16
|
+
*/
|
|
17
|
+
export default function attachInstance(err: Error & {
|
|
18
|
+
instance?: string;
|
|
19
|
+
}, res: any): void;
|