@antelopejs/interface-api-util 0.1.2 → 0.2.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,26 +1,93 @@
1
- # 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 src="https://img.shields.io/npm/v/@antelopejs/interface-api-util?style=for-the-badge&labelColor=000000&color=000000" alt="npm"></a>
5
- <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=for-the-badge&labelColor=000000"></a>
6
- <a href="https://discord.gg/sjK28QHrA7"><img src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000" alt="Discord"></a>
7
- <a href="./docs/1.introduction.md"><img src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000" alt="Documentation"></a>
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>
8
8
  </div>
9
9
 
10
- Utility helpers for API request validation, rate limiting, and error handling in AntelopeJS. This package provides the `assert` and `assertValidation` helpers that throw structured HTTP error responses when conditions fail or input validation does not pass, plus the `@RateLimit` decorator that caps how often a handler can be called.
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
- ## Installation
12
+ ## What it offers
13
+
14
+ - `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.
16
+ - `@RateLimit(limit, windowMs)` caps calls to a handler per client in a fixed window and answers `429` beyond it.
17
+
18
+ ## Implementations
19
+
20
+ The helpers run in your module. They need the API interface served by a module:
21
+
22
+ | Module | Notes |
23
+ | ------------------------------------------------------ | --------------------------------------------------------------------------- |
24
+ | [`@antelopejs/api`](https://github.com/AntelopeJS/api) | Implements `@antelopejs/interface-api` and `@antelopejs/interface-api-util` |
25
+
26
+ ## Install
27
+
28
+ Add the package to the module that declares routes:
13
29
 
14
30
  ```bash
15
- npm install @antelopejs/interface-api-util
31
+ pnpm add @antelopejs/interface-api-util
32
+ ```
33
+
34
+ Then add an implementation of the API interface to the project:
35
+
36
+ ```bash
37
+ ajs project modules add @antelopejs/api
38
+ ```
39
+
40
+ ## Quick start
41
+
42
+ ```ts
43
+ import {
44
+ Controller,
45
+ Get,
46
+ HTTPResult,
47
+ Parameter,
48
+ } from "@antelopejs/interface-api";
49
+ import { RateLimit, assert } from "@antelopejs/interface-api-util";
50
+
51
+ const BOOKS = new Map([["1", { title: "The Glass Meridian" }]]);
52
+
53
+ export class BookController extends Controller("/books") {
54
+ @Get(":id")
55
+ @RateLimit(100, 60_000)
56
+ async getBook(@Parameter("id") id: string) {
57
+ const book = BOOKS.get(id);
58
+ assert(book, 404, "Book not found");
59
+ return new HTTPResult(200, book);
60
+ }
61
+ }
16
62
  ```
17
63
 
64
+ Place `@RateLimit` directly below the route decorator. Placed above it, `@RateLimit` throws when the class is defined.
65
+
18
66
  ## Documentation
19
67
 
20
- Detailed documentation is available in the `docs` directory:
68
+ - [Assertions, validation and rate limiting](./docs/1.introduction.md)
69
+
70
+ ## Compatibility
71
+
72
+ Use with `@antelopejs/core` 1.x and the following peer dependencies:
21
73
 
22
- - [Introduction](./docs/1.introduction.md) - Overview and basic concepts
74
+ <!-- docs-compat: ./package.json#peerDependencies -->
75
+
76
+ | Package | Range |
77
+ | ---------------------------- | ----------------- |
78
+ | `@antelopejs/interface-api` | `>=0.0.15 <1.0.0` |
79
+ | `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` |
80
+
81
+ ## Contributing
82
+
83
+ Read [CONTRIBUTING.md](./CONTRIBUTING.md) before you open a pull request. The local checks are:
84
+
85
+ ```bash
86
+ pnpm install
87
+ pnpm lint
88
+ pnpm test
89
+ ```
23
90
 
24
91
  ## License
25
92
 
26
- This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
93
+ [Apache-2.0](./LICENSE)
@@ -9,10 +9,11 @@ exports.default = (0, config_1.defineConfig)({
9
9
  source: {
10
10
  type: "package",
11
11
  package: "@antelopejs/api",
12
- version: "1.0.0",
12
+ version: "1.3.0",
13
13
  },
14
14
  config: {
15
15
  servers: [{ protocol: "http", host: "127.0.0.1", port: 5010 }],
16
+ publicBaseUrl: "http://127.0.0.1:5010",
16
17
  },
17
18
  },
18
19
  },
@@ -1 +1 @@
1
- {"version":3,"file":"antelope.test.js","sourceRoot":"","sources":["../src/antelope.test.ts"],"names":[],"mappings":";;AAAA,8DAAiE;AAEjE,kBAAe,IAAA,qBAAY,EAAC;IAC1B,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,iBAAiB;IAC9B,OAAO,EAAE;QACP,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,OAAO;aACjB;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;aAC/D;SACF;KACF;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,YAAY;KACrB;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"antelope.test.js","sourceRoot":"","sources":["../src/antelope.test.ts"],"names":[],"mappings":";;AAAA,8DAAiE;AAEjE,kBAAe,IAAA,qBAAY,EAAC;IAC1B,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,iBAAiB;IAC9B,OAAO,EAAE;QACP,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,OAAO;aACjB;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC9D,aAAa,EAAE,uBAAuB;aACvC;SACF;KACF;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,YAAY;KACrB;CACF,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -23,13 +23,21 @@ export * from "./rate-limit";
23
23
  */
24
24
  export declare function assert<T>(condition: T, code: number, message: string): asserts condition;
25
25
  /**
26
- * Executes the given validator function on a body, throwing an HTTPResult error if it fails.
26
+ * Executes a synchronous validator on a body, converting thrown errors to HTTPResult.
27
27
  * The validator should return a validated object on success and throw on failure.
28
+ * Promises are returned unchanged; asynchronous rejections are not converted.
28
29
  *
29
30
  * The error thrown by the validator may be altered by passing an error handler.
30
31
  * The result of this error handler will be passed directly as the body of the HTTPResult.
31
32
  * If no error handler is given, assertValidation will resort to calling the error's toString() method if possible.
32
33
  *
34
+ * The validator receives the body at the type of the argument you pass (`Input`, inferred from `body`;
35
+ * `unknown` when you give an explicit `T` only). A validator that accepts a wider type, such as
36
+ * `schema.parse`, always fits; one that accepts a narrower type than the body is rejected at compile time,
37
+ * except when the body is typed `any`, which fits every validator.
38
+ *
39
+ * @typeParam T Type of the validated result
40
+ * @typeParam Input Type of the body; the validator receives it at this type
33
41
  * @param body Input to validate
34
42
  * @param validator Validator function
35
43
  * @param errorFunc Error handler
@@ -46,4 +54,4 @@ export declare function assert<T>(condition: T, code: number, message: string):
46
54
  * const { name } = assertValidation(requestBody, schema.parse);
47
55
  * ```
48
56
  */
49
- export declare function assertValidation<T>(body: unknown, validator: (body: unknown) => T, errorFunc?: (err: unknown) => unknown, code?: number): T;
57
+ export declare function assertValidation<T, Input = unknown>(body: Input, validator: (body: Input) => T, errorFunc?: (err: unknown) => unknown, code?: number): T;
package/dist/index.js CHANGED
@@ -46,13 +46,21 @@ function assert(condition, code, message) {
46
46
  }
47
47
  }
48
48
  /**
49
- * Executes the given validator function on a body, throwing an HTTPResult error if it fails.
49
+ * Executes a synchronous validator on a body, converting thrown errors to HTTPResult.
50
50
  * The validator should return a validated object on success and throw on failure.
51
+ * Promises are returned unchanged; asynchronous rejections are not converted.
51
52
  *
52
53
  * The error thrown by the validator may be altered by passing an error handler.
53
54
  * The result of this error handler will be passed directly as the body of the HTTPResult.
54
55
  * If no error handler is given, assertValidation will resort to calling the error's toString() method if possible.
55
56
  *
57
+ * The validator receives the body at the type of the argument you pass (`Input`, inferred from `body`;
58
+ * `unknown` when you give an explicit `T` only). A validator that accepts a wider type, such as
59
+ * `schema.parse`, always fits; one that accepts a narrower type than the body is rejected at compile time,
60
+ * except when the body is typed `any`, which fits every validator.
61
+ *
62
+ * @typeParam T Type of the validated result
63
+ * @typeParam Input Type of the body; the validator receives it at this type
56
64
  * @param body Input to validate
57
65
  * @param validator Validator function
58
66
  * @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;AA0BD,4CAYC;AAxED,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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;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;AAkCD,4CAYC;AAhFD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,gBAAgB,CAC9B,IAAW,EACX,SAA6B,EAC7B,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"}
@@ -4,9 +4,9 @@ exports.RateLimit = void 0;
4
4
  exports.hitWindow = hitWindow;
5
5
  exports.pruneExpired = pruneExpired;
6
6
  exports.resolveKey = resolveKey;
7
- const interface_api_1 = require("@antelopejs/interface-api");
8
7
  const interface_core_1 = require("@antelopejs/interface-core");
9
8
  const decorators_1 = require("@antelopejs/interface-core/decorators");
9
+ const interface_api_1 = require("@antelopejs/interface-api");
10
10
  const TOO_MANY_REQUESTS = 429;
11
11
  const DEFAULT_MESSAGE = "Too many requests";
12
12
  const UNKNOWN_KEY = "unknown";
@@ -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,36 +4,33 @@ 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
+ }
9
17
  const BAD_REQUEST_CODE = 400;
10
18
  const UNPROCESSABLE_ENTITY_CODE = 422;
11
19
  const ASSERTION_ERROR_MESSAGE = "Missing payload";
12
20
  const VALIDATION_ERROR_MESSAGE = "Invalid payload";
13
21
  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") {
22
+ function parsePayload(body) {
23
+ if (!(body instanceof RawPayload)) {
30
24
  throw new Error(VALIDATION_ERROR_MESSAGE);
31
25
  }
32
26
  return {
33
- name: nameValue,
34
- active: activeValue,
27
+ name: body.name,
28
+ active: body.active,
35
29
  };
36
30
  }
31
+ function countBytes(body) {
32
+ return body.length;
33
+ }
37
34
  function mapValidationError() {
38
35
  return {
39
36
  code: VALIDATION_ERROR_CODE,
@@ -43,51 +40,33 @@ function mapValidationError() {
43
40
  describe("API Util Interface", () => {
44
41
  describe("assert", () => {
45
42
  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));
43
+ node_assert_1.default.doesNotThrow(() => (0, __1.assert)(true, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE));
47
44
  });
48
45
  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);
46
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assert)(false, BAD_REQUEST_CODE, ASSERTION_ERROR_MESSAGE));
57
47
  node_assert_1.default.equal(httpError.getStatus(), BAD_REQUEST_CODE);
58
48
  node_assert_1.default.equal(httpError.getBody(), ASSERTION_ERROR_MESSAGE);
59
49
  });
60
50
  });
61
51
  describe("assertValidation", () => {
62
52
  it("returns parsed payload when validation succeeds", () => {
63
- const payload = (0, interface_api_util_1.assertValidation)({
64
- name: "antelope",
65
- active: true,
66
- }, parsePayload);
53
+ const input = new RawPayload("antelope", true);
54
+ const payload = (0, __1.assertValidation)(input, parsePayload);
55
+ node_assert_1.default.notEqual(payload, input);
67
56
  node_assert_1.default.equal(payload.name, "antelope");
68
57
  node_assert_1.default.equal(payload.active, true);
69
58
  });
59
+ it("hands the validator a body of the caller's own type", () => {
60
+ const byteCount = (0, __1.assertValidation)(Buffer.from("antelope"), countBytes);
61
+ node_assert_1.default.equal(byteCount, "antelope".length);
62
+ });
70
63
  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);
64
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)({}, parsePayload));
79
65
  node_assert_1.default.equal(httpError.getStatus(), BAD_REQUEST_CODE);
80
66
  node_assert_1.default.equal(httpError.getBody(), `Error: ${VALIDATION_ERROR_MESSAGE}`);
81
67
  });
82
68
  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);
69
+ const httpError = (0, capture_http_result_1.captureHTTPResult)(() => (0, __1.assertValidation)({}, parsePayload, mapValidationError, UNPROCESSABLE_ENTITY_CODE));
91
70
  node_assert_1.default.equal(httpError.getStatus(), UNPROCESSABLE_ENTITY_CODE);
92
71
  node_assert_1.default.equal(httpError.getBody(), JSON.stringify(mapValidationError()));
93
72
  });
@@ -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 interface_api_1 = require("@antelopejs/interface-api");
16
+ const node_http_1 = require("node:http");
17
+ const node_net_1 = require("node:net");
17
18
  const interface_core_1 = require("@antelopejs/interface-core");
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
- }
42
- catch (error) {
43
- return error;
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");
44
45
  }
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,30 +1,69 @@
1
- # Interface API Util Documentation
1
+ # Validate and throttle API requests
2
2
 
3
3
  ## Overview
4
4
 
5
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`.
6
6
 
7
+ ## Choose a guard
8
+
9
+ | Task | Use | Failure behavior |
10
+ | -------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------- |
11
+ | Require a resource or permission | [`assert`](#assert) | A falsy condition throws the status and message you supply. |
12
+ | Parse an untrusted request body | [`assertValidation`](#assertvalidation) | A synchronous validation error becomes an HTTP error; the default status is 400. |
13
+ | Limit repeated calls | [`RateLimit`](#ratelimit) | Requests above the fixed-window limit receive 429 before the handler runs. |
14
+
15
+ These guards complement one another. Throttling does not authenticate a caller, and a TypeScript body annotation does not validate incoming JSON.
16
+
7
17
  ## Getting Started
8
18
 
9
19
  Install the package in your project:
10
20
 
11
21
  ```bash
12
- npm install @antelopejs/interface-api-util
22
+ pnpm add @antelopejs/interface-api-util
23
+ ```
24
+
25
+ Use the utilities in an existing AntelopeJS API controller. The `findUser`, `findPost`, `removePost`, and `saveUser` functions below stand for your application's persistence functions; they are not exports of this package. The Zod examples also require `zod` in your project.
26
+
27
+ ## Reject a bad request
28
+
29
+ This endpoint accepts a non-empty `name` query parameter. With the API provider listening, `GET /greet?name=Ada` returns a greeting; `GET /greet` or `GET /greet?name=` returns HTTP 400. No database or validation library is needed for this example.
30
+
31
+ ```ts docs-check
32
+ import { Controller, Get, Parameter } from "@antelopejs/interface-api";
33
+ import { assert } from "@antelopejs/interface-api-util";
34
+
35
+ class GreetingController extends Controller("/greet") {
36
+ @Get("")
37
+ greet(@Parameter("name", "query") name: string | true | undefined) {
38
+ assert(
39
+ typeof name === "string" && name.trim().length > 0,
40
+ 400,
41
+ "Name is required",
42
+ );
43
+ return { message: `Hello, ${name}!` };
44
+ }
45
+ }
13
46
  ```
14
47
 
48
+ Use `assert` for a condition you can state directly, `assertValidation` for a schema parser, and `RateLimit` to bound repeated requests. The reference below covers each independently.
49
+
15
50
  ## `assert`
16
51
 
17
52
  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.
18
53
 
19
54
  ```typescript
20
- function assert<T>(condition: T, code: number, message: string): asserts condition;
55
+ function assert<T>(
56
+ condition: T,
57
+ code: number,
58
+ message: string,
59
+ ): asserts condition;
21
60
  ```
22
61
 
23
- | Parameter | Type | Description |
24
- | ----------- | -------- | ---------------------------------------------------- |
25
- | `condition` | `T` | The value to check (falsy values trigger the error) |
26
- | `code` | `number` | HTTP status code for the error response |
27
- | `message` | `string` | Error message included in the response body |
62
+ | Parameter | Type | Description |
63
+ | ----------- | -------- | --------------------------------------------------- |
64
+ | `condition` | `T` | The value to check (falsy values trigger the error) |
65
+ | `code` | `number` | HTTP status code for the error response |
66
+ | `message` | `string` | Error message included in the response body |
28
67
 
29
68
  The function uses TypeScript's `asserts` keyword, so the compiler narrows the type of `condition` to truthy after the call.
30
69
 
@@ -52,7 +91,11 @@ import { HTTPResult } from "@antelopejs/interface-api";
52
91
  async function deletePost(postId: string, currentUserId: string) {
53
92
  const post = await findPost(postId);
54
93
  assert(post, 404, "Post not found");
55
- assert(post.authorId === currentUserId, 403, "Not authorized to delete this post");
94
+ assert(
95
+ post.authorId === currentUserId,
96
+ 403,
97
+ "Not authorized to delete this post",
98
+ );
56
99
 
57
100
  await removePost(postId);
58
101
  return new HTTPResult(200, { deleted: true });
@@ -61,23 +104,47 @@ async function deletePost(postId: string, currentUserId: string) {
61
104
 
62
105
  ## `assertValidation`
63
106
 
64
- The `assertValidation` function runs a validator on an input value. If the validator throws, `assertValidation` catches the error and throws an `HTTPResult` instead.
107
+ 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`.
65
108
 
66
109
  ```typescript
67
- function assertValidation<T>(
68
- body: unknown,
69
- validator: (body: unknown) => T,
110
+ function assertValidation<T, Input = unknown>(
111
+ body: Input,
112
+ validator: (body: Input) => T,
70
113
  errorFunc?: (err: unknown) => unknown,
71
114
  code?: number,
72
115
  ): T;
73
116
  ```
74
117
 
75
- | Parameter | Type | Default | Description |
76
- | ----------- | ----------------------------- | ------- | ------------------------------------------------------- |
77
- | `body` | `unknown` | | The input value to validate |
78
- | `validator` | `(body: unknown) => T` | | A function that returns the validated value or throws |
79
- | `errorFunc` | `(err: unknown) => unknown` | | Optional error transformer for customizing the response |
80
- | `code` | `number` | `400` | HTTP status code for the error response |
118
+ | Parameter | Type | Default | Description |
119
+ | ----------- | --------------------------- | ------- | ------------------------------------------------------- |
120
+ | `body` | `Input` | | The input value to validate |
121
+ | `validator` | `(body: Input) => T` | | A function that returns the validated value or throws |
122
+ | `errorFunc` | `(err: unknown) => unknown` | | Optional error transformer for customizing the response |
123
+ | `code` | `number` | `400` | HTTP status code for the error response |
124
+
125
+ `Input` is inferred from `body` and has no constraint; it defaults to `unknown` when you write only `assertValidation<T>(...)`. The validator receives the body at that type, 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: a `string | Buffer` body is rejected by a `(body: string) => T` validator. The exception is a body typed `any`, which fits every validator, so the validator's declared type is then unchecked. When the validator's parameter is unannotated, a literal body such as `"lit"` gives it the widened type `string`.
126
+
127
+ ```ts docs-check
128
+ import { Controller, Post, RawBody } from "@antelopejs/interface-api";
129
+ import { assertValidation } from "@antelopejs/interface-api-util";
130
+
131
+ function parseJsonObject(body: Buffer): Record<string, string> {
132
+ const parsed: unknown = JSON.parse(body.toString());
133
+ if (typeof parsed !== "object" || parsed === null) {
134
+ throw new Error("Expected a JSON object");
135
+ }
136
+ return Object.fromEntries(
137
+ Object.entries(parsed).map(([key, value]) => [key, String(value)]),
138
+ );
139
+ }
140
+
141
+ class ImportController extends Controller("import") {
142
+ @Post("")
143
+ create(@RawBody() body: Buffer) {
144
+ return assertValidation(body, parseJsonObject);
145
+ }
146
+ }
147
+ ```
81
148
 
82
149
  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.
83
150
 
@@ -116,7 +183,7 @@ function validateAge(requestBody: unknown) {
116
183
  schema.parse,
117
184
  (err) => {
118
185
  if (err instanceof z.ZodError) {
119
- return { errors: err.errors.map((e) => e.message) };
186
+ return { errors: err.issues.map((e) => e.message) };
120
187
  }
121
188
  return { errors: [String(err)] };
122
189
  },
@@ -127,7 +194,9 @@ function validateAge(requestBody: unknown) {
127
194
 
128
195
  ## `RateLimit`
129
196
 
130
- 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.
197
+ `@RateLimit` counts requests per key, 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 before the handler runs.
198
+
199
+ Allowed requests add `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` to `context.response`; rejected requests carry them on the thrown response. `Reset` is a UNIX timestamp in seconds, while `Retry-After` is a delay in seconds. If a handler replaces the response with a new `HTTPResult`, header preservation depends on the API provider; retain the context response when you need those headers.
131
200
 
132
201
  ```typescript
133
202
  function RateLimit(
@@ -137,11 +206,11 @@ function RateLimit(
137
206
  ): MethodDecorator;
138
207
  ```
139
208
 
140
- | Parameter | Type | Description |
141
- | ---------- | ------------------ | -------------------------------------------------------- |
142
- | `limit` | `number` | Maximum number of requests allowed per window |
143
- | `windowMs` | `number` | Window duration in milliseconds |
144
- | `options` | `RateLimitOptions` | Optional custom key function and `429` response body |
209
+ | Parameter | Type | Description |
210
+ | ---------- | ------------------ | ---------------------------------------------------- |
211
+ | `limit` | `number` | Maximum number of requests allowed per window |
212
+ | `windowMs` | `number` | Window duration in milliseconds |
213
+ | `options` | `RateLimitOptions` | Optional custom key function and `429` response body |
145
214
 
146
215
  ```typescript
147
216
  interface RateLimitOptions {
@@ -158,25 +227,33 @@ By default, requests are counted per client IP (`context.rawRequest.socket.remot
158
227
 
159
228
  ```typescript
160
229
  import { RateLimit } from "@antelopejs/interface-api-util";
161
- import { Controller, Get, HTTPResult } from "@antelopejs/interface-api";
162
-
163
- class UsersController extends Controller("users") {
164
- @Get()
230
+ import {
231
+ Controller,
232
+ Get,
233
+ Context,
234
+ RequestContext,
235
+ } from "@antelopejs/interface-api";
236
+
237
+ class UsersController extends Controller("/users") {
238
+ @Get("")
165
239
  @RateLimit(100, 60_000)
166
- list() {
167
- return new HTTPResult(200, { users: [] });
240
+ list(@Context() context: RequestContext) {
241
+ context.response.setBody({ users: [] });
242
+ return context.response;
168
243
  }
169
244
  }
170
245
  ```
171
246
 
247
+ Requests 1–100 from one IP are allowed within its 60-second window; request 101 receives 429. Another decorated handler has a separate counter, even for the same IP.
248
+
172
249
  ### Limit by a custom key
173
250
 
174
- ```typescript
251
+ ```ts docs-check
175
252
  import { RateLimit } from "@antelopejs/interface-api-util";
176
253
  import { Controller, Post, HTTPResult } from "@antelopejs/interface-api";
177
254
 
178
255
  class AuthController extends Controller("auth") {
179
- @Post("login")
256
+ @Post(":tenant/login")
180
257
  @RateLimit(5, 60_000, {
181
258
  key: (ctx) => ctx.routeParameters.tenant,
182
259
  message: "Too many login attempts, slow down",
@@ -187,4 +264,14 @@ class AuthController extends Controller("auth") {
187
264
  }
188
265
  ```
189
266
 
190
- 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).
267
+ ### Choose a deployment strategy
268
+
269
+ | Deployment | What the limiter counts | What to do |
270
+ | --------------------------- | ------------------------------------ | --------------------------------------------------------------------------- |
271
+ | One process, direct clients | Requests per socket IP by default | Use the default key when an IP is the intended identity. |
272
+ | Behind a reverse proxy | The proxy's socket IP by default | Supply a key derived from a trusted proxy hop or an authenticated identity. |
273
+ | Multiple instances | Independent counters in each process | Enforce a shared limit at the edge or in a shared store. |
274
+
275
+ Counters reset on process restart. Elapsed windows are pruned periodically, bounding the store by distinct keys seen within an active window rather than retaining keys forever.
276
+
277
+ Never trust a raw client-supplied `X-Forwarded-For` as identity: clients can spoof it to evade limits or create many keys. A stable custom key does not make the in-memory counter distributed.
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "rootDir": "..",
7
+ "composite": false,
8
+ "incremental": false,
9
+ "noEmit": true
10
+ },
11
+ "include": ["../src", "./**/*.ts"]
12
+ }
package/package.json CHANGED
@@ -1,26 +1,30 @@
1
1
  {
2
2
  "name": "@antelopejs/interface-api-util",
3
- "version": "0.1.2",
4
- "description": "AntelopeJS utility functions for API request validation and error handling",
3
+ "version": "0.2.0",
4
+ "description": "AntelopeJS helpers for API handlers: request assertions, input validation and rate limiting",
5
5
  "keywords": [
6
6
  "antelopejs",
7
- "interface",
8
7
  "api",
8
+ "interface",
9
+ "rate-limit",
9
10
  "validation"
10
11
  ],
12
+ "homepage": "https://interfaces.antelopejs.com/docs/api-util",
13
+ "bugs": {
14
+ "url": "https://github.com/AntelopeJS/interface-api-util/issues"
15
+ },
16
+ "license": "Apache-2.0",
11
17
  "repository": {
12
18
  "type": "git",
13
19
  "url": "git+https://github.com/AntelopeJS/interface-api-util.git"
14
20
  },
15
- "license": "Apache-2.0",
16
- "packageManager": "pnpm@10.6.5",
17
- "main": "dist/index.js",
18
- "types": "dist/index.d.ts",
19
21
  "files": [
20
22
  "dist",
21
23
  "docs",
22
24
  "skills"
23
25
  ],
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
24
28
  "exports": {
25
29
  ".": {
26
30
  "types": "./dist/index.d.ts",
@@ -28,37 +32,58 @@
28
32
  },
29
33
  "./package.json": "./package.json"
30
34
  },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "provenance": true
38
+ },
31
39
  "scripts": {
32
40
  "build": "rimraf dist && tsc",
33
- "format": "biome format --write .",
34
- "lint:fix": "biome check --write .",
35
- "lint": "biome check .",
41
+ "format": "oxfmt .",
42
+ "lint:fix": "oxlint --fix",
43
+ "lint": "oxlint",
36
44
  "prepack": "pnpm run build",
37
45
  "release": "pnpm run lint && pnpm run prepack && release-it",
38
- "test": "pnpm run build && ajs module test ."
39
- },
40
- "antelopeJs": {
41
- "test": "src/antelope.test.ts",
42
- "skills": [
43
- "./skills"
44
- ]
46
+ "test": "pnpm run build && ajs module test .",
47
+ "format:check": "oxfmt --check .",
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
+ "ratchet": "antelopejs-ratchet",
53
+ "sync": "antelopejs-sync",
54
+ "ratchet:update": "antelopejs-ratchet --update",
55
+ "sync:check": "antelopejs-sync --check",
56
+ "typecheck": "tsc --noEmit",
57
+ "suppressions": "antelopejs-suppressions",
58
+ "suppressions:update": "antelopejs-suppressions --update"
45
59
  },
46
60
  "devDependencies": {
47
- "@antelopejs/interface-api": "^0.0.5",
48
- "@antelopejs/interface-core": "^0.0.5",
49
- "@biomejs/biome": "2.3.2",
61
+ "@antelopejs/core": "^1.16.0",
62
+ "@antelopejs/interface-api": "^0.0.15",
63
+ "@antelopejs/interface-core": "^0.2.4",
64
+ "@antelopejs/tooling-configs": "^0.2.11",
50
65
  "@types/mocha": "^10.0.10",
51
66
  "@types/node": "^22.19.15",
67
+ "eslint-plugin-perfectionist": "^5.11.0",
68
+ "knip": "^6.34.0",
69
+ "lefthook": "^2.2.2",
70
+ "oxfmt": "^0.66.0",
71
+ "oxlint": "1.81.0",
72
+ "oxlint-tsgolint": "^7.0.2001",
52
73
  "release-it": "^19.2.4",
53
74
  "release-it-changelogen": "^0.1.0",
54
75
  "rimraf": "^6.1.3",
55
76
  "typescript": "^5.9.3"
56
77
  },
57
78
  "peerDependencies": {
58
- "@antelopejs/interface-api": ">=0.0.3 <1.0.0",
59
- "@antelopejs/interface-core": ">=0.0.3 <1.0.0"
79
+ "@antelopejs/interface-api": ">=0.0.15 <1.0.0",
80
+ "@antelopejs/interface-core": ">=0.2.4 <1.0.0"
60
81
  },
61
- "publishConfig": {
62
- "access": "public"
82
+ "packageManager": "pnpm@10.6.5",
83
+ "antelopeJs": {
84
+ "test": "src/antelope.test.ts",
85
+ "skills": [
86
+ "./skills"
87
+ ]
63
88
  }
64
89
  }
@@ -18,7 +18,12 @@ as thrown `HTTPResult` values, which the API interface converts into HTTP respon
18
18
  The package has a single export subpath (root only):
19
19
 
20
20
  ```ts
21
- import { assert, assertValidation, RateLimit, type RateLimitOptions } from "@antelopejs/interface-api-util";
21
+ import {
22
+ assert,
23
+ assertValidation,
24
+ RateLimit,
25
+ type RateLimitOptions,
26
+ } from "@antelopejs/interface-api-util";
22
27
  ```
23
28
 
24
29
  `hitWindow`, `pruneExpired`, and `resolveKey` are also exported but marked `@internal` (rate-limit
@@ -27,11 +32,25 @@ plumbing, exposed for tests) — do not reach for them in application code.
27
32
  ## Usage
28
33
 
29
34
  ```ts
30
- import { Controller, Get, Post, HTTPResult, Parameter, RawBody } from "@antelopejs/interface-api";
31
- import { assert, assertValidation, RateLimit } from "@antelopejs/interface-api-util";
35
+ import {
36
+ Controller,
37
+ Get,
38
+ Post,
39
+ HTTPResult,
40
+ Parameter,
41
+ RawBody,
42
+ } from "@antelopejs/interface-api";
43
+ import {
44
+ assert,
45
+ assertValidation,
46
+ RateLimit,
47
+ } from "@antelopejs/interface-api-util";
32
48
  import * as z from "zod";
33
49
 
34
- const createMemberSchema = z.object({ fullName: z.string().min(1), email: z.string().email() });
50
+ const createMemberSchema = z.object({
51
+ fullName: z.string().min(1),
52
+ email: z.string().email(),
53
+ });
35
54
 
36
55
  class MembersController extends Controller("members") {
37
56
  @Get(":id")
@@ -44,7 +63,9 @@ class MembersController extends Controller("members") {
44
63
 
45
64
  @Post()
46
65
  async create(@RawBody() body: Buffer) {
47
- const { fullName, email } = assertValidation(body, (b) => createMemberSchema.parse(JSON.parse((b as Buffer).toString())));
66
+ const { fullName, email } = assertValidation(body, (b) =>
67
+ createMemberSchema.parse(JSON.parse(b.toString())),
68
+ );
48
69
  return new HTTPResult(201, await saveMember({ fullName, email }));
49
70
  }
50
71
  }
@@ -52,9 +73,11 @@ class MembersController extends Controller("members") {
52
73
 
53
74
  - `assert(condition, code, message)` — throws `new HTTPResult(code, message)` when falsy; uses
54
75
  TypeScript `asserts condition`, so the compiler narrows after the call.
55
- - `assertValidation(body, validator, errorFunc?, code = 400)` — returns the validator's result;
56
- if the validator throws, rethrows an `HTTPResult` whose body is `errorFunc(err)` if provided,
57
- 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)` 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`.
58
81
  - `RateLimit(limit, windowMs, options?)` — fixed-window counter per key; over-limit requests throw
59
82
  a 429 `HTTPResult` (with `Retry-After`) before the handler runs. Successful responses get
60
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
+ }