@centralping/ergo 0.1.0-beta.3 → 0.1.0

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 (61) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +35 -1
  3. package/http/accepts.js +5 -6
  4. package/http/authorization.js +0 -1
  5. package/http/body.js +4 -2
  6. package/http/cache-control.js +0 -1
  7. package/http/compress.js +0 -1
  8. package/http/cookie.js +0 -1
  9. package/http/cors.js +0 -2
  10. package/http/csrf.js +0 -2
  11. package/http/handler.js +0 -1
  12. package/http/idempotency.js +0 -1
  13. package/http/json-api-query.js +0 -2
  14. package/http/logger.js +0 -1
  15. package/http/precondition.js +0 -1
  16. package/http/prefer.js +0 -1
  17. package/http/rate-limit.js +0 -2
  18. package/http/security-headers.js +0 -1
  19. package/http/send.js +0 -1
  20. package/http/timeout.js +0 -1
  21. package/http/url.js +0 -2
  22. package/http/validate.js +9 -7
  23. package/lib/accepts.js +0 -1
  24. package/lib/authorization.js +0 -1
  25. package/lib/cors.js +0 -3
  26. package/lib/from-connect.js +0 -1
  27. package/lib/json-api-query/validate.js +0 -1
  28. package/lib/validate.js +14 -1
  29. package/package.json +2 -1
  30. package/types/http/accepts.d.ts +13 -1
  31. package/types/http/authorization.d.ts +5 -1
  32. package/types/http/cache-control.d.ts +5 -1
  33. package/types/http/compress.d.ts +1 -1
  34. package/types/http/cookie.d.ts +3 -1
  35. package/types/http/cors.d.ts +13 -1
  36. package/types/http/csrf.d.ts +11 -1
  37. package/types/http/handler.d.ts +1 -1
  38. package/types/http/idempotency.d.ts +28 -2
  39. package/types/http/json-api-query.d.ts +6 -1
  40. package/types/http/logger.d.ts +20 -1
  41. package/types/http/main.d.ts +106 -18
  42. package/types/http/precondition.d.ts +5 -2
  43. package/types/http/prefer.d.ts +1 -1
  44. package/types/http/rate-limit.d.ts +13 -3
  45. package/types/http/security-headers.d.ts +5 -1
  46. package/types/http/send.d.ts +1 -1
  47. package/types/http/timeout.d.ts +1 -1
  48. package/types/http/url.d.ts +9 -1
  49. package/types/http/validate.d.ts +9 -1
  50. package/types/lib/accepts.d.ts +6 -1
  51. package/types/lib/authorization.d.ts +1 -1
  52. package/types/lib/cors.d.ts +25 -1
  53. package/types/lib/from-connect.d.ts +1 -2
  54. package/types/lib/json-api-query/validate.d.ts +1 -1
  55. package/types/lib/validate.d.ts +7 -2
  56. package/types/utils/attempt.d.ts +1 -1
  57. package/types/utils/compose-with.d.ts +2 -4
  58. package/types/utils/compose.d.ts +4 -7
  59. package/utils/attempt.js +0 -1
  60. package/utils/compose-with.js +0 -2
  61. package/utils/compose.js +0 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ### Fixed
8
+
9
+ - **BREAKING (types only)**: All middleware factory `.d.ts` declarations now emit inferred
10
+ function signatures instead of `Function`. `compose()`/`composeWith()` return
11
+ `(...args) => Promise<object>` instead of `Function`. `csrf()` and `logger()` emit
12
+ full object types instead of `object`. No runtime behavior changes. (#75)
13
+
14
+ ### Added
15
+
16
+ - TypeScript usage example alongside the JavaScript Quick Start in `README.md`. (#74)
17
+
18
+ ## [0.1.0-beta.4] - 2026-05-29
19
+
20
+ ### Added
21
+
22
+ - JSON Schema `format` keyword support via `ajv-formats`. Standard formats (`email`, `uri`,
23
+ `date-time`, `uuid`, etc.) are validated by default. Opt out with `formats: false` or select
24
+ specific formats with an array (e.g. `formats: ['email', 'uri']`). (#58)
25
+
26
+ ### Fixed
27
+
28
+ - **BREAKING**: `accepts()` now defaults `throwIfFail` to `true`, enforcing 406 responses for unsatisfied content negotiation per the Fast Fail pipeline contract. Set `throwIfFail: false` to restore the previous informational-only behavior. (#55)
29
+ - `body()` default types now include `application/merge-patch+json` (RFC 7386) and `application/json-patch+json` (RFC 6902), resolving 415 rejections for valid PATCH content types that ergo-router's `strictPatch` allows. (#56)
30
+ - `validate()` params validation now checks `acc.route.params` (ergo-router convention) with fallback to `acc.params` (standalone), fixing silent no-op validation when used with ergo-router. (#57)
31
+
5
32
  ## [0.1.0-beta.1] - 2026-05-20
6
33
 
7
34
  ### Changed
package/README.md CHANGED
@@ -72,6 +72,40 @@ const pipeline = compose(
72
72
  createServer(handler(pipeline)).listen(3000);
73
73
  ```
74
74
 
75
+ <details>
76
+ <summary>TypeScript</summary>
77
+
78
+ ```ts
79
+ import {createServer, type IncomingMessage, type ServerResponse} from 'node:http';
80
+ import {compose, handler, logger, cors, authorization, body} from '@centralping/ergo';
81
+
82
+ interface AuthResult {
83
+ authorized: boolean;
84
+ info?: {uid: number};
85
+ }
86
+
87
+ const pipeline = compose(
88
+ [logger(), 'log'],
89
+ [cors(), 'cors'],
90
+ [authorization({strategies: [{
91
+ type: 'Bearer' as const,
92
+ authorizer: (_: Record<string, string>, token: string): AuthResult =>
93
+ token === 'my-token' ? {authorized: true, info: {uid: 1}} : {authorized: false}
94
+ }]}), 'auth'],
95
+ [body(), 'body'],
96
+ (req: IncomingMessage, res: ServerResponse, acc: {auth: AuthResult['info']; body: {parsed: unknown}}) =>
97
+ ({response: {body: {user: acc.auth, data: acc.body.parsed}}})
98
+ );
99
+
100
+ createServer(handler(pipeline)).listen(3000);
101
+ ```
102
+
103
+ > **Note:** The type annotations above represent the expected types for the accumulator
104
+ > properties. As ergo's `.d.ts` type declarations improve, these types will be inferred
105
+ > automatically — removing the need for explicit annotations.
106
+
107
+ </details>
108
+
75
109
  ## Middleware Overview
76
110
 
77
111
  | Middleware | Description | Standard |
@@ -135,4 +169,4 @@ npm run format # Prettier
135
169
 
136
170
  ## License
137
171
 
138
- [MIT](LICENSE) &copy; 2019-present Jason Cust
172
+ [MIT](https://github.com/CentralPing/ergo/blob/main/LICENSE) &copy; 2019-present Jason Cust
package/http/accepts.js CHANGED
@@ -5,9 +5,9 @@
5
5
  * `Accept-Charset`, and `Accept-Encoding` request headers and determine the best
6
6
  * matching content type, language, charset, and encoding for the response.
7
7
  *
8
- * When `throwIfFail` is true, returns `{response: {statusCode: 406, detail}}` for any
9
- * header that cannot be satisfied, enforcing strict content negotiation in the
10
- * Fast Fail pipeline.
8
+ * By default, returns `{response: {statusCode: 406, detail}}` for any header
9
+ * that cannot be satisfied, enforcing strict content negotiation in the Fast
10
+ * Fail pipeline. Set `throwIfFail: false` for informational-only negotiation.
11
11
  *
12
12
  * @module http/accepts
13
13
  * @since 0.1.0
@@ -37,14 +37,13 @@ const headerMap = {
37
37
  * Creates a content negotiation middleware.
38
38
  *
39
39
  * @param {object} [options] - Negotiation configuration
40
- * @param {boolean} [options.throwIfFail=false] - Return `{response: {statusCode: 406, detail}}` if any negotiation key is undefined
40
+ * @param {boolean} [options.throwIfFail=true] - Return `{response: {statusCode: 406, detail}}` if any negotiation key is undefined
41
41
  * @param {string[]} [options.types] - Acceptable media types
42
42
  * @param {string[]} [options.languages] - Acceptable languages
43
43
  * @param {string[]} [options.charsets] - Acceptable character sets
44
44
  * @param {string[]} [options.encodings] - Acceptable content encodings
45
- * @returns {function} - Middleware `(req) => {type, language, charset, encoding}` on success, or `{response: {statusCode: 406, detail: string}}` when `throwIfFail` is true and any negotiation value is undefined
46
45
  */
47
- export default ({throwIfFail = false, ...options} = {}) => {
46
+ export default ({throwIfFail = true, ...options} = {}) => {
48
47
  const acceptor = accepts(options);
49
48
 
50
49
  return ({headers = {}} = {}) => {
@@ -44,7 +44,6 @@ import authorize from '../lib/authorization.js';
44
44
  *
45
45
  * @param {object} [options] - Authorization configuration
46
46
  * @param {Array<{type: string, attributes?: object, authorizer: function}>} [options.strategies=[]] - Authentication strategy definitions
47
- * @returns {function} - Async middleware `(req) => info` on success; on failure `{response: {statusCode: 401|403, headers?}}` with optional `WWW-Authenticate` header tuples from `authenticate`
48
47
  */
49
48
  export default ({strategies = []} = {}) => {
50
49
  const authorizer = authorize(strategies);
package/http/body.js CHANGED
@@ -2,7 +2,8 @@
2
2
  * @fileoverview HTTP middleware factory for request body parsing.
3
3
  *
4
4
  * Reads and parses the request body according to the declared `Content-Type`. Supports:
5
- * - `application/json` and `application/vnd.api+json` — parsed via `JSON.parse`
5
+ * - `application/json`, `application/vnd.api+json`, `application/merge-patch+json`
6
+ * (RFC 7386), and `application/json-patch+json` (RFC 6902) — parsed via `JSON.parse`
6
7
  * - `application/x-www-form-urlencoded` — parsed via the query string parser
7
8
  * - `multipart/form-data` — parsed via the RFC 7578 streaming multipart parser
8
9
  *
@@ -117,7 +118,6 @@ const decompressors = new Proxy(
117
118
  * @param {number} [options.decompressedLimit] - Maximum decompressed body size (default 10 * limit, capped at MAX_DECOMPRESSED)
118
119
  * @param {string[]} [options.types] - Allowed Content-Type MIME types
119
120
  * @param {string} [options.charset='utf-8'] - Default character encoding
120
- * @returns {function} - Async middleware `(req) => {type, charset, encoding, length, received, boundary, raw, parsed}` on success; on error `{response: {statusCode: 400|411|413|415, detail: string}}`. Errors without `statusCode` are rethrown.
121
121
  */
122
122
  const DEFAULT_LIMIT = 1 << 20; // 1 MiB
123
123
  const MAX_DECOMPRESSED = 10 * DEFAULT_LIMIT; // 10 MiB hard cap
@@ -128,6 +128,8 @@ export default ({
128
128
  types = [
129
129
  'application/vnd.api+json',
130
130
  'application/json',
131
+ 'application/merge-patch+json',
132
+ 'application/json-patch+json',
131
133
  'application/x-www-form-urlencoded',
132
134
  'multipart/form-data'
133
135
  ],
@@ -43,7 +43,6 @@
43
43
  * @param {number} [options.sMaxAge] - `s-maxage` value in seconds
44
44
  * @param {number} [options.staleWhileRevalidate] - `stale-while-revalidate` value in seconds
45
45
  * @param {number} [options.staleIfError] - `stale-if-error` value in seconds
46
- * @returns {function} - Ergo middleware `() => Array<[string, string]>`
47
46
  */
48
47
  export default ({
49
48
  directives,
package/http/compress.js CHANGED
@@ -40,7 +40,6 @@ const COMPRESSIBLE_RE = /^(text\/|application\/(json|javascript|xml|x-www-form-u
40
40
  * @param {object} [options] - Compression configuration
41
41
  * @param {number} [options.threshold=1024] - Minimum byte size before compression is applied
42
42
  * @param {string[]} [options.encodings=['br','gzip','deflate']] - Supported encodings in priority order
43
- * @returns {function} - Ergo middleware `(req, res) => void` that wraps `res.write`/`res.end`
44
43
  */
45
44
  export default ({threshold = 1024, encodings = ['br', 'gzip', 'deflate']} = {}) => {
46
45
  return (req, res) => {
package/http/cookie.js CHANGED
@@ -31,7 +31,6 @@ import {parse, jar} from '../lib/cookie/index.js';
31
31
  * Creates a cookie parsing middleware.
32
32
  *
33
33
  * @param {object} [options] - Options forwarded to the RFC 6265 cookie parser
34
- * @returns {function} - Ergo middleware `({headers}) => CookieJar`
35
34
  */
36
35
  export default options =>
37
36
  ({headers: {cookie} = {}} = {}) =>
package/http/cors.js CHANGED
@@ -39,8 +39,6 @@ import cors from '../lib/cors.js';
39
39
  * @param {string|string[]} [options.exposeHeaders] - Headers to expose to the client
40
40
  * @param {boolean} [options.allowCredentials=false] - Whether to allow credentials
41
41
  * @param {number} [options.maxAge] - Preflight cache duration in seconds
42
- * @returns {function} - Ergo middleware that returns `undefined` for non-CORS requests,
43
- * `{response: {headers}}` when allowed, or `{response: {statusCode: 403}}` when denied
44
42
  */
45
43
  export default options => {
46
44
  const corsValidator = cors(options);
package/http/csrf.js CHANGED
@@ -46,8 +46,6 @@ import {issue, verify} from '../lib/csrf.js';
46
46
  * @param {string} options.secret - HMAC secret for token signing
47
47
  * @param {string} [options.encoding] - Token encoding (default: base64)
48
48
  * @param {object} [options.cookieOptions={}] - Cookie directives passed to the cookie factory
49
- * @returns {object} - Object with `issue(req, res, ...rest)` and `verify(req, res, ...rest)` methods;
50
- * `verify` returns `{response: {statusCode: 403}}` when CSRF token verification fails
51
49
  */
52
50
  export default ({
53
51
  cookieTokenName = 'CSRF-TOKEN',
package/http/handler.js CHANGED
@@ -37,7 +37,6 @@ import createSend from './send.js';
37
37
  *
38
38
  * @param {function} pipeline - Composed middleware pipeline
39
39
  * @param {object} [sendOptions] - Options forwarded to `send()`
40
- * @returns {function} - Async handler `(req, res) => void` for `http.createServer()`
41
40
  */
42
41
  export default (pipeline, sendOptions = {}) => {
43
42
  const send = createSend(sendOptions);
@@ -40,7 +40,6 @@ const DEFAULT_METHODS = new Set(['POST', 'PATCH']);
40
40
  * on applicable methods
41
41
  * @param {Set<string>|string[]} [options.methods] - HTTP methods to apply idempotency to
42
42
  * (default: POST, PATCH)
43
- * @returns {function} - Middleware `(req, res, domainAcc) => {value?, response?}`
44
43
  */
45
44
  export default function idempotency({store, ttlMs, required = false, methods} = {}) {
46
45
  const _store = store ?? new IdempotencyStore(ttlMs ? {ttlMs} : undefined);
@@ -30,8 +30,6 @@ import {validate} from '../lib/json-api-query/index.js';
30
30
  * Creates a JSON:API query validation middleware.
31
31
  *
32
32
  * @param {...*} options - Options forwarded to the underlying JSON:API validator
33
- * @returns {function} - Ergo middleware `(req, res, acc) => void`; returns
34
- * `{response: {statusCode: 400}}` when JSON:API query parameters fail validation
35
33
  */
36
34
  export default (...options) => {
37
35
  const validator = validate(...options);
package/http/logger.js CHANGED
@@ -73,7 +73,6 @@ const host = Object.freeze({
73
73
  * @param {string} [options.headerRequestIpName] - Client IP header name (default: 'x-real-ip')
74
74
  * @param {Set<string>} [options.redactHeaders] - Header names to replace with '[REDACTED]' in logs
75
75
  * (default: authorization, proxy-authorization, cookie, set-cookie)
76
- * @returns {object} - Log entry with request metadata and host info (statusCode/duration added on finish)
77
76
  */
78
77
  export default ({
79
78
  /* eslint-disable-next-line no-console */
@@ -37,7 +37,6 @@
37
37
  * @param {string[]|Set<string>} [options.methods] - HTTP methods to enforce on.
38
38
  * When omitted, enforces unconditionally (the pipeline builder handles method scoping).
39
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
40
  */
42
41
  export default function precondition({methods} = {}) {
43
42
  const methodSet = methods ? (methods instanceof Set ? methods : new Set(methods)) : undefined;
package/http/prefer.js CHANGED
@@ -28,7 +28,6 @@ import parsePrefer from '../lib/prefer.js';
28
28
  /**
29
29
  * Creates a Prefer header parsing middleware.
30
30
  *
31
- * @returns {function} - Middleware `(req) => object` returning parsed preferences
32
31
  */
33
32
  export default () => {
34
33
  return req => parsePrefer(req.headers?.prefer);
@@ -33,8 +33,6 @@ import {MemoryStore, checkRateLimit, defaultKeyGenerator} from '../lib/rate-limi
33
33
  * @param {number} [options.windowMs=60000] - Window size in milliseconds (default: 1 minute)
34
34
  * @param {object} [options.store] - Pluggable store (must implement `hit(key, windowMs)`)
35
35
  * @param {function} [options.keyGenerator] - `(req) => string` client identifier (default: remote IP)
36
- * @returns {function} - Middleware `(req) => {response}` that returns rate-limit header tuples on allowed
37
- * requests and `{response: {statusCode: 429, retryAfter}}` when the limit is exceeded
38
36
  */
39
37
  export default function rateLimit({max = 100, windowMs = 60000, store, keyGenerator} = {}) {
40
38
  const _store = store ?? new MemoryStore();
@@ -52,7 +52,6 @@ import buildSecurityHeaderTuples from '../lib/security-headers.js';
52
52
  * @param {string|false} [options.referrerPolicy='no-referrer'] - Referrer-Policy header
53
53
  * @param {string|false} [options.xXssProtection='0'] - X-XSS-Protection header (0 disables the browser filter)
54
54
  * @param {string} [options.permissionsPolicy] - Permissions-Policy header (omitted by default)
55
- * @returns {function} - Ergo middleware `() => Array<[string, string]>`
56
55
  */
57
56
  export default (options = {}) => {
58
57
  const headerTuples = buildSecurityHeaderTuples(options);
package/http/send.js CHANGED
@@ -108,7 +108,6 @@ function bodyType(body) {
108
108
  * @param {boolean|function} [options.envelope=false] - Wrap 2xx Object bodies in a response
109
109
  * envelope. `false` (default) — no envelope. `true` — built-in format `{id, status, data,
110
110
  * count?}`. `function(body, ctx)` — custom envelope.
111
- * @returns {function} - `(req, res, responseAcc, domainAcc) => void`
112
111
  */
113
112
  export default ({
114
113
  prettify = false,
package/http/timeout.js CHANGED
@@ -29,7 +29,6 @@
29
29
  * @param {object} [options] - Timeout configuration
30
30
  * @param {number} [options.ms=30000] - Timeout in milliseconds
31
31
  * @param {number} [options.statusCode=408] - HTTP status code on timeout (408 or 504)
32
- * @returns {function} - Ergo middleware `(req, res, domainAcc, responseAcc) => void`
33
32
  */
34
33
  export default ({ms = 30000, statusCode = 408} = {}) => {
35
34
  return (req, res, domainAcc, responseAcc) => {
package/http/url.js CHANGED
@@ -26,8 +26,6 @@ import queryParse from '../lib/query.js';
26
26
 
27
27
  /**
28
28
  * Creates a URL parsing middleware.
29
- *
30
- * @returns {function} - Ergo middleware `({url}) => {query, pathname, search}`
31
29
  */
32
30
  export default () =>
33
31
  ({url} = {}) => {
package/http/validate.js CHANGED
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * @fileoverview HTTP middleware factory for JSON Schema validation.
3
3
  *
4
- * Validates properties from the accumulator (body, url, params) against provided JSON
5
- * Schemas using AJV. Schemas are compiled once at middleware creation time for performance.
4
+ * Validates properties from the accumulator (body, url, route params) against provided
5
+ * JSON Schemas using AJV. Schemas are compiled once at middleware creation time for
6
+ * performance. Route params are resolved from `acc.route.params` (ergo-router) with
7
+ * fallback to `acc.params` (standalone).
6
8
  *
7
9
  * Returns `{response: {statusCode: 422, detail: ...}}` with structured error details on validation failure.
8
10
  * Must be placed after `body()` and/or `url()` in the pipeline so accumulator values
@@ -39,10 +41,10 @@ import createValidator from '../lib/validate.js';
39
41
  * @param {object} [schemas] - Schema map; each key corresponds to an accumulator property
40
42
  * @param {object} [schemas.body] - JSON Schema for the parsed request body
41
43
  * @param {object} [schemas.query] - JSON Schema for parsed query parameters
42
- * @param {object} [schemas.params] - JSON Schema for route path parameters
44
+ * @param {object} [schemas.params] - JSON Schema for route path parameters (reads `acc.route.params` or `acc.params`)
43
45
  * @param {object} [options] - AJV options forwarded to each compiled validator
44
- * @returns {function} - Ergo middleware `(req, res, acc) => void` that returns `{response: {statusCode: 422}}`
45
- * (with `detail` and `details` from AJV) on validation failure
46
+ * @param {boolean|Array<string>|object} [options.formats] - Format keyword support via
47
+ * `ajv-formats`; forwarded to `createValidator`. Defaults to all standard formats enabled
46
48
  */
47
49
  export default (schemas = {}, options = {}) => {
48
50
  const validators = {};
@@ -67,8 +69,8 @@ export default (schemas = {}, options = {}) => {
67
69
  validators.query(acc.url.query);
68
70
  }
69
71
 
70
- if (validators.params && acc.params) {
71
- validators.params(acc.params);
72
+ if (validators.params && (acc.route?.params ?? acc.params)) {
73
+ validators.params(acc.route?.params ?? acc.params);
72
74
  }
73
75
  } catch (err) {
74
76
  return {
package/lib/accepts.js CHANGED
@@ -31,7 +31,6 @@ import flatArray from '../utils/flat-array.js';
31
31
  * @param {string|string[]} [options.languages] - Acceptable languages
32
32
  * @param {string|string[]} [options.charsets] - Acceptable charsets
33
33
  * @param {string|string[]} [options.encodings] - Acceptable encodings
34
- * @returns {function} - `(headers) => {type, language, charset, encoding}`
35
34
  */
36
35
  export default ({types, languages, charsets, encodings} = {}) =>
37
36
  (headers = {}) => {
@@ -38,7 +38,6 @@ import sanitizeQuotedString from './sanitize-quoted-string.js';
38
38
  * Creates an authorization dispatcher for the given strategy list.
39
39
  *
40
40
  * @param {Array<{type: string, attributes?: object, authorizer: function}>} strategies - Authentication strategy definitions
41
- * @returns {function} - Async `(authorization) => {authorized, info}`
42
41
  */
43
42
  export default (strategies = []) => {
44
43
  const dispatcher = createDispatcher(strategies);
package/lib/cors.js CHANGED
@@ -56,7 +56,6 @@ const defaultMethods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PU
56
56
  * @param {string|RegExp|function|Array} [options.allowHeaders='*'] - Allowed request headers
57
57
  * @param {string|string[]} [options.exposeHeaders] - Headers to expose to the client
58
58
  * @param {number} [options.maxAge] - Pre-flight cache duration in seconds
59
- * @returns {function} - `({origin, method, requestMethod, requestHeaders}) => {allowed, info}`
60
59
  */
61
60
  export default ({
62
61
  origins = '*', // '*', 'foo.com', /foo/, ['foo', /bar/]
@@ -131,7 +130,6 @@ export default ({
131
130
  *
132
131
  * @param {string|RegExp|function|Array<string|RegExp>} valid - Origin policy
133
132
  * @param {boolean} [allowCreds] - If true, wildcard reflects the request origin
134
- * @returns {function} - `(origin: string) => string|false` — returns the allowed origin or false
135
133
  */
136
134
  function configOriginValidator(valid, allowCreds) {
137
135
  if (valid === '*') {
@@ -151,7 +149,6 @@ function configOriginValidator(valid, allowCreds) {
151
149
  * Builds a request header validator function from an allowed-headers policy.
152
150
  *
153
151
  * @param {string|RegExp|function|Array<string|RegExp>} allowed - Header policy
154
- * @returns {function} - `(headers: string[]) => string[]` — returns filtered allowed headers
155
152
  */
156
153
  function configHeaderValidator(allowed) {
157
154
  if (allowed === '*') {
@@ -42,7 +42,6 @@
42
42
  * Wrap a Connect/Express-style middleware for use in an ergo pipeline.
43
43
  *
44
44
  * @param {function} middleware - Connect middleware `(req, res, next) => void`
45
- * @returns {function} - Ergo-compatible middleware `(req, res) => Promise<undefined>`
46
45
  */
47
46
  export default function fromConnect(middleware) {
48
47
  return (req, res) =>
@@ -39,7 +39,6 @@ import defaultSchema from './schema.json' with {type: 'json'};
39
39
  * @param {boolean|string} [options.coerceTypes='array'] - Coerce validated values to specified types.
40
40
  * @param {boolean} [options.ownProperties=true] - Restrict validation to own properties of data object.
41
41
  * @param {object} [schema] - JSON Schema 2020-12. Defaults to the included schema.
42
- * @returns {function} - The configured validator function.
43
42
  */
44
43
  export default (
45
44
  {coerceTypes = 'array', ownProperties = true, ...ajvOptions} = {},
package/lib/validate.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * @module lib/validate
11
11
  * @since 0.1.0
12
12
  * @requires ajv
13
+ * @requires ajv-formats
13
14
  * @requires ../utils/http-errors.js
14
15
  *
15
16
  * @example
@@ -25,6 +26,7 @@
25
26
  * validate({}); // throws 422 with details
26
27
  */
27
28
  import Ajv from 'ajv';
29
+ import addFormats from 'ajv-formats';
28
30
  import httpErrors from '../utils/http-errors.js';
29
31
 
30
32
  /**
@@ -34,8 +36,12 @@ import httpErrors from '../utils/http-errors.js';
34
36
  * @param {object} [options] - Validator options
35
37
  * @param {boolean} [options.allErrors=true] - Report all errors instead of stopping at the first
36
38
  * @param {boolean} [options.coerceTypes=false] - Coerce input values to match schema types
39
+ * @param {boolean|Array<string>|object} [options.formats] - Format keyword support via
40
+ * `ajv-formats`. `undefined` or `true` enables all standard formats; `false` disables (AJV
41
+ * strict mode rejects unknown formats); an array enables selective formats
42
+ * (e.g. `['email', 'uri']`); an object is passed as the full plugin config
43
+ * (e.g. `{mode: 'fast'}`)
37
44
  * @param {object} [options.ajv] - Additional AJV constructor options
38
- * @returns {function} - `validateData(data)` — returns `data` on success, throws 422 on failure
39
45
  * @throws {Error} 422 with `details` array if schema validation fails
40
46
  */
41
47
  export default function createValidator(schema, options = {}) {
@@ -45,6 +51,13 @@ export default function createValidator(schema, options = {}) {
45
51
  ...options.ajv
46
52
  });
47
53
 
54
+ if (options.formats !== false) {
55
+ addFormats(
56
+ ajv,
57
+ options.formats === true || options.formats === undefined ? undefined : options.formats
58
+ );
59
+ }
60
+
48
61
  const validate = ajv.compile(schema);
49
62
 
50
63
  return function validateData(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centralping/ergo",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0",
4
4
  "description": "A Fast Fail REST API toolkit for Node.js -- composable middleware with structured Negotiation, Authorization, Validation, and Execution stages.",
5
5
  "main": "http/index.js",
6
6
  "type": "module",
@@ -116,6 +116,7 @@
116
116
  },
117
117
  "dependencies": {
118
118
  "ajv": "^8.20.0",
119
+ "ajv-formats": "^3.0.1",
119
120
  "content-type": "^2.0.0",
120
121
  "etag": "^1.8.1",
121
122
  "negotiator": "^1.0.0"
@@ -4,5 +4,17 @@ declare function _default({ throwIfFail, ...options }?: {
4
4
  languages?: string[] | undefined;
5
5
  charsets?: string[] | undefined;
6
6
  encodings?: string[] | undefined;
7
- }): Function;
7
+ }): ({ headers }?: {
8
+ headers?: {} | undefined;
9
+ }) => {
10
+ type: any;
11
+ language: any;
12
+ charset: any;
13
+ encoding: any;
14
+ } | {
15
+ response: {
16
+ statusCode: number;
17
+ detail: string;
18
+ };
19
+ };
8
20
  export default _default;
@@ -4,5 +4,9 @@ declare function _default({ strategies }?: {
4
4
  attributes?: object;
5
5
  authorizer: Function;
6
6
  }[] | undefined;
7
- }): Function;
7
+ }): ({ headers: { authorization } }?: {
8
+ headers?: {
9
+ authorization?: string | undefined;
10
+ } | undefined;
11
+ }) => Promise<any>;
8
12
  export default _default;
@@ -12,5 +12,9 @@ declare function _default({ directives, public: isPublic, private: isPrivate, no
12
12
  sMaxAge?: number | undefined;
13
13
  staleWhileRevalidate?: number | undefined;
14
14
  staleIfError?: number | undefined;
15
- }): Function;
15
+ }): () => {
16
+ response: {
17
+ headers: string[][];
18
+ };
19
+ };
16
20
  export default _default;
@@ -1,5 +1,5 @@
1
1
  declare function _default({ threshold, encodings }?: {
2
2
  threshold?: number | undefined;
3
3
  encodings?: string[] | undefined;
4
- }): Function;
4
+ }): (req: any, res: any) => void;
5
5
  export default _default;
@@ -1,2 +1,4 @@
1
- declare function _default(options?: object): Function;
1
+ declare function _default(options?: object): ({ headers: { cookie } }?: {
2
+ headers?: {} | undefined;
3
+ }) => object;
2
4
  export default _default;
@@ -5,5 +5,17 @@ declare function _default(options?: {
5
5
  exposeHeaders?: string | string[] | undefined;
6
6
  allowCredentials?: boolean | undefined;
7
7
  maxAge?: number | undefined;
8
- }): Function;
8
+ }): ({ headers: { origin, "access-control-request-method": requestMethod, "access-control-request-headers": requestHeadersRaw }, method }?: {
9
+ headers?: {} | undefined;
10
+ }) => {
11
+ response: {
12
+ statusCode: number;
13
+ headers?: undefined;
14
+ };
15
+ } | {
16
+ response: {
17
+ headers: any[][];
18
+ statusCode?: undefined;
19
+ };
20
+ } | undefined;
9
21
  export default _default;
@@ -5,5 +5,15 @@ declare function _default({ cookieTokenName, headerTokenName, cookieUuidName, se
5
5
  secret: string;
6
6
  encoding?: string | undefined;
7
7
  cookieOptions?: object | undefined;
8
- }): object;
8
+ }): {
9
+ issue(req: any, res: any, acc: any): void;
10
+ verify({ headers: { [headerTokenName.toLowerCase()]: headerToken } }: {
11
+ headers?: {} | undefined;
12
+ } | undefined, res: any, acc: any): {
13
+ response: {
14
+ statusCode: number;
15
+ detail: string;
16
+ };
17
+ } | undefined;
18
+ };
9
19
  export default _default;
@@ -1,2 +1,2 @@
1
- declare function _default(pipeline: Function, sendOptions?: object): Function;
1
+ declare function _default(pipeline: Function, sendOptions?: object): (req: any, res: any) => Promise<void>;
2
2
  export default _default;
@@ -10,11 +10,37 @@
10
10
  * on applicable methods
11
11
  * @param {Set<string>|string[]} [options.methods] - HTTP methods to apply idempotency to
12
12
  * (default: POST, PATCH)
13
- * @returns {function} - Middleware `(req, res, domainAcc) => {value?, response?}`
14
13
  */
15
14
  export default function idempotency({ store, ttlMs, required, methods }?: {
16
15
  store?: object | undefined;
17
16
  ttlMs?: number | undefined;
18
17
  required?: boolean | undefined;
19
18
  methods?: string[] | Set<string> | undefined;
20
- }): Function;
19
+ }): (req: any, _res: any, domainAcc: any) => {
20
+ response?: undefined;
21
+ value?: undefined;
22
+ } | {
23
+ response: {
24
+ statusCode: number;
25
+ detail: string;
26
+ };
27
+ value?: undefined;
28
+ } | {
29
+ value: {
30
+ replayed: boolean;
31
+ key?: undefined;
32
+ fingerprint?: undefined;
33
+ complete?: undefined;
34
+ discard?: undefined;
35
+ };
36
+ response: any;
37
+ } | {
38
+ value: {
39
+ key: string;
40
+ fingerprint: string;
41
+ complete: (response: any) => any;
42
+ discard: () => any;
43
+ replayed?: undefined;
44
+ };
45
+ response?: undefined;
46
+ };
@@ -1,2 +1,7 @@
1
- declare function _default(...options: any[]): Function;
1
+ declare function _default(...options: any[]): (req: any, res: any, acc: any) => {
2
+ response: {
3
+ statusCode: number;
4
+ detail: string;
5
+ };
6
+ } | undefined;
2
7
  export default _default;
@@ -5,5 +5,24 @@ declare function _default({ log, error: logError, uuid, headerRequestIdName, hea
5
5
  headerRequestIdName?: string | undefined;
6
6
  headerRequestIpName?: string | undefined;
7
7
  redactHeaders?: Set<string> | undefined;
8
- }): object;
8
+ }): (req: any, res: any) => {
9
+ requestId: any;
10
+ timestamp: number;
11
+ ip: any;
12
+ method: any;
13
+ url: any;
14
+ httpVersion: any;
15
+ host: Readonly<{
16
+ hostname: any;
17
+ arch: any;
18
+ platform: any;
19
+ pid: any;
20
+ }>;
21
+ request: {
22
+ headers: object;
23
+ encrypted: any;
24
+ remoteAddress: any;
25
+ remotePort: any;
26
+ };
27
+ };
9
28
  export default _default;
@@ -1,23 +1,39 @@
1
1
  declare const _default: {
2
2
  compose: {
3
- (...ops: (Function | any[])[]): Function;
4
- all(...ops: (Function | any[])[]): Function;
3
+ (...ops: (Function | any[])[]): (...args: any[]) => Promise<object>;
4
+ all(...ops: (Function | any[])[]): (...args: any[]) => Promise<object>;
5
5
  };
6
- handler: (pipeline: Function, sendOptions?: object) => Function;
6
+ handler: (pipeline: Function, sendOptions?: object) => (req: any, res: any) => Promise<void>;
7
7
  accepts: ({ throwIfFail, ...options }?: {
8
8
  throwIfFail?: boolean | undefined;
9
9
  types?: string[] | undefined;
10
10
  languages?: string[] | undefined;
11
11
  charsets?: string[] | undefined;
12
12
  encodings?: string[] | undefined;
13
- }) => Function;
13
+ }) => ({ headers }?: {
14
+ headers?: {} | undefined;
15
+ }) => {
16
+ type: any;
17
+ language: any;
18
+ charset: any;
19
+ encoding: any;
20
+ } | {
21
+ response: {
22
+ statusCode: number;
23
+ detail: string;
24
+ };
25
+ };
14
26
  authorization: ({ strategies }?: {
15
27
  strategies?: {
16
28
  type: string;
17
29
  attributes?: object;
18
30
  authorizer: Function;
19
31
  }[] | undefined;
20
- }) => Function;
32
+ }) => ({ headers: { authorization } }?: {
33
+ headers?: {
34
+ authorization?: string | undefined;
35
+ } | undefined;
36
+ }) => Promise<any>;
21
37
  body: ({ limit, decompressedLimit, types, charset }?: {
22
38
  limit?: number | undefined;
23
39
  decompressedLimit?: number | undefined;
@@ -51,12 +67,18 @@ declare const _default: {
51
67
  sMaxAge?: number | undefined;
52
68
  staleWhileRevalidate?: number | undefined;
53
69
  staleIfError?: number | undefined;
54
- }) => Function;
70
+ }) => () => {
71
+ response: {
72
+ headers: string[][];
73
+ };
74
+ };
55
75
  compress: ({ threshold, encodings }?: {
56
76
  threshold?: number | undefined;
57
77
  encodings?: string[] | undefined;
58
- }) => Function;
59
- cookie: (options?: object) => Function;
78
+ }) => (req: any, res: any) => void;
79
+ cookie: (options?: object) => ({ headers: { cookie } }?: {
80
+ headers?: {} | undefined;
81
+ }) => object;
60
82
  cors: (options?: {
61
83
  origins?: string | Function | RegExp | string[] | undefined;
62
84
  allowMethods?: string[] | undefined;
@@ -64,7 +86,19 @@ declare const _default: {
64
86
  exposeHeaders?: string | string[] | undefined;
65
87
  allowCredentials?: boolean | undefined;
66
88
  maxAge?: number | undefined;
67
- }) => Function;
89
+ }) => ({ headers: { origin, "access-control-request-method": requestMethod, "access-control-request-headers": requestHeadersRaw }, method }?: {
90
+ headers?: {} | undefined;
91
+ }) => {
92
+ response: {
93
+ statusCode: number;
94
+ headers?: undefined;
95
+ };
96
+ } | {
97
+ response: {
98
+ headers: any[][];
99
+ statusCode?: undefined;
100
+ };
101
+ } | undefined;
68
102
  csrf: ({ cookieTokenName, headerTokenName, cookieUuidName, secret, encoding, cookieOptions }?: {
69
103
  cookieTokenName?: string | undefined;
70
104
  headerTokenName?: string | undefined;
@@ -72,11 +106,26 @@ declare const _default: {
72
106
  secret: string;
73
107
  encoding?: string | undefined;
74
108
  cookieOptions?: object | undefined;
75
- }) => object;
109
+ }) => {
110
+ issue(req: any, res: any, acc: any): void;
111
+ verify({ headers: { [headerTokenName.toLowerCase()]: headerToken } }: {
112
+ headers?: {} | undefined;
113
+ } | undefined, res: any, acc: any): {
114
+ response: {
115
+ statusCode: number;
116
+ detail: string;
117
+ };
118
+ } | undefined;
119
+ };
76
120
  fromConnect: typeof fromConnect;
77
121
  httpErrors: typeof httpErrors;
78
122
  idempotency: typeof idempotency;
79
- jsonApiQuery: (...options: any[]) => Function;
123
+ jsonApiQuery: (...options: any[]) => (req: any, res: any, acc: any) => {
124
+ response: {
125
+ statusCode: number;
126
+ detail: string;
127
+ };
128
+ } | undefined;
80
129
  logger: ({ log, error: logError, uuid, headerRequestIdName, headerRequestIpName, redactHeaders }?: {
81
130
  log?: Function | undefined;
82
131
  error?: Function | undefined;
@@ -84,8 +133,27 @@ declare const _default: {
84
133
  headerRequestIdName?: string | undefined;
85
134
  headerRequestIpName?: string | undefined;
86
135
  redactHeaders?: Set<string> | undefined;
87
- }) => object;
88
- prefer: () => Function;
136
+ }) => (req: any, res: any) => {
137
+ requestId: any;
138
+ timestamp: number;
139
+ ip: any;
140
+ method: any;
141
+ url: any;
142
+ httpVersion: any;
143
+ host: Readonly<{
144
+ hostname: any;
145
+ arch: any;
146
+ platform: any;
147
+ pid: any;
148
+ }>;
149
+ request: {
150
+ headers: object;
151
+ encrypted: any;
152
+ remoteAddress: any;
153
+ remotePort: any;
154
+ };
155
+ };
156
+ prefer: () => (req: any) => object;
89
157
  precondition: typeof precondition;
90
158
  rateLimit: typeof rateLimit;
91
159
  securityHeaders: (options?: {
@@ -96,24 +164,44 @@ declare const _default: {
96
164
  referrerPolicy?: string | false | undefined;
97
165
  xXssProtection?: string | false | undefined;
98
166
  permissionsPolicy?: string | undefined;
99
- }) => Function;
100
- url: () => Function;
167
+ }) => () => {
168
+ response: {
169
+ headers: [string, string][];
170
+ };
171
+ };
172
+ url: () => ({ url }?: {}) => {
173
+ query: any;
174
+ pathname: any;
175
+ search: undefined;
176
+ } | {
177
+ query: object;
178
+ pathname: any;
179
+ search: any;
180
+ };
101
181
  send: ({ prettify, vary, etag, prefer, envelope }?: {
102
182
  prettify?: boolean | undefined;
103
183
  vary?: string[] | undefined;
104
184
  etag?: boolean | undefined;
105
185
  prefer?: boolean | undefined;
106
186
  envelope?: boolean | Function | undefined;
107
- }) => Function;
187
+ }) => (req: any, res: any, responseAcc: any, domainAcc?: {}) => void;
108
188
  timeout: ({ ms, statusCode }?: {
109
189
  ms?: number | undefined;
110
190
  statusCode?: number | undefined;
111
- }) => Function;
191
+ }) => (req: any, res: any, domainAcc: any, responseAcc: any) => void;
112
192
  validate: (schemas?: {
113
193
  body?: object | undefined;
114
194
  query?: object | undefined;
115
195
  params?: object | undefined;
116
- }, options?: object) => Function;
196
+ }, options?: {
197
+ formats?: boolean | object | string[] | undefined;
198
+ }) => (req: any, res: any, acc: any) => {
199
+ response: {
200
+ statusCode: any;
201
+ detail: any;
202
+ details: any;
203
+ };
204
+ } | undefined;
117
205
  };
118
206
  export default _default;
119
207
  import compose from '../utils/compose-with.js';
@@ -36,8 +36,11 @@
36
36
  * @param {string[]|Set<string>} [options.methods] - HTTP methods to enforce on.
37
37
  * When omitted, enforces unconditionally (the pipeline builder handles method scoping).
38
38
  * When provided, only activates for the specified methods.
39
- * @returns {function} - Middleware `(req) => void` that returns `{response: {statusCode: 428}}` if no conditional header is present
40
39
  */
41
40
  export default function precondition({ methods }?: {
42
41
  methods?: string[] | Set<string> | undefined;
43
- }): Function;
42
+ }): (req: any) => {
43
+ response: {
44
+ statusCode: number;
45
+ };
46
+ } | undefined;
@@ -1,2 +1,2 @@
1
- declare function _default(): Function;
1
+ declare function _default(): (req: any) => object;
2
2
  export default _default;
@@ -6,12 +6,22 @@
6
6
  * @param {number} [options.windowMs=60000] - Window size in milliseconds (default: 1 minute)
7
7
  * @param {object} [options.store] - Pluggable store (must implement `hit(key, windowMs)`)
8
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
9
  */
12
10
  export default function rateLimit({ max, windowMs, store, keyGenerator }?: {
13
11
  max?: number | undefined;
14
12
  windowMs?: number | undefined;
15
13
  store?: object | undefined;
16
14
  keyGenerator?: Function | undefined;
17
- }): Function;
15
+ }): (req: any) => {
16
+ response: {
17
+ statusCode: number;
18
+ retryAfter: number | undefined;
19
+ headers?: undefined;
20
+ };
21
+ } | {
22
+ response: {
23
+ headers: string[][];
24
+ statusCode?: undefined;
25
+ retryAfter?: undefined;
26
+ };
27
+ };
@@ -6,5 +6,9 @@ declare function _default(options?: {
6
6
  referrerPolicy?: string | false | undefined;
7
7
  xXssProtection?: string | false | undefined;
8
8
  permissionsPolicy?: string | undefined;
9
- }): Function;
9
+ }): () => {
10
+ response: {
11
+ headers: [string, string][];
12
+ };
13
+ };
10
14
  export default _default;
@@ -4,5 +4,5 @@ declare function _default({ prettify, vary, etag, prefer, envelope }?: {
4
4
  etag?: boolean | undefined;
5
5
  prefer?: boolean | undefined;
6
6
  envelope?: boolean | Function | undefined;
7
- }): Function;
7
+ }): (req: any, res: any, responseAcc: any, domainAcc?: {}) => void;
8
8
  export default _default;
@@ -1,5 +1,5 @@
1
1
  declare function _default({ ms, statusCode }?: {
2
2
  ms?: number | undefined;
3
3
  statusCode?: number | undefined;
4
- }): Function;
4
+ }): (req: any, res: any, domainAcc: any, responseAcc: any) => void;
5
5
  export default _default;
@@ -1,2 +1,10 @@
1
- declare function _default(): Function;
1
+ declare function _default(): ({ url }?: {}) => {
2
+ query: any;
3
+ pathname: any;
4
+ search: undefined;
5
+ } | {
6
+ query: object;
7
+ pathname: any;
8
+ search: any;
9
+ };
2
10
  export default _default;
@@ -2,5 +2,13 @@ declare function _default(schemas?: {
2
2
  body?: object | undefined;
3
3
  query?: object | undefined;
4
4
  params?: object | undefined;
5
- }, options?: object): Function;
5
+ }, options?: {
6
+ formats?: boolean | object | string[] | undefined;
7
+ }): (req: any, res: any, acc: any) => {
8
+ response: {
9
+ statusCode: any;
10
+ detail: any;
11
+ details: any;
12
+ };
13
+ } | undefined;
6
14
  export default _default;
@@ -3,5 +3,10 @@ declare function _default({ types, languages, charsets, encodings }?: {
3
3
  languages?: string | string[] | undefined;
4
4
  charsets?: string | string[] | undefined;
5
5
  encodings?: string | string[] | undefined;
6
- }): Function;
6
+ }): (headers?: {}) => {
7
+ type: any;
8
+ language: any;
9
+ charset: any;
10
+ encoding: any;
11
+ };
7
12
  export default _default;
@@ -2,5 +2,5 @@ declare function _default(strategies?: Array<{
2
2
  type: string;
3
3
  attributes?: object;
4
4
  authorizer: Function;
5
- }>): Function;
5
+ }>): (authorization?: string) => Promise<any>;
6
6
  export default _default;
@@ -5,5 +5,29 @@ declare function _default({ origins, allowMethods, allowCredentials, allowHeader
5
5
  allowHeaders?: string | Function | RegExp | any[] | undefined;
6
6
  exposeHeaders?: string | string[] | undefined;
7
7
  maxAge?: number | undefined;
8
- }): Function;
8
+ }): ({ origin, method, requestMethod, requestHeaders }?: {}) => {
9
+ allowed: boolean;
10
+ info: {
11
+ type: string;
12
+ origin: any;
13
+ method?: undefined;
14
+ headers?: undefined;
15
+ };
16
+ } | {
17
+ allowed: boolean;
18
+ info: {
19
+ type: string;
20
+ origin: any;
21
+ method: any;
22
+ headers?: undefined;
23
+ };
24
+ } | {
25
+ allowed: boolean;
26
+ info: {
27
+ origin: any;
28
+ headers: any[];
29
+ type?: undefined;
30
+ method?: undefined;
31
+ };
32
+ };
9
33
  export default _default;
@@ -41,6 +41,5 @@
41
41
  * Wrap a Connect/Express-style middleware for use in an ergo pipeline.
42
42
  *
43
43
  * @param {function} middleware - Connect middleware `(req, res, next) => void`
44
- * @returns {function} - Ergo-compatible middleware `(req, res) => Promise<undefined>`
45
44
  */
46
- export default function fromConnect(middleware: Function): Function;
45
+ export default function fromConnect(middleware: Function): (req: any, res: any) => Promise<any>;
@@ -1,5 +1,5 @@
1
1
  declare function _default({ coerceTypes, ownProperties, ...ajvOptions }?: {
2
2
  coerceTypes?: string | boolean | undefined;
3
3
  ownProperties?: boolean | undefined;
4
- }, schema?: object): Function;
4
+ }, schema?: object): any;
5
5
  export default _default;
@@ -5,12 +5,17 @@
5
5
  * @param {object} [options] - Validator options
6
6
  * @param {boolean} [options.allErrors=true] - Report all errors instead of stopping at the first
7
7
  * @param {boolean} [options.coerceTypes=false] - Coerce input values to match schema types
8
+ * @param {boolean|Array<string>|object} [options.formats] - Format keyword support via
9
+ * `ajv-formats`. `undefined` or `true` enables all standard formats; `false` disables (AJV
10
+ * strict mode rejects unknown formats); an array enables selective formats
11
+ * (e.g. `['email', 'uri']`); an object is passed as the full plugin config
12
+ * (e.g. `{mode: 'fast'}`)
8
13
  * @param {object} [options.ajv] - Additional AJV constructor options
9
- * @returns {function} - `validateData(data)` — returns `data` on success, throws 422 on failure
10
14
  * @throws {Error} 422 with `details` array if schema validation fails
11
15
  */
12
16
  export default function createValidator(schema: object, options?: {
13
17
  allErrors?: boolean | undefined;
14
18
  coerceTypes?: boolean | undefined;
19
+ formats?: boolean | object | string[] | undefined;
15
20
  ajv?: object | undefined;
16
- }): Function;
21
+ }): (data: any) => any;
@@ -1,2 +1,2 @@
1
- declare function _default(fn: Function, fail: Function): Function;
1
+ declare function _default(fn: Function, fail: Function): (...args: any[]) => Promise<any>;
2
2
  export default _default;
@@ -26,15 +26,13 @@ export default composeWith;
26
26
  * If either is absent, a fresh one is created.
27
27
  *
28
28
  * @param {...(function|Array)} ops - Operation specs; each is a function or `[fn, setPath]`
29
- * @returns {function} - Async composed pipeline
30
29
  */
31
- declare function composeWith(...ops: (Function | any[])[]): Function;
30
+ declare function composeWith(...ops: (Function | any[])[]): (...args: any[]) => Promise<object>;
32
31
  declare namespace composeWith {
33
32
  /**
34
33
  * Concurrent variant of composeWith.
35
34
  *
36
35
  * @param {...(function|Array)} ops - Operation specs
37
- * @returns {function} - Async composed pipeline
38
36
  */
39
- function all(...ops: (Function | any[])[]): Function;
37
+ function all(...ops: (Function | any[])[]): (...args: any[]) => Promise<object>;
40
38
  }
@@ -45,20 +45,18 @@ export default compose;
45
45
  * Composes middleware functions into an async pipeline with result accumulation.
46
46
  *
47
47
  * @param {...function} fns - Middleware functions to compose
48
- * @returns {function} - Async composed pipeline
49
48
  */
50
- declare function compose(...fns: Function[]): Function;
49
+ declare function compose(...fns: Function[]): (...args: any[]) => Promise<any>;
51
50
  declare namespace compose {
52
- function all(...fns: any[]): Function;
51
+ function all(...fns: any[]): (...args: any[]) => Promise<any>;
53
52
  namespace all {
54
53
  /**
55
54
  * Creates a concurrent pipeline with configuration options.
56
55
  *
57
56
  * @param {object} options - Pipeline options
58
57
  * @param {...function} fns - Middleware functions to compose
59
- * @returns {function} - Async composed pipeline
60
58
  */
61
- function withOptions(options: object, ...fns: Function[]): Function;
59
+ function withOptions(options: object, ...fns: Function[]): (...args: any[]) => Promise<any>;
62
60
  }
63
61
  /**
64
62
  * Creates a sequential pipeline with configuration options.
@@ -67,11 +65,10 @@ declare namespace compose {
67
65
  * @param {function} [options.breakWhen] - Predicate `(acc) => boolean`; when truthy,
68
66
  * serial iteration stops after the current step's result is merged
69
67
  * @param {...function} fns - Middleware functions to compose
70
- * @returns {function} - Async composed pipeline
71
68
  */
72
69
  function withOptions(options: {
73
70
  breakWhen?: Function | undefined;
74
- }, ...fns: Function[]): Function;
71
+ }, ...fns: Function[]): (...args: any[]) => Promise<any>;
75
72
  }
76
73
  /**
77
74
  * Creates a null-prototype accumulator object.
package/utils/attempt.js CHANGED
@@ -23,7 +23,6 @@
23
23
  /**
24
24
  * @param {function} fn - Primary async function to execute
25
25
  * @param {function} fail - Error handler called with (...originalArgs, error)
26
- * @returns {function} - Wrapped async function with try/catch behavior
27
26
  */
28
27
  export default (fn, fail) => {
29
28
  return async (...args) => {
@@ -198,7 +198,6 @@ async function concurrent(descriptors, args, domainAcc, responseAcc) {
198
198
  * If either is absent, a fresh one is created.
199
199
  *
200
200
  * @param {...(function|Array)} ops - Operation specs; each is a function or `[fn, setPath]`
201
- * @returns {function} - Async composed pipeline
202
201
  */
203
202
  const composeWith = (...ops) => {
204
203
  const descriptors = ops.map(normalizeOp);
@@ -215,7 +214,6 @@ const composeWith = (...ops) => {
215
214
  * Concurrent variant of composeWith.
216
215
  *
217
216
  * @param {...(function|Array)} ops - Operation specs
218
- * @returns {function} - Async composed pipeline
219
217
  */
220
218
  composeWith.all = (...ops) => {
221
219
  const descriptors = ops.map(normalizeOp);
package/utils/compose.js CHANGED
@@ -44,7 +44,6 @@
44
44
  * Composes middleware functions into an async pipeline with result accumulation.
45
45
  *
46
46
  * @param {...function} fns - Middleware functions to compose
47
- * @returns {function} - Async composed pipeline
48
47
  */
49
48
  const compose = (...fns) => setup(serial, fns);
50
49
  compose.all = (...fns) => setup(concurrent, fns);
@@ -56,7 +55,6 @@ compose.all = (...fns) => setup(concurrent, fns);
56
55
  * @param {function} [options.breakWhen] - Predicate `(acc) => boolean`; when truthy,
57
56
  * serial iteration stops after the current step's result is merged
58
57
  * @param {...function} fns - Middleware functions to compose
59
- * @returns {function} - Async composed pipeline
60
58
  */
61
59
  compose.withOptions = (options, ...fns) => setup(serial, fns, options);
62
60
 
@@ -65,7 +63,6 @@ compose.withOptions = (options, ...fns) => setup(serial, fns, options);
65
63
  *
66
64
  * @param {object} options - Pipeline options
67
65
  * @param {...function} fns - Middleware functions to compose
68
- * @returns {function} - Async composed pipeline
69
66
  */
70
67
  compose.all.withOptions = (options, ...fns) => setup(concurrent, fns, options);
71
68
 
@@ -132,7 +129,6 @@ async function concurrent(fns, args, acc, _options) {
132
129
  * @param {function} processor - `serial` or `concurrent`
133
130
  * @param {function[]} fns - Middleware functions
134
131
  * @param {object} [options] - Pipeline options forwarded to the processor
135
- * @returns {function} - Async composed function `(...args) => Accumulator`
136
132
  */
137
133
  function setup(processor, fns, options = {}) {
138
134
  return async (...args) => {