@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.
Files changed (155) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +139 -0
  4. package/http/accepts.js +69 -0
  5. package/http/authorization.js +65 -0
  6. package/http/body.js +311 -0
  7. package/http/cache-control.js +123 -0
  8. package/http/compress.js +157 -0
  9. package/http/cookie.js +39 -0
  10. package/http/cors.js +79 -0
  11. package/http/csrf.js +76 -0
  12. package/http/handler.js +74 -0
  13. package/http/index.js +13 -0
  14. package/http/json-api-query.js +53 -0
  15. package/http/logger.js +167 -0
  16. package/http/main.js +140 -0
  17. package/http/precondition.js +53 -0
  18. package/http/prefer.js +36 -0
  19. package/http/rate-limit.js +66 -0
  20. package/http/security-headers.js +62 -0
  21. package/http/send.js +399 -0
  22. package/http/timeout.js +47 -0
  23. package/http/url.js +47 -0
  24. package/http/validate.js +84 -0
  25. package/lib/accepts.js +49 -0
  26. package/lib/attach-instance.js +23 -0
  27. package/lib/authorization.js +187 -0
  28. package/lib/body/multiparse.js +173 -0
  29. package/lib/body/multipart/headers.js +69 -0
  30. package/lib/body/writer.js +73 -0
  31. package/lib/cookie/cookie.js +192 -0
  32. package/lib/cookie/index.js +14 -0
  33. package/lib/cookie/jar.js +106 -0
  34. package/lib/cookie/parse.js +101 -0
  35. package/lib/cors.js +191 -0
  36. package/lib/csrf.js +96 -0
  37. package/lib/from-connect.js +69 -0
  38. package/lib/json-api-query/index.js +25 -0
  39. package/lib/json-api-query/schema.json +105 -0
  40. package/lib/json-api-query/validate.js +56 -0
  41. package/lib/link.js +96 -0
  42. package/lib/prefer.js +52 -0
  43. package/lib/query.js +113 -0
  44. package/lib/rate-limit.js +115 -0
  45. package/lib/sanitize-quoted-string.js +28 -0
  46. package/lib/security-headers.js +125 -0
  47. package/lib/validate.js +80 -0
  48. package/lib/vary.js +40 -0
  49. package/package.json +158 -0
  50. package/types/http/accepts.d.ts +8 -0
  51. package/types/http/authorization.d.ts +8 -0
  52. package/types/http/body.d.ts +20 -0
  53. package/types/http/cache-control.d.ts +16 -0
  54. package/types/http/compress.d.ts +5 -0
  55. package/types/http/cookie.d.ts +2 -0
  56. package/types/http/cors.d.ts +9 -0
  57. package/types/http/csrf.d.ts +9 -0
  58. package/types/http/handler.d.ts +2 -0
  59. package/types/http/index.d.ts +1 -0
  60. package/types/http/json-api-query.d.ts +2 -0
  61. package/types/http/logger.d.ts +9 -0
  62. package/types/http/main.d.ts +142 -0
  63. package/types/http/precondition.d.ts +44 -0
  64. package/types/http/prefer.d.ts +2 -0
  65. package/types/http/rate-limit.d.ts +17 -0
  66. package/types/http/security-headers.d.ts +10 -0
  67. package/types/http/send.d.ts +8 -0
  68. package/types/http/timeout.d.ts +5 -0
  69. package/types/http/url.d.ts +2 -0
  70. package/types/http/validate.d.ts +6 -0
  71. package/types/lib/accepts.d.ts +7 -0
  72. package/types/lib/attach-instance.d.ts +19 -0
  73. package/types/lib/authorization.d.ts +6 -0
  74. package/types/lib/body/multiparse.d.ts +9 -0
  75. package/types/lib/body/multipart/headers.d.ts +2 -0
  76. package/types/lib/body/writer.d.ts +2 -0
  77. package/types/lib/cookie/cookie.d.ts +32 -0
  78. package/types/lib/cookie/index.d.ts +2 -0
  79. package/types/lib/cookie/jar.d.ts +8 -0
  80. package/types/lib/cookie/parse.d.ts +19 -0
  81. package/types/lib/cors.d.ts +9 -0
  82. package/types/lib/csrf.d.ts +32 -0
  83. package/types/lib/from-connect.d.ts +47 -0
  84. package/types/lib/json-api-query/index.d.ts +123 -0
  85. package/types/lib/json-api-query/validate.d.ts +5 -0
  86. package/types/lib/link.d.ts +37 -0
  87. package/types/lib/prefer.d.ts +36 -0
  88. package/types/lib/query.d.ts +6 -0
  89. package/types/lib/rate-limit.d.ts +76 -0
  90. package/types/lib/sanitize-quoted-string.d.ts +19 -0
  91. package/types/lib/security-headers.d.ts +24 -0
  92. package/types/lib/validate.d.ts +16 -0
  93. package/types/lib/vary.d.ts +17 -0
  94. package/types/utils/attempt.d.ts +2 -0
  95. package/types/utils/buffers/index.d.ts +2 -0
  96. package/types/utils/buffers/match.d.ts +10 -0
  97. package/types/utils/buffers/split.d.ts +10 -0
  98. package/types/utils/compose-with.d.ts +40 -0
  99. package/types/utils/compose.d.ts +83 -0
  100. package/types/utils/flat-array.d.ts +2 -0
  101. package/types/utils/get.d.ts +5 -0
  102. package/types/utils/http-errors.d.ts +22 -0
  103. package/types/utils/iterables/buffer-split.d.ts +2 -0
  104. package/types/utils/iterables/chain.d.ts +2 -0
  105. package/types/utils/iterables/exec-all.d.ts +2 -0
  106. package/types/utils/iterables/filter.d.ts +2 -0
  107. package/types/utils/iterables/for-each.d.ts +2 -0
  108. package/types/utils/iterables/from-stream.d.ts +2 -0
  109. package/types/utils/iterables/index.d.ts +10 -0
  110. package/types/utils/iterables/map.d.ts +2 -0
  111. package/types/utils/iterables/range.d.ts +24 -0
  112. package/types/utils/iterables/reduce.d.ts +2 -0
  113. package/types/utils/iterables/take.d.ts +2 -0
  114. package/types/utils/observables/buffer-split.d.ts +2 -0
  115. package/types/utils/observables/chain.d.ts +2 -0
  116. package/types/utils/observables/index.d.ts +4 -0
  117. package/types/utils/observables/map.d.ts +2 -0
  118. package/types/utils/observables/take.d.ts +2 -0
  119. package/types/utils/pick.d.ts +2 -0
  120. package/types/utils/set.d.ts +2 -0
  121. package/types/utils/streams/index.d.ts +2 -0
  122. package/types/utils/streams/meter.d.ts +5 -0
  123. package/types/utils/streams/tee.d.ts +2 -0
  124. package/types/utils/type.d.ts +2 -0
  125. package/utils/attempt.js +37 -0
  126. package/utils/buffers/index.js +13 -0
  127. package/utils/buffers/match.js +96 -0
  128. package/utils/buffers/split.js +55 -0
  129. package/utils/compose-with.js +232 -0
  130. package/utils/compose.js +165 -0
  131. package/utils/flat-array.js +24 -0
  132. package/utils/get.js +39 -0
  133. package/utils/http-errors.js +113 -0
  134. package/utils/iterables/buffer-split.js +117 -0
  135. package/utils/iterables/chain.js +32 -0
  136. package/utils/iterables/exec-all.js +42 -0
  137. package/utils/iterables/filter.js +35 -0
  138. package/utils/iterables/for-each.js +33 -0
  139. package/utils/iterables/from-stream.js +29 -0
  140. package/utils/iterables/index.js +21 -0
  141. package/utils/iterables/map.js +47 -0
  142. package/utils/iterables/range.js +34 -0
  143. package/utils/iterables/reduce.js +43 -0
  144. package/utils/iterables/take.js +36 -0
  145. package/utils/observables/buffer-split.js +109 -0
  146. package/utils/observables/chain.js +33 -0
  147. package/utils/observables/index.js +19 -0
  148. package/utils/observables/map.js +34 -0
  149. package/utils/observables/take.js +40 -0
  150. package/utils/pick.js +41 -0
  151. package/utils/set.js +38 -0
  152. package/utils/streams/index.js +11 -0
  153. package/utils/streams/meter.js +98 -0
  154. package/utils/streams/tee.js +84 -0
  155. package/utils/type.js +47 -0
@@ -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,8 @@
1
+ declare function _default({ strategies }?: {
2
+ strategies?: {
3
+ type: string;
4
+ attributes?: object;
5
+ authorizer: Function;
6
+ }[] | 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,5 @@
1
+ declare function _default({ threshold, encodings }?: {
2
+ threshold?: number | undefined;
3
+ encodings?: string[] | undefined;
4
+ }): Function;
5
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare function _default(options?: object): Function;
2
+ 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,2 @@
1
+ declare function _default(pipeline: Function, sendOptions?: object): Function;
2
+ export default _default;
@@ -0,0 +1 @@
1
+ export * from "./main.js";
@@ -0,0 +1,2 @@
1
+ declare function _default(...options: any[]): Function;
2
+ export default _default;
@@ -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,2 @@
1
+ declare function _default(): Function;
2
+ export default _default;
@@ -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,5 @@
1
+ declare function _default({ ms, statusCode }?: {
2
+ ms?: number | undefined;
3
+ statusCode?: number | undefined;
4
+ }): Function;
5
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare function _default(): Function;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ declare function _default(schemas?: {
2
+ body?: object | undefined;
3
+ query?: object | undefined;
4
+ params?: object | undefined;
5
+ }, options?: object): Function;
6
+ 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;