@antelopejs/interface-api-util 0.1.3 → 0.3.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.
package/README.md CHANGED
@@ -1,18 +1,20 @@
1
- # @antelopejs/interface-api-util
1
+ # `@antelopejs/interface-api-util`
2
2
 
3
3
  <div align="center">
4
- <a href="https://www.npmjs.com/package/@antelopejs/interface-api-util"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-api-util?style=for-the-badge&labelColor=000000&color=000000"></a>
5
- <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-api-util?style=for-the-badge&labelColor=000000&color=000000"></a>
6
- <a href="https://discord.gg/sjK28QHrA7"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
7
- <a href="https://interfaces.antelopejs.com/docs/api-util"><img alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
4
+ <a href="https://www.npmjs.com/package/@antelopejs/interface-api-util"><img data-illustration alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-api-util?style=for-the-badge&labelColor=000000&color=000000"></a>
5
+ <a href="./LICENSE"><img data-illustration alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-api-util?style=for-the-badge&labelColor=000000&color=000000"></a>
6
+ <a href="https://discord.gg/g4pFt6eXqu"><img data-illustration alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
7
+ <a href="https://interfaces.antelopejs.com/docs/api-util"><img data-illustration alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
8
8
  </div>
9
9
 
10
10
  Request validation and rate limiting helpers for handlers written against [`@antelopejs/interface-api`](https://github.com/AntelopeJS/interface-api). Every failure becomes an `HTTPResult` error response.
11
11
 
12
+ Part of [AntelopeJS](https://github.com/AntelopeJS/antelopejs). Used by [AntelopeJS DMS](https://github.com/AntelopeJS/dms), the open-source back office for your PostgreSQL, MongoDB or RethinkDB database.
13
+
12
14
  ## What it offers
13
15
 
14
16
  - `assert(condition, code, message)` throws an `HTTPResult` when the condition is falsy and narrows its type when it holds.
15
- - `assertValidation(body, validator)` runs a synchronous validator, such as a Zod `parse`, and turns a thrown error into a `400` response.
17
+ - `assertValidation(body, validator)` runs a synchronous validator, such as a Zod `parse`, and turns a thrown error into an error response, `400` by default.
16
18
  - `@RateLimit(limit, windowMs)` caps calls to a handler per client in a fixed window and answers `429` beyond it.
17
19
 
18
20
  ## Implementations
@@ -69,14 +71,16 @@ Place `@RateLimit` directly below the route decorator. Placed above it, `@RateLi
69
71
 
70
72
  ## Compatibility
71
73
 
72
- Use with `@antelopejs/core` 1.x and the following peer dependencies:
74
+ The module declares these peer dependencies:
75
+
76
+ <!-- antelopejs-docs-compat:start -->
73
77
 
74
- <!-- docs-compat: ./package.json#peerDependencies -->
78
+ | Package | Supported | Tested with |
79
+ | ---------------------------- | ---------------- | ----------- |
80
+ | `@antelopejs/interface-api` | `>=0.3.0 <1.0.0` | `0.2.0` |
81
+ | `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` | `0.4.0` |
75
82
 
76
- | Package | Range |
77
- | ---------------------------- | ----------------- |
78
- | `@antelopejs/interface-api` | `>=0.0.15 <1.0.0` |
79
- | `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` |
83
+ <!-- antelopejs-docs-compat:end -->
80
84
 
81
85
  ## Contributing
82
86
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type HTTPBody } from "@antelopejs/interface-api";
1
2
  export * from "./rate-limit";
2
3
  /**
3
4
  * Assert a condition is truthy, throwing an HTTPResult error if false.
@@ -21,16 +22,24 @@ export * from "./rate-limit";
21
22
  * }
22
23
  * ```
23
24
  */
24
- export declare function assert<T>(condition: T, code: number, message: string): asserts condition;
25
+ export declare function assert(condition: unknown, code: number, message: string): asserts condition;
25
26
  /**
26
27
  * Executes a synchronous validator on a body, converting thrown errors to HTTPResult.
27
28
  * The validator should return a validated object on success and throw on failure.
28
29
  * Promises are returned unchanged; asynchronous rejections are not converted.
29
30
  *
30
31
  * The error thrown by the validator may be altered by passing an error handler.
31
- * The result of this error handler will be passed directly as the body of the HTTPResult.
32
+ * The result of this error handler will be passed directly as the body of the HTTPResult:
33
+ * a string is sent as is, any other `HTTPBody` as JSON, and `undefined` as an empty text body.
32
34
  * If no error handler is given, assertValidation will resort to calling the error's toString() method if possible.
33
35
  *
36
+ * The validator receives the body at the type of the argument you pass (`Input`, inferred from `body`;
37
+ * `unknown` when you give an explicit `T` only). A validator that accepts a wider type, such as
38
+ * `schema.parse`, always fits; one that accepts a narrower type than the body is rejected at compile time,
39
+ * except when the body is typed `any`, which fits every validator.
40
+ *
41
+ * @typeParam T Type of the validated result
42
+ * @typeParam Input Type of the body; the validator receives it at this type
34
43
  * @param body Input to validate
35
44
  * @param validator Validator function
36
45
  * @param errorFunc Error handler
@@ -47,4 +56,4 @@ export declare function assert<T>(condition: T, code: number, message: string):
47
56
  * const { name } = assertValidation(requestBody, schema.parse);
48
57
  * ```
49
58
  */
50
- export declare function assertValidation<T>(body: unknown, validator: (body: unknown) => T, errorFunc?: (err: unknown) => unknown, code?: number): T;
59
+ export declare function assertValidation<T, Input = unknown>(body: Input, validator: (body: Input) => T, errorFunc?: (err: unknown) => HTTPBody | undefined, code?: number): T;
package/dist/index.js CHANGED
@@ -51,9 +51,17 @@ function assert(condition, code, message) {
51
51
  * Promises are returned unchanged; asynchronous rejections are not converted.
52
52
  *
53
53
  * The error thrown by the validator may be altered by passing an error handler.
54
- * The result of this error handler will be passed directly as the body of the HTTPResult.
54
+ * The result of this error handler will be passed directly as the body of the HTTPResult:
55
+ * a string is sent as is, any other `HTTPBody` as JSON, and `undefined` as an empty text body.
55
56
  * If no error handler is given, assertValidation will resort to calling the error's toString() method if possible.
56
57
  *
58
+ * The validator receives the body at the type of the argument you pass (`Input`, inferred from `body`;
59
+ * `unknown` when you give an explicit `T` only). A validator that accepts a wider type, such as
60
+ * `schema.parse`, always fits; one that accepts a narrower type than the body is rejected at compile time,
61
+ * except when the body is typed `any`, which fits every validator.
62
+ *
63
+ * @typeParam T Type of the validated result
64
+ * @typeParam Input Type of the body; the validator receives it at this type
57
65
  * @param body Input to validate
58
66
  * @param validator Validator function
59
67
  * @param errorFunc Error handler
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA0BA,wBAQC;AA2BD,4CAYC;AAzED,6DAAuD;AAEvD,+CAA6B;AAE7B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,MAAM,CACpB,SAAY,EACZ,IAAY,EACZ,OAAe;IAEf,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,0BAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAgB,gBAAgB,CAC9B,IAAa,EACb,SAA+B,EAC/B,SAAqC,EACrC,IAAI,GAAG,GAAG;IAEV,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,0BAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA0BA,wBAQC;AAmCD,4CAYC;AAjFD,6DAAsE;AAEtE,+CAA6B;AAE7B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,MAAM,CACpB,SAAkB,EAClB,IAAY,EACZ,OAAe;IAEf,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,0BAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,SAAgB,gBAAgB,CAC9B,IAAW,EACX,SAA6B,EAC7B,SAAkD,EAClD,IAAI,GAAG,GAAG;IAEV,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,0BAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -132,7 +132,7 @@ function nextFreeIndex(meta, key) {
132
132
  * }
133
133
  * ```
134
134
  */
135
- exports.RateLimit = (0, decorators_1.MakeMethodDecorator)((target, key, _descriptor, limit, windowMs, options) => {
135
+ exports.RateLimit = (0, decorators_1.MakeMethodDecorator)((target, key, _descriptor, ...[limit, windowMs, options]) => {
136
136
  const resolvedOptions = options ?? {};
137
137
  const meta = getControllerMeta(target);
138
138
  assertGuardWiringOrder(meta, key);
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { HTTPResult } from "@antelopejs/interface-api";
2
+ export declare function captureHTTPResult(action: () => void): HTTPResult;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.captureHTTPResult = captureHTTPResult;
4
+ const interface_api_1 = require("@antelopejs/interface-api");
5
+ function captureHTTPResult(action) {
6
+ try {
7
+ action();
8
+ }
9
+ catch (error) {
10
+ if (error instanceof interface_api_1.HTTPResult) {
11
+ return error;
12
+ }
13
+ throw error;
14
+ }
15
+ throw new Error("Expected the action to throw");
16
+ }
17
+ //# sourceMappingURL=capture-http-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-http-result.js","sourceRoot":"","sources":["../../src/tests/capture-http-result.ts"],"names":[],"mappings":";;AAEA,8CAUC;AAZD,6DAAuD;AAEvD,SAAgB,iBAAiB,CAAC,MAAkB;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC;IACX,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,0BAAU,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC"}
@@ -4,93 +4,90 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const node_assert_1 = __importDefault(require("node:assert"));
7
- const interface_api_1 = require("@antelopejs/interface-api");
8
- const interface_api_util_1 = require("@antelopejs/interface-api-util");
7
+ const __1 = require("..");
8
+ const capture_http_result_1 = require("./capture-http-result");
9
+ class RawPayload {
10
+ name;
11
+ active;
12
+ constructor(name, active) {
13
+ this.name = name;
14
+ this.active = active;
15
+ }
16
+ }
17
+ const MALFORMED_BODY = { malformed: true };
9
18
  const BAD_REQUEST_CODE = 400;
10
19
  const UNPROCESSABLE_ENTITY_CODE = 422;
11
20
  const ASSERTION_ERROR_MESSAGE = "Missing payload";
12
21
  const VALIDATION_ERROR_MESSAGE = "Invalid payload";
13
22
  const VALIDATION_ERROR_CODE = "VALIDATION_ERROR";
14
- function toHTTPResult(error) {
15
- if (!(error instanceof interface_api_1.HTTPResult)) {
16
- throw new Error("Expected an HTTPResult error");
17
- }
18
- return error;
19
- }
20
- function isObjectRecord(value) {
21
- return typeof value === "object" && value !== null;
22
- }
23
- function parsePayload(value) {
24
- if (!isObjectRecord(value)) {
25
- throw new Error(VALIDATION_ERROR_MESSAGE);
26
- }
27
- const nameValue = value.name;
28
- const activeValue = value.active;
29
- if (typeof nameValue !== "string" || typeof activeValue !== "boolean") {
23
+ function parsePayload(body) {
24
+ if (!(body instanceof RawPayload)) {
30
25
  throw new Error(VALIDATION_ERROR_MESSAGE);
31
26
  }
32
27
  return {
33
- name: nameValue,
34
- active: activeValue,
28
+ name: body.name,
29
+ active: body.active,
35
30
  };
36
31
  }
32
+ function countBytes(body) {
33
+ return body.length;
34
+ }
37
35
  function mapValidationError() {
38
36
  return {
39
37
  code: VALIDATION_ERROR_CODE,
40
38
  detail: VALIDATION_ERROR_MESSAGE,
41
39
  };
42
40
  }
41
+ function mapIssueList() {
42
+ return [{ path: ["name"], message: "bad" }];
43
+ }
44
+ function refuseSilently() {
45
+ return undefined;
46
+ }
43
47
  describe("API Util Interface", () => {
44
48
  describe("assert", () => {
45
49
  it("returns when the condition is truthy", () => {
46
- node_assert_1.default.doesNotThrow(() => (0, interface_api_util_1.assert)(true, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE));
50
+ node_assert_1.default.doesNotThrow(() => (0, __1.assert)(true, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE));
47
51
  });
48
52
  it("throws HTTPResult when the condition is falsy", () => {
49
- let thrownError;
50
- try {
51
- (0, interface_api_util_1.assert)(false, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE);
52
- }
53
- catch (error) {
54
- thrownError = error;
55
- }
56
- const httpError = toHTTPResult(thrownError);
53
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assert)(false, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE));
57
54
  node_assert_1.default.equal(httpError.getStatus(), BAD_REQUEST_CODE);
58
55
  node_assert_1.default.equal(httpError.getBody(), ASSERTION_ERROR_MESSAGE);
59
56
  });
60
57
  });
61
58
  describe("assertValidation", () => {
62
59
  it("returns parsed payload when validation succeeds", () => {
63
- const payload = (0, interface_api_util_1.assertValidation)({
64
- name: "antelope",
65
- active: true,
66
- }, parsePayload);
60
+ const input = new RawPayload("antelope", true);
61
+ const payload = (0, __1.assertValidation)(input, parsePayload);
62
+ node_assert_1.default.notEqual(payload, input);
67
63
  node_assert_1.default.equal(payload.name, "antelope");
68
64
  node_assert_1.default.equal(payload.active, true);
69
65
  });
66
+ it("hands the validator a body of the caller's own type", () => {
67
+ const byteCount = (0, __1.assertValidation)(Buffer.from("antelope"), countBytes);
68
+ node_assert_1.default.equal(byteCount, "antelope".length);
69
+ });
70
70
  it("throws HTTPResult with default code and serialized error message", () => {
71
- let thrownError;
72
- try {
73
- (0, interface_api_util_1.assertValidation)({}, parsePayload);
74
- }
75
- catch (error) {
76
- thrownError = error;
77
- }
78
- const httpError = toHTTPResult(thrownError);
71
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)(MALFORMED_BODY, parsePayload));
79
72
  node_assert_1.default.equal(httpError.getStatus(), BAD_REQUEST_CODE);
80
73
  node_assert_1.default.equal(httpError.getBody(), `Error: ${VALIDATION_ERROR_MESSAGE}`);
81
74
  });
82
75
  it("applies custom error mapper and status code", () => {
83
- let thrownError;
84
- try {
85
- (0, interface_api_util_1.assertValidation)({}, parsePayload, mapValidationError, UNPROCESSABLE_ENTITY_CODE);
86
- }
87
- catch (error) {
88
- thrownError = error;
89
- }
90
- const httpError = toHTTPResult(thrownError);
76
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)(MALFORMED_BODY, parsePayload, mapValidationError, UNPROCESSABLE_ENTITY_CODE));
91
77
  node_assert_1.default.equal(httpError.getStatus(), UNPROCESSABLE_ENTITY_CODE);
92
78
  node_assert_1.default.equal(httpError.getBody(), JSON.stringify(mapValidationError()));
93
79
  });
80
+ it("answers a JSON array from the error handler as JSON text", () => {
81
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)(MALFORMED_BODY, parsePayload, mapIssueList));
82
+ node_assert_1.default.equal(httpError.getBody(), '[{"path":["name"],"message":"bad"}]');
83
+ node_assert_1.default.equal(httpError.getContentType(), "application/json");
84
+ });
85
+ it("answers an empty text body when the error handler returns undefined", () => {
86
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)(MALFORMED_BODY, parsePayload, refuseSilently));
87
+ node_assert_1.default.equal(httpError.getStatus(), BAD_REQUEST_CODE);
88
+ node_assert_1.default.equal(httpError.getBody(), "");
89
+ node_assert_1.default.equal(httpError.getContentType(), "text/plain");
90
+ });
94
91
  });
95
92
  });
96
93
  //# sourceMappingURL=index.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/tests/index.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/tests/index.test.ts"],"names":[],"mappings":"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"}
@@ -13,9 +13,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const node_assert_1 = __importDefault(require("node:assert"));
16
+ const node_http_1 = require("node:http");
17
+ const node_net_1 = require("node:net");
16
18
  const interface_core_1 = require("@antelopejs/interface-core");
17
19
  const interface_api_1 = require("@antelopejs/interface-api");
18
20
  const rate_limit_1 = require("../rate-limit");
21
+ const capture_http_result_1 = require("./capture-http-result");
19
22
  const TOO_MANY_REQUESTS = 429;
20
23
  const LIMIT = 2;
21
24
  const WINDOW_MS = 60_000;
@@ -23,26 +26,24 @@ const RETRY_AFTER_HEADER = "Retry-After";
23
26
  const RATELIMIT_LIMIT_HEADER = "X-RateLimit-Limit";
24
27
  const RATELIMIT_REMAINING_HEADER = "X-RateLimit-Remaining";
25
28
  const RATELIMIT_RESET_HEADER = "X-RateLimit-Reset";
26
- function toHTTPResult(error) {
27
- if (!(error instanceof interface_api_1.HTTPResult)) {
28
- throw new Error("Expected an HTTPResult error");
29
- }
30
- return error;
31
- }
32
29
  function makeContext(remoteAddress) {
30
+ const socket = new node_net_1.Socket();
31
+ Object.defineProperty(socket, "remoteAddress", { value: remoteAddress });
32
+ const rawRequest = new node_http_1.IncomingMessage(socket);
33
33
  return {
34
- rawRequest: { socket: { remoteAddress } },
34
+ rawRequest,
35
+ rawResponse: new node_http_1.ServerResponse(rawRequest),
36
+ url: new URL("http://localhost/"),
37
+ routeParameters: {},
35
38
  response: new interface_api_1.HTTPResult(),
36
39
  };
37
40
  }
38
- function captureThrow(action) {
39
- try {
40
- action();
41
+ function handlerDescriptor(controller) {
42
+ const descriptor = Object.getOwnPropertyDescriptor(controller.prototype, "handler");
43
+ if (!descriptor) {
44
+ throw new Error("Expected handler to be a declared method");
41
45
  }
42
- catch (error) {
43
- return error;
44
- }
45
- throw new Error("Expected the action to throw");
46
+ return descriptor;
46
47
  }
47
48
  function guardProvider(controller) {
48
49
  const meta = (0, interface_core_1.GetMetadata)(controller, interface_api_1.ControllerMeta);
@@ -52,7 +53,7 @@ function guardProvider(controller) {
52
53
  }
53
54
  return provider;
54
55
  }
55
- describe("RateLimit", () => {
56
+ function describeWindows() {
56
57
  describe("hitWindow", () => {
57
58
  it("allows up to the limit then denies within the window", () => {
58
59
  const store = new Map();
@@ -101,6 +102,8 @@ describe("RateLimit", () => {
101
102
  node_assert_1.default.equal(store.has("fresh"), true);
102
103
  });
103
104
  });
105
+ }
106
+ function describeKeyResolution() {
104
107
  describe("resolveKey", () => {
105
108
  it("uses the custom key function when provided", () => {
106
109
  const options = { key: () => "custom" };
@@ -117,6 +120,8 @@ describe("RateLimit", () => {
117
120
  node_assert_1.default.equal((0, rate_limit_1.resolveKey)(makeContext("1.2.3.4"), options), "1.2.3.4");
118
121
  });
119
122
  });
123
+ }
124
+ function describeDecorator() {
120
125
  describe("decorator", () => {
121
126
  it("registers a guard provider at the trailing parameter index", () => {
122
127
  class Indexed extends (0, interface_api_1.Controller)("rate-limit-index") {
@@ -124,9 +129,7 @@ describe("RateLimit", () => {
124
129
  }
125
130
  const proto = Indexed.prototype;
126
131
  (0, interface_api_1.SetParameterProvider)(proto, "handler", 0, () => "existing");
127
- (0, rate_limit_1.RateLimit)(LIMIT, WINDOW_MS)(proto, "handler", {
128
- value: proto.handler,
129
- });
132
+ (0, rate_limit_1.RateLimit)(LIMIT, WINDOW_MS)(proto, "handler", handlerDescriptor(Indexed));
130
133
  const meta = (0, interface_core_1.GetMetadata)(Indexed, interface_api_1.ControllerMeta);
131
134
  node_assert_1.default.ok(meta.computed_params.handler[1]?.provider);
132
135
  });
@@ -146,7 +149,7 @@ describe("RateLimit", () => {
146
149
  const context = makeContext("9.9.9.9");
147
150
  node_assert_1.default.equal(provider(context), undefined);
148
151
  node_assert_1.default.equal(provider(context), undefined);
149
- const error = toHTTPResult(captureThrow(() => provider(context)));
152
+ const error = (0, capture_http_result_1.captureHTTPResult)(() => provider(context));
150
153
  const headers = error.getHeaders();
151
154
  const retryAfter = Number(headers[RETRY_AFTER_HEADER]);
152
155
  node_assert_1.default.equal(error.getStatus(), TOO_MANY_REQUESTS);
@@ -193,10 +196,15 @@ describe("RateLimit", () => {
193
196
  handler() { }
194
197
  }
195
198
  const proto = Misordered.prototype;
196
- const descriptor = { value: proto.handler };
199
+ const descriptor = handlerDescriptor(Misordered);
197
200
  (0, interface_api_1.Get)()(proto, "handler", descriptor);
198
201
  node_assert_1.default.throws(() => (0, rate_limit_1.RateLimit)(LIMIT, WINDOW_MS)(proto, "handler", descriptor), /directly beneath/);
199
202
  });
200
203
  });
204
+ }
205
+ describe("RateLimit", () => {
206
+ describeWindows();
207
+ describeKeyResolution();
208
+ describeDecorator();
201
209
  });
202
210
  //# sourceMappingURL=rate-limit.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.test.js","sourceRoot":"","sources":["../../src/tests/rate-limit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limit.test.js","sourceRoot":"","sources":["../../src/tests/rate-limit.test.ts"],"names":[],"mappings":"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"}
@@ -1,58 +1,110 @@
1
- # Interface API Util Documentation
1
+ ---
2
+ title: Validate and throttle API requests
3
+ description: Reference for assert, assertValidation, and the RateLimit decorator, which turn failed checks and excess requests into HTTPResult error responses.
4
+ mode: reference
5
+ ---
2
6
 
3
7
  ## Overview
4
8
 
5
- Interface API Util provides utilities for validating API requests, rate limiting handlers, and returning structured HTTP error responses. The package exports `assert` for checking conditions, `assertValidation` for running input validators, and the `@RateLimit` decorator for throttling handlers. All of them surface failures as `HTTPResult` errors from `@antelopejs/interface-api`.
9
+ `@antelopejs/interface-api-util` holds three helpers for handlers written against `@antelopejs/interface-api`. `assert` checks a condition. `assertValidation` runs an input validator. The `@RateLimit` decorator limits how often a handler runs. Each one reports a failure by throwing an `HTTPResult` from `@antelopejs/interface-api`.
6
10
 
7
- ## Getting Started
11
+ ## Choose a guard
8
12
 
9
- Install the package in your project:
13
+ | Task | Use | Failure behavior |
14
+ | -------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- |
15
+ | Require a resource or permission | [`assert`](#assert) | A falsy condition throws the status and message you supply. |
16
+ | Parse an untrusted request body | [`assertValidation`](#assertvalidation) | An error thrown by a synchronous validator becomes an HTTP error, status 400 by default. |
17
+ | Limit repeated calls | [`RateLimit`](#ratelimit) | A request above the fixed-window limit receives 429 before the handler runs. |
18
+
19
+ Each helper covers one check. A rate limit does not authenticate a caller, and a TypeScript annotation on a body does not validate the incoming JSON.
20
+
21
+ ## Getting started
22
+
23
+ Install the package in your module:
10
24
 
11
25
  ```bash
12
- npm install @antelopejs/interface-api-util
26
+ pnpm add @antelopejs/interface-api-util
27
+ ```
28
+
29
+ The examples below use the helpers in an AntelopeJS API controller. `findUser`, `findPost`, `removePost`, and `saveUser` stand for your application's data access code. This package does not export them. The Zod examples also need `zod` in your project.
30
+
31
+ ## Reject a bad request
32
+
33
+ This endpoint needs a non-empty `name` query parameter. With the API module listening, `GET /greet?name=Ada` returns a greeting. `GET /greet` and `GET /greet?name=` return HTTP 400. The example needs no database and no validation library.
34
+
35
+ ```ts docs-check
36
+ import { Controller, Get, Parameter } from "@antelopejs/interface-api";
37
+ import { assert } from "@antelopejs/interface-api-util";
38
+
39
+ class GreetingController extends Controller("/greet") {
40
+ @Get("")
41
+ greet(@Parameter("name", "query") name: string | true | undefined) {
42
+ assert(
43
+ typeof name === "string" && name.trim().length > 0,
44
+ 400,
45
+ "Name is required",
46
+ );
47
+ return { message: `Hello, ${name}!` };
48
+ }
49
+ }
13
50
  ```
14
51
 
52
+ `assert` fits a condition you can write as an expression. `assertValidation` fits a schema parser. `RateLimit` bounds repeated requests. The sections below describe each one.
53
+
15
54
  ## `assert`
16
55
 
17
- The `assert` function checks whether a condition is truthy. If the condition is falsy, it throws an `HTTPResult` with the specified HTTP status code and error message.
56
+ `assert` checks whether a condition is truthy. If the condition is falsy, it throws an `HTTPResult` with the given status code and message.
18
57
 
19
58
  ```typescript
20
- function assert<T>(
21
- condition: T,
59
+ declare function assert(
60
+ condition: unknown,
22
61
  code: number,
23
62
  message: string,
24
63
  ): asserts condition;
25
64
  ```
26
65
 
27
- | Parameter | Type | Description |
28
- | ----------- | -------- | --------------------------------------------------- |
29
- | `condition` | `T` | The value to check (falsy values trigger the error) |
30
- | `code` | `number` | HTTP status code for the error response |
31
- | `message` | `string` | Error message included in the response body |
66
+ | Parameter | Type | Description |
67
+ | ----------- | --------- | ------------------------------------------------ |
68
+ | `condition` | `unknown` | The value to check. A falsy value throws. |
69
+ | `code` | `number` | HTTP status code of the error response |
70
+ | `message` | `string` | Body of the error response, sent as `text/plain` |
32
71
 
33
- The function uses TypeScript's `asserts` keyword, so the compiler narrows the type of `condition` to truthy after the call.
72
+ The return type is an `asserts condition` signature, so after the call the compiler narrows `condition` to its truthy type.
34
73
 
35
74
  ### Check that a resource exists
36
75
 
76
+ `findUser` stands for your own data access code.
77
+
37
78
  ```typescript
38
79
  import { assert } from "@antelopejs/interface-api-util";
39
80
  import { HTTPResult } from "@antelopejs/interface-api";
40
81
 
82
+ declare function findUser(
83
+ id: string,
84
+ ): Promise<{ id: string; name: string } | undefined>;
85
+
41
86
  async function getUser(id: string) {
42
87
  const user = await findUser(id);
43
88
  assert(user, 404, "User not found");
44
89
 
45
- // TypeScript knows `user` is defined here
90
+ // `user` is defined here
46
91
  return new HTTPResult(200, user);
47
92
  }
48
93
  ```
49
94
 
50
95
  ### Validate permissions
51
96
 
97
+ `findPost` and `removePost` stand for your own data access code.
98
+
52
99
  ```typescript
53
100
  import { assert } from "@antelopejs/interface-api-util";
54
101
  import { HTTPResult } from "@antelopejs/interface-api";
55
102
 
103
+ declare function findPost(
104
+ id: string,
105
+ ): Promise<{ id: string; authorId: string } | undefined>;
106
+ declare function removePost(id: string): Promise<void>;
107
+
56
108
  async function deletePost(postId: string, currentUserId: string) {
57
109
  const post = await findPost(postId);
58
110
  assert(post, 404, "Post not found");
@@ -69,29 +121,57 @@ async function deletePost(postId: string, currentUserId: string) {
69
121
 
70
122
  ## `assertValidation`
71
123
 
72
- The `assertValidation` function runs a synchronous validator on an input value. If the validator throws, `assertValidation` catches the error and throws an `HTTPResult` instead. It does not await promises or convert asynchronous rejections; use a synchronous validator such as Zod's `parse`, not `parseAsync`.
124
+ `assertValidation` runs a synchronous validator on an input value. If the validator throws, `assertValidation` catches the error and throws an `HTTPResult` instead. It does not await a promise. A validator that returns a promise gets the promise back unchanged, and a rejection of that promise is not converted. Use a synchronous validator such as Zod's `parse`, not `parseAsync`.
73
125
 
74
126
  ```typescript
75
- function assertValidation<T>(
76
- body: unknown,
77
- validator: (body: unknown) => T,
78
- errorFunc?: (err: unknown) => unknown,
127
+ import type { HTTPBody } from "@antelopejs/interface-api";
128
+
129
+ declare function assertValidation<T, Input = unknown>(
130
+ body: Input,
131
+ validator: (body: Input) => T,
132
+ errorFunc?: (err: unknown) => HTTPBody | undefined,
79
133
  code?: number,
80
134
  ): T;
81
135
  ```
82
136
 
83
- | Parameter | Type | Default | Description |
84
- | ----------- | --------------------------- | ------- | ------------------------------------------------------- |
85
- | `body` | `unknown` | | The input value to validate |
86
- | `validator` | `(body: unknown) => T` | | A function that returns the validated value or throws |
87
- | `errorFunc` | `(err: unknown) => unknown` | | Optional error transformer for customizing the response |
88
- | `code` | `number` | `400` | HTTP status code for the error response |
137
+ | Parameter | Type | Default | Description |
138
+ | ----------- | ----------------------------------------- | ------- | ------------------------------------------------------- |
139
+ | `body` | `Input` | | The input value to validate |
140
+ | `validator` | `(body: Input) => T` | | A function that returns the validated value or throws |
141
+ | `errorFunc` | `(err: unknown) => HTTPBody \| undefined` | | Maps the thrown error to the body of the error response |
142
+ | `code` | `number` | `400` | HTTP status code of the error response |
89
143
 
90
- The function returns the validated value on success. On failure, it throws an `HTTPResult` whose body is either the result of `errorFunc` or the stringified error.
144
+ TypeScript infers `Input` from `body`, with no constraint. When you write only `assertValidation<T>(...)`, `Input` is `unknown`. The validator receives the body at type `Input`, so a body typed `Buffer` reaches a `(body: Buffer) => T` validator without a cast. A validator that accepts a wider type, such as Zod's `parse(data: unknown)`, always fits. A validator that accepts a narrower type than the body does not compile. For example, a `(body: string) => T` validator does not accept a `string | Buffer` body. The exception is a body typed `any`, which fits every validator, so the compiler then does not check the validator's parameter type. When the validator's parameter has no annotation, a literal body such as `"lit"` gives it the widened type `string`.
91
145
 
92
- ### Validate with Zod
146
+ ```ts docs-check
147
+ import { Controller, Post, RawBody } from "@antelopejs/interface-api";
148
+ import { assertValidation } from "@antelopejs/interface-api-util";
93
149
 
94
- ```typescript
150
+ function parseJsonObject(body: Buffer): Record<string, string> {
151
+ const parsed: unknown = JSON.parse(body.toString());
152
+ if (typeof parsed !== "object" || parsed === null) {
153
+ throw new Error("Expected a JSON object");
154
+ }
155
+ return Object.fromEntries(
156
+ Object.entries(parsed).map(([key, value]) => [key, String(value)]),
157
+ );
158
+ }
159
+
160
+ class ImportController extends Controller("import") {
161
+ @Post("")
162
+ create(@RawBody() body: Buffer) {
163
+ return assertValidation(body, parseJsonObject);
164
+ }
165
+ }
166
+ ```
167
+
168
+ On success, the function returns the validated value. On failure, it throws an `HTTPResult` with status `code`. Without `errorFunc`, the body is `String(err)`. With `errorFunc`, the body is its return value. A string is sent as `text/plain`, any other `HTTPBody` as JSON, and `undefined` as an empty `text/plain` body.
169
+
170
+ ### Validate with `zod`
171
+
172
+ `zod` and `saveUser` belong to your project. This package depends on neither, so the two Zod examples are not compiled with the docs.
173
+
174
+ ```typescript fragment
95
175
  import { assertValidation } from "@antelopejs/interface-api-util";
96
176
  import * as z from "zod";
97
177
 
@@ -110,9 +190,9 @@ function createUser(requestBody: unknown) {
110
190
 
111
191
  ### Custom error formatting
112
192
 
113
- Pass an `errorFunc` to control the error response body.
193
+ Pass an `errorFunc` to set the body of the error response. This example also changes the status to 422.
114
194
 
115
- ```typescript
195
+ ```typescript fragment
116
196
  import { assertValidation } from "@antelopejs/interface-api-util";
117
197
  import * as z from "zod";
118
198
 
@@ -135,16 +215,23 @@ function validateAge(requestBody: unknown) {
135
215
 
136
216
  ## `RateLimit`
137
217
 
138
- The `@RateLimit` decorator caps how often a route handler can be called. It counts requests per client in a fixed time window (in-memory, per process). Successful responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (a UNIX epoch-seconds timestamp) so clients can self-pace. Once the count exceeds `limit` within `windowMs`, the handler instead throws an `HTTPResult` with status `429` (adding a `Retry-After` header) without running.
218
+ `@RateLimit` counts requests per key and per decorated handler, in a fixed time window held in process memory. A key's window starts with its first request. It is not aligned to the clock minute. Once the count exceeds `limit` within `windowMs`, the guard throws an `HTTPResult` with status `429` and a `Retry-After` header, and the handler does not run.
219
+
220
+ An allowed request adds `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` to `context.response`. A rejected request carries the same headers on the thrown response. `X-RateLimit-Reset` is the end of the window as a UNIX timestamp in seconds. `Retry-After` is the delay until then, in seconds. Both are rounded up. If a handler returns a new `HTTPResult` instead of `context.response`, whether the headers survive depends on the API module. Return the context response to keep them.
139
221
 
140
222
  ```typescript
141
- function RateLimit(
223
+ import type { MethodDecorator } from "@antelopejs/interface-core/decorators";
224
+ import type { RateLimitOptions } from "@antelopejs/interface-api-util";
225
+
226
+ declare const RateLimit: (
142
227
  limit: number,
143
228
  windowMs: number,
144
229
  options?: RateLimitOptions,
145
- ): MethodDecorator;
230
+ ) => MethodDecorator;
146
231
  ```
147
232
 
233
+ `MethodDecorator` is the type from `@antelopejs/interface-core/decorators`, not the global TypeScript type of the same name.
234
+
148
235
  | Parameter | Type | Description |
149
236
  | ---------- | ------------------ | ---------------------------------------------------- |
150
237
  | `limit` | `number` | Maximum number of requests allowed per window |
@@ -152,31 +239,46 @@ function RateLimit(
152
239
  | `options` | `RateLimitOptions` | Optional custom key function and `429` response body |
153
240
 
154
241
  ```typescript
242
+ import type { RequestContext } from "@antelopejs/interface-api";
243
+
155
244
  interface RateLimitOptions {
156
245
  key?: (context: RequestContext) => string | null | undefined;
157
246
  message?: string;
158
247
  }
159
248
  ```
160
249
 
161
- By default, requests are counted per client IP (`context.rawRequest.socket.remoteAddress`). Provide `options.key` to count by user id, API key, or a forwarded header instead. If the custom key function returns a falsy value (for example a missing route parameter), the limiter falls back to the client IP rather than collapsing every such request into one shared bucket.
250
+ | Option | Default | Description |
251
+ | --------- | ----------------------------------------- | --------------------------------------------- |
252
+ | `key` | `context.rawRequest.socket.remoteAddress` | Returns the key that a request counts against |
253
+ | `message` | `"Too many requests"` | Body of the `429` response |
254
+
255
+ By default, the guard counts requests per client IP (`context.rawRequest.socket.remoteAddress`). If the socket has no remote address, the key is the string `"unknown"`, which all such requests share. Provide `options.key` to count by user id, API key, or a forwarded header instead. If the custom key function returns a falsy value, such as a missing route parameter, the guard uses the client IP for that request, so those requests do not all count against one key.
162
256
 
163
- > **Ordering matters.** Place `@RateLimit` **directly beneath** the route decorator (`@Get`, `@Post`, …). The route decorator captures the handler's parameters when it is applied, so a `@RateLimit` above it would be ignored — this is detected at startup and throws, rather than silently leaving the route unprotected.
257
+ Place `@RateLimit` below the route decorator (`@Get`, `@Post`, and the others). Decorators apply from the bottom up, and the route decorator copies the handler's parameter list when it registers the route. A `@RateLimit` written above it would add its guard too late. `@RateLimit` detects this order and throws an `Error` when the class is defined, so the route never runs unprotected.
164
258
 
165
- ### Limit by client IP
259
+ ### Limit by client address
166
260
 
167
261
  ```typescript
168
262
  import { RateLimit } from "@antelopejs/interface-api-util";
169
- import { Controller, Get, HTTPResult } from "@antelopejs/interface-api";
170
-
171
- class UsersController extends Controller("users") {
172
- @Get()
263
+ import {
264
+ Controller,
265
+ Get,
266
+ Context,
267
+ RequestContext,
268
+ } from "@antelopejs/interface-api";
269
+
270
+ class UsersController extends Controller("/users") {
271
+ @Get("")
173
272
  @RateLimit(100, 60_000)
174
- list() {
175
- return new HTTPResult(200, { users: [] });
273
+ list(@Context() context: RequestContext) {
274
+ context.response.setBody({ users: [] });
275
+ return context.response;
176
276
  }
177
277
  }
178
278
  ```
179
279
 
280
+ The first 100 requests from one IP within its 60-second window pass, and request 101 receives 429. Each decorated handler has its own counter, so the same IP has a separate count on another handler.
281
+
180
282
  ### Limit by a custom key
181
283
 
182
284
  ```ts docs-check
@@ -195,4 +297,14 @@ class AuthController extends Controller("auth") {
195
297
  }
196
298
  ```
197
299
 
198
- Counts are kept in process memory, so they are not shared across instances and reset on restart. Elapsed windows are pruned periodically, so the store is bounded by the number of distinct keys seen within a single window rather than growing forever. For distributed limits, key by a stable identifier and enforce limits in a shared store at the edge. Behind a proxy, the socket address is the proxy's — supply `options.key` to read a forwarded header, but only trust the hop your proxy sets (a raw client-supplied `X-Forwarded-For` is spoofable and can be used to evade or to inflate the key cardinality).
300
+ ### Choose a deployment strategy
301
+
302
+ | Deployment | What the limiter counts | What to do |
303
+ | --------------------------- | ------------------------------------ | --------------------------------------------------------------------------- |
304
+ | One process, direct clients | Requests per socket IP by default | Use the default key when an IP is the intended identity. |
305
+ | Behind a reverse proxy | The proxy's socket IP by default | Supply a key derived from a trusted proxy hop or an authenticated identity. |
306
+ | Multiple instances | Independent counters in each process | Enforce a shared limit at the edge or in a shared store. |
307
+
308
+ Counters reset when the process restarts. At most once per window, on a request, the guard drops every window that has ended. The store therefore holds only keys whose window started within the last two windows.
309
+
310
+ A client can set `X-Forwarded-For` to any value. A key read from that header without a trusted proxy lets a client evade the limit or create many keys. A stable custom key does not share the in-memory counter across processes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/interface-api-util",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "AntelopeJS helpers for API handlers: request assertions, input validation and rate limiting",
5
5
  "keywords": [
6
6
  "antelopejs",
@@ -45,17 +45,26 @@
45
45
  "release": "pnpm run lint && pnpm run prepack && release-it",
46
46
  "test": "pnpm run build && ajs module test .",
47
47
  "format:check": "oxfmt --check .",
48
- "knip": "knip"
48
+ "knip": "knip",
49
+ "prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
50
+ "checks": "antelopejs-checks",
51
+ "size": "antelopejs-check-size",
52
+ "sync": "antelopejs-sync",
53
+ "sync:check": "antelopejs-sync --check",
54
+ "typecheck": "tsc --noEmit",
55
+ "suppressions": "antelopejs-suppressions",
56
+ "suppressions:update": "antelopejs-suppressions --update"
49
57
  },
50
58
  "devDependencies": {
51
59
  "@antelopejs/core": "^1.16.0",
52
- "@antelopejs/interface-api": "^0.0.15",
53
- "@antelopejs/interface-core": "^0.2.4",
54
- "@antelopejs/tooling-configs": "^0.0.9",
60
+ "@antelopejs/interface-api": "^0.2.0",
61
+ "@antelopejs/interface-core": "^0.4.0",
62
+ "@antelopejs/tooling-configs": "^0.2.18",
55
63
  "@types/mocha": "^10.0.10",
56
64
  "@types/node": "^22.19.15",
57
65
  "eslint-plugin-perfectionist": "^5.11.0",
58
66
  "knip": "^6.34.0",
67
+ "lefthook": "^2.2.2",
59
68
  "oxfmt": "^0.66.0",
60
69
  "oxlint": "1.81.0",
61
70
  "oxlint-tsgolint": "^7.0.2001",
@@ -65,14 +74,22 @@
65
74
  "typescript": "^5.9.3"
66
75
  },
67
76
  "peerDependencies": {
68
- "@antelopejs/interface-api": ">=0.0.15 <1.0.0",
77
+ "@antelopejs/interface-api": ">=0.3.0 <1.0.0",
69
78
  "@antelopejs/interface-core": ">=0.2.4 <1.0.0"
70
79
  },
71
80
  "packageManager": "pnpm@10.6.5",
81
+ "pnpm": {
82
+ "overrides": {
83
+ "@antelopejs/interface-core": "0.4.0"
84
+ }
85
+ },
72
86
  "antelopeJs": {
73
87
  "test": "src/antelope.test.ts",
74
88
  "skills": [
75
89
  "./skills"
76
- ]
90
+ ],
91
+ "docs": {
92
+ "standard": true
93
+ }
77
94
  }
78
95
  }
@@ -64,7 +64,7 @@ class MembersController extends Controller("members") {
64
64
  @Post()
65
65
  async create(@RawBody() body: Buffer) {
66
66
  const { fullName, email } = assertValidation(body, (b) =>
67
- createMemberSchema.parse(JSON.parse((b as Buffer).toString())),
67
+ createMemberSchema.parse(JSON.parse(b.toString())),
68
68
  );
69
69
  return new HTTPResult(201, await saveMember({ fullName, email }));
70
70
  }
@@ -73,9 +73,11 @@ class MembersController extends Controller("members") {
73
73
 
74
74
  - `assert(condition, code, message)` — throws `new HTTPResult(code, message)` when falsy; uses
75
75
  TypeScript `asserts condition`, so the compiler narrows after the call.
76
- - `assertValidation(body, validator, errorFunc?, code = 400)` — returns the validator's result;
77
- if the validator throws, rethrows an `HTTPResult` whose body is `errorFunc(err)` if provided,
78
- otherwise `String(err)`.
76
+ - `assertValidation<T, Input = unknown>(body, validator, errorFunc?, code = 400)` — returns the
77
+ validator's result; if the validator throws, rethrows an `HTTPResult` whose body is
78
+ `errorFunc(err)` (an `HTTPBody`, or `undefined` for an empty body) if provided, otherwise `String(err)`. `Input` is the type of `body`, and the
79
+ validator receives it at that type (a `Buffer` body needs no cast). A validator taking a wider
80
+ type (`schema.parse`) fits; one taking a narrower type than the body is a compile error, unless the body is typed `any`.
79
81
  - `RateLimit(limit, windowMs, options?)` — fixed-window counter per key; over-limit requests throw
80
82
  a 429 `HTTPResult` (with `Retry-After`) before the handler runs. Successful responses get
81
83
  `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (epoch seconds) headers.
@@ -0,0 +1,60 @@
1
+ {
2
+ "version": 1,
3
+ "cases": [
4
+ {
5
+ "id": "api-util-interface-validate-body",
6
+ "area": "framework",
7
+ "style": "clean",
8
+ "prompt": "In a POST handler I receive the raw request body as a Buffer. How do I validate it with a Zod schema and return a 400 when it is invalid?",
9
+ "expect": {
10
+ "skills": ["api-util-interface"],
11
+ "acceptable": ["api-interface", "ajs-consume-interface", "antelopejs"],
12
+ "mustMention": ["assertValidation"]
13
+ }
14
+ },
15
+ {
16
+ "id": "api-util-interface-not-found-casual",
17
+ "area": "framework",
18
+ "style": "casual",
19
+ "prompt": "how do i send a 404 from my controller when the record does not exist",
20
+ "expect": {
21
+ "skills": ["api-util-interface"],
22
+ "acceptable": ["api-interface", "ajs-consume-interface", "antelopejs"],
23
+ "mustMention": ["assert"]
24
+ }
25
+ },
26
+ {
27
+ "id": "api-util-interface-rate-limit",
28
+ "area": "framework",
29
+ "style": "clean",
30
+ "prompt": "Limit my login route to 5 attempts per minute per client and answer 429 beyond that.",
31
+ "expect": {
32
+ "skills": ["api-util-interface"],
33
+ "acceptable": ["api-interface", "ajs-consume-interface", "antelopejs"],
34
+ "mustMention": ["RateLimit"]
35
+ }
36
+ },
37
+ {
38
+ "id": "api-util-interface-rate-limit-ignored-casual",
39
+ "area": "framework",
40
+ "style": "casual",
41
+ "prompt": "my rate limit decorator does nothing and startup throws an error about ordering",
42
+ "expect": {
43
+ "skills": ["api-util-interface"],
44
+ "acceptable": ["api-interface", "antelopejs"],
45
+ "mustMention": ["RateLimit", "beneath"]
46
+ }
47
+ },
48
+ {
49
+ "id": "api-util-interface-controller-neighbour",
50
+ "area": "framework",
51
+ "style": "clean",
52
+ "prompt": "Add a GET endpoint with a prefix handler that runs before every route in my controller.",
53
+ "expect": {
54
+ "skills": ["api-interface"],
55
+ "acceptable": ["antelopejs", "ajs-consume-interface"],
56
+ "avoid": ["api-util-interface"]
57
+ }
58
+ }
59
+ ]
60
+ }