@moxon/api 6.0.0-pre → 6.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.
Files changed (91) hide show
  1. package/README.md +44 -19
  2. package/dist/app.js +9 -4
  3. package/dist/app.js.map +1 -1
  4. package/dist/constants.d.ts +22 -0
  5. package/dist/constants.js +22 -0
  6. package/dist/constants.js.map +1 -1
  7. package/dist/endpoints/define.d.ts +30 -0
  8. package/dist/endpoints/define.js +14 -0
  9. package/dist/endpoints/define.js.map +1 -0
  10. package/dist/endpoints/hash.d.ts +2 -0
  11. package/dist/endpoints/hash.js +16 -0
  12. package/dist/endpoints/hash.js.map +1 -0
  13. package/dist/endpoints/index.d.ts +2 -0
  14. package/dist/endpoints/index.js +4 -0
  15. package/dist/endpoints/index.js.map +1 -0
  16. package/dist/endpoints/router.d.ts +10 -0
  17. package/dist/endpoints/router.js +50 -0
  18. package/dist/endpoints/router.js.map +1 -0
  19. package/dist/manifest.d.ts +11 -0
  20. package/dist/manifest.js +12 -0
  21. package/dist/manifest.js.map +1 -0
  22. package/dist/middleware/cors.d.ts +15 -0
  23. package/dist/middleware/cors.js +25 -7
  24. package/dist/middleware/cors.js.map +1 -1
  25. package/dist/middleware/ratelimit.d.ts +53 -1
  26. package/dist/middleware/ratelimit.js +180 -71
  27. package/dist/middleware/ratelimit.js.map +1 -1
  28. package/dist/modules/v4/barcode.d.ts +1 -1
  29. package/dist/modules/v4/barcode.js +9 -2
  30. package/dist/modules/v4/barcode.js.map +1 -1
  31. package/dist/modules/v4/captcha.d.ts +1 -1
  32. package/dist/modules/v4/captcha.js +5 -1
  33. package/dist/modules/v4/captcha.js.map +1 -1
  34. package/dist/modules/v4/chat.js +61 -12
  35. package/dist/modules/v4/chat.js.map +1 -1
  36. package/dist/modules/v4/cron.js +20 -2
  37. package/dist/modules/v4/cron.js.map +1 -1
  38. package/dist/modules/v4/hyperplanning.d.ts +11 -0
  39. package/dist/modules/v4/hyperplanning.js +30 -29
  40. package/dist/modules/v4/hyperplanning.js.map +1 -1
  41. package/dist/modules/v4/qrcode.d.ts +10 -0
  42. package/dist/modules/v4/qrcode.js +138 -14
  43. package/dist/modules/v4/qrcode.js.map +1 -1
  44. package/dist/modules/v4/regex.d.ts +1 -1
  45. package/dist/modules/v4/regex.js +67 -22
  46. package/dist/modules/v4/regex.js.map +1 -1
  47. package/dist/modules/v4/tic_tac_toe.js +56 -15
  48. package/dist/modules/v4/tic_tac_toe.js.map +1 -1
  49. package/dist/modules/v5/asymmetric.d.ts +15 -0
  50. package/dist/modules/v5/asymmetric.js +67 -5
  51. package/dist/modules/v5/asymmetric.js.map +1 -1
  52. package/dist/modules/v5/barcode.d.ts +1 -1
  53. package/dist/modules/v5/barcode.js +9 -2
  54. package/dist/modules/v5/barcode.js.map +1 -1
  55. package/dist/modules/v5/captcha.d.ts +12 -4
  56. package/dist/modules/v5/captcha.js +25 -8
  57. package/dist/modules/v5/captcha.js.map +1 -1
  58. package/dist/modules/v5/chat.js +66 -17
  59. package/dist/modules/v5/chat.js.map +1 -1
  60. package/dist/modules/v5/cron.d.ts +26 -0
  61. package/dist/modules/v5/cron.js +356 -0
  62. package/dist/modules/v5/cron.js.map +1 -0
  63. package/dist/modules/v5/pow.js +1 -1
  64. package/dist/modules/v5/pow.js.map +1 -1
  65. package/dist/modules/v5/tic_tac_toe.js +61 -20
  66. package/dist/modules/v5/tic_tac_toe.js.map +1 -1
  67. package/dist/modules/v5.d.ts +3 -2
  68. package/dist/modules/v5.js +3 -2
  69. package/dist/modules/v5.js.map +1 -1
  70. package/dist/routes/delete.js +0 -8
  71. package/dist/routes/delete.js.map +1 -1
  72. package/dist/routes/get.js +0 -2
  73. package/dist/routes/get.js.map +1 -1
  74. package/dist/routes/patch.js +0 -4
  75. package/dist/routes/patch.js.map +1 -1
  76. package/dist/routes/post.js +12 -25
  77. package/dist/routes/post.js.map +1 -1
  78. package/dist/storage/index.js +9 -8
  79. package/dist/storage/index.js.map +1 -1
  80. package/dist/types/storage.d.ts +4 -0
  81. package/dist/utils/challenge.d.ts +5 -3
  82. package/dist/utils/challenge.js +52 -6
  83. package/dist/utils/challenge.js.map +1 -1
  84. package/dist/utils/helpers.d.ts +18 -0
  85. package/dist/utils/helpers.js +35 -1
  86. package/dist/utils/helpers.js.map +1 -1
  87. package/dist/utils/remote.d.ts +42 -0
  88. package/dist/utils/remote.js +313 -0
  89. package/dist/utils/remote.js.map +1 -0
  90. package/package.json +28 -8
  91. package/src/favicon.ico +0 -0
package/README.md CHANGED
@@ -9,11 +9,11 @@ It works two ways:
9
9
  - **As a library** — import any module directly into your code.
10
10
  - **As a server** — a single import boots an Express app that exposes every module over HTTP.
11
11
 
12
- The HTTP surface is versioned from `v4` to `v6`, and every version stays mounted side by side, so a new release never changes the behaviour of the one you already target. The full endpoint reference lives on [docs.sylvain.sh](https://docs.sylvain.sh).
12
+ The HTTP surface is versioned from `v4` to `v6`, and every version stays mounted side by side, so a new release never changes the behaviour of the one you already target. The full endpoint reference lives on [moxon.dev](https://moxon.dev).
13
13
 
14
14
  ## Migrating from `@20syldev/api`
15
15
 
16
- `@moxon/api` is the new name of `@20syldev/api`. This release is the code of `@20syldev/api@6.0.0` under its new name: same exports, same `/v6` HTTP API. Nothing else changed.
16
+ `@moxon/api` is the new name of `@20syldev/api`, which no longer receives updates. `6.0.0-pre` carries the code of `@20syldev/api@6.0.0` under the new name, and every release since ships only as `@moxon/api`. The exports and the `/v6` HTTP API are unchanged, so migrating only takes a rename.
17
17
 
18
18
  ```bash
19
19
  npm uninstall @20syldev/api
@@ -75,15 +75,15 @@ console.log(user.username);
75
75
 
76
76
  ## What's inside
77
77
 
78
- | Domain | Exports |
79
- | ------------------ | ------------------------------------------------------------------------------------- |
80
- | **Crypto** | `asymmetric` `symmetric` `hash` `jwt` `otp` `token` `password` `captcha` |
81
- | **Math & data** | **`matrix`** **`algorithms`** `evaluate` `statistics` `convert` `csv` |
82
- | **Text** | **`text`** **`encode`** **`validate`** `caseConvert` `levenshtein` `regex` `parseUrl` |
83
- | **Graphics** | **`chart`** `barcode` `qrcode` `avatar` `placeholder` `color` `palette` |
84
- | **Fake data** | `personal` `address` `credit` `username` `dice` `domain` `agent` |
85
- | **Network & time** | `ip` `geo` `time` `cron` `hyperplanning` |
86
- | **Stateful** | `chat` `tic_tac_toe` |
78
+ | Domain | Exports |
79
+ | ------------------ | ------------------------------------------------------------------------------------------ |
80
+ | **Crypto** | `asymmetric` `asymmetricAsync` `symmetric` `hash` `jwt` `otp` `token` `password` `captcha` |
81
+ | **Math & data** | **`matrix`** **`algorithms`** `evaluate` `statistics` `convert` `csv` |
82
+ | **Text** | **`text`** **`encode`** **`validate`** `caseConvert` `levenshtein` `regex` `parseUrl` |
83
+ | **Graphics** | **`chart`** `barcode` `qrcode` `avatar` `placeholder` `color` `palette` |
84
+ | **Fake data** | `personal` `address` `credit` `username` `dice` `domain` `agent` |
85
+ | **Network & time** | `ip` `geo` `time` `cron` `hyperplanning` |
86
+ | **Stateful** | `chat` `tic_tac_toe` |
87
87
 
88
88
  The six names in **bold** are namespaces — they group related functions, called as `namespace.method()`:
89
89
 
@@ -112,6 +112,7 @@ Two names differ from their HTTP endpoint, because `case` is a reserved word and
112
112
  ```js
113
113
  import {
114
114
  asymmetric,
115
+ asymmetricAsync,
115
116
  caseConvert,
116
117
  chart,
117
118
  csv,
@@ -130,6 +131,10 @@ const { result: encrypted } = asymmetric('encrypt', { text: 'secret message', pu
130
131
  const { result: decrypted } = asymmetric('decrypt', { text: encrypted, privateKey });
131
132
  console.log(decrypted); // 'secret message'
132
133
 
134
+ // Same result, but the key pair is generated off the main thread
135
+ const keys = await asymmetricAsync('keygen', { modulusLength: 4096 });
136
+ console.log(keys.modulusLength); // 4096
137
+
133
138
  // AES-256-GCM symmetric encryption, key derived with scrypt
134
139
  const { result: blob } = symmetric('encrypt', 'secret message', 'a-strong-key');
135
140
  const { result: plain } = symmetric('decrypt', blob, 'a-strong-key');
@@ -184,6 +189,21 @@ console.log(parsed.host, parsed.port, parsed.params, parsed.fragment);
184
189
  console.log(validate.email('alice@example.com').valid); // true
185
190
  ```
186
191
 
192
+ ## Endpoint manifest
193
+
194
+ `@moxon/api/manifest` describes the HTTP endpoints as data — method, path, category, parameters and a request example — for tools that document or call the API. Each entry is typed as `ManifestEntry`.
195
+
196
+ ```js
197
+ import { manifest } from '@moxon/api/manifest';
198
+
199
+ const hash = manifest.find((entry) => entry.name === 'hash');
200
+ console.log(hash.method, hash.path); // 'post' '/hash'
201
+ console.log(Object.keys(hash.params)); // ['text', 'method', 'encoding']
202
+ console.log(hash.example); // { text: 'hello', method: 'sha256' }
203
+ ```
204
+
205
+ > _The manifest is experimental: it only lists the endpoints already declared as contracts (`hash` for now), and its shape may still change in a minor release._
206
+
187
207
  ## Running the server
188
208
 
189
209
  From the installed package, one import mounts every version:
@@ -199,10 +219,13 @@ $ npm run build && npm start
199
219
  ```
200
220
 
201
221
  ```console
202
- > @moxon/api@6.0.0-pre build
203
- > tsc
222
+ > @moxon/api@6.2.0 build
223
+ > npm run clean && tsc
204
224
 
205
- > @moxon/api@6.0.0-pre start
225
+ > @moxon/api@6.2.0 clean
226
+ > node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"
227
+
228
+ > @moxon/api@6.2.0 start
206
229
  > node dist/app.js
207
230
 
208
231
  API is running on
@@ -216,7 +239,7 @@ For development with automatic reloading:
216
239
  $ npm run dev
217
240
  ```
218
241
 
219
- Endpoints follow the `/:version/:endpoint` pattern — `GET /v5/color`, `GET /v5/evaluate?expr=2%2B2`, `POST /v5/matrix`. `GET /` lists the available versions, and `GET /:version` lists that version's endpoints with their parameters.
242
+ Endpoints follow the `/:version/:endpoint` pattern — `GET /v5/color`, `GET /v5/evaluate?expr=2%2B2`, `POST /v5/matrix`. `GET /` lists the available versions, and `GET /:version` lists that version's endpoints with their paths.
220
243
 
221
244
  ### Configuration
222
245
 
@@ -230,7 +253,7 @@ Every setting is optional and read from the environment; a `.env` file at the pr
230
253
  | `INSTANCE_CREATED` | — | Launch date reported by `/:version/infos` |
231
254
  | `LOGS_TOKEN` | — | Value required in the `X-Logs-Token` header to read `/logs`; the route answers 404 while unset |
232
255
  | `CHALLENGE_SECRET` | random | Key signing the captcha and proof-of-work tokens; a random one is generated at startup while unset |
233
- | `TRUSTED_PROXIES` | one hop | CIDR blocks separated by spaces or commas, or the `loopback` / `linklocal` / `uniquelocal` shorthands, for deployments behind a CDN or platform router |
256
+ | `TRUSTED_PROXIES` | one hop | CIDR blocks or `loopback` / `linklocal` / `uniquelocal`, space or comma separated; unset, exactly one reverse proxy is assumed and a warning is logged |
234
257
  | `GLOBAL_LIMIT` | `50000` | Requests per hour accepted across the whole instance |
235
258
  | `DEFAULT_LIMIT` | `2000` | Per-client requests per hour |
236
259
  | `DEFAULT_BURST` | `50` | Per-client requests per 10-second window |
@@ -239,7 +262,9 @@ The three metadata variables are omitted from responses when unset, so an instan
239
262
 
240
263
  Higher quotas can be granted per client: `ADVANCED_`, `PRO_` and `BUSINESS_` variants of `_LIMIT` and `_BURST` define the tiers, and the matching `*_TOKEN_LIST` variables hold the space-separated bearer tokens that map to them. A client's tier and quota are reported by `GET /auth`.
241
264
 
242
- > _Rate-limit counters live in process memory, so each instance of a multi-instance deployment counts separately._
265
+ > _Rate-limit counters live in process memory, so each instance of a multi-instance deployment counts separately. Clients are counted by address: an IPv4-mapped IPv6 address counts as its IPv4 form and any other IPv6 address as its `/64` block, while all the `/64` blocks of one `/48` network share four times the per-client hourly quota. A forwarded address that is not an IP address counts as the connecting peer. Only requests the per-client limits let through use the `GLOBAL_LIMIT` budget._
266
+
267
+ > _The one-hop default reads the client address from the last `X-Forwarded-For` entry, which is right behind a single reverse proxy such as nginx or Caddy. Exposed directly, callers can forge that header and pick their own address; behind several hops, every caller is counted as the nearest proxy. Set `TRUSTED_PROXIES` to match the actual chain._
243
268
 
244
269
  > _Captcha and proof-of-work tokens are signed with `CHALLENGE_SECRET`. While it is unset each process invents its own key, so tokens stop verifying after a restart and are rejected by sibling instances — set it explicitly for any deployment running more than one process, with a full-entropy value such as `openssl rand -hex 32`._
245
270
 
@@ -268,9 +293,9 @@ Every version is fully typed. The package root always tracks the latest one.
268
293
  | `npm run format` | Format with Prettier and auto-fix with ESLint |
269
294
  | `npm run check` | TypeScript and ESLint verification |
270
295
 
271
- ## Hosted instance
296
+ ## Documentation
272
297
 
273
- A public instance runs at [api.sylvain.sh](https://api.sylvain.sh), with the complete endpoint reference, request examples and usage guides on [docs.sylvain.sh](https://docs.sylvain.sh).
298
+ The complete endpoint reference, request examples and usage guides live on [moxon.dev](https://moxon.dev).
274
299
 
275
300
  ## License
276
301
 
package/dist/app.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import express from 'express';
2
2
  import { env } from './config/env.js';
3
- import { setupCors } from './middleware/cors.js';
3
+ import { endpoints } from './endpoints/index.js';
4
+ import { build } from './endpoints/router.js';
5
+ import { setupCors, setupParsers } from './middleware/cors.js';
4
6
  import { errorHandler } from './middleware/error.js';
5
7
  import { jsonResponseMiddleware } from './middleware/json.js';
6
8
  import { loggerMiddleware } from './middleware/logger.js';
@@ -13,12 +15,14 @@ import patchRoutes from './routes/patch.js';
13
15
  import postRoutes from './routes/post.js';
14
16
  import { error } from './utils/response.js';
15
17
  const app = express();
16
- // Setup CORS, body parsing, static files
18
+ // Security headers, CORS, static files
17
19
  setupCors(app);
18
20
  // Custom JSON response
19
21
  app.use(jsonResponseMiddleware);
20
- // Rate limiting
22
+ // Rate limiting, ahead of body parsing so malformed bodies are counted too
21
23
  app.use(rateLimitMiddleware);
24
+ // Body parsing
25
+ setupParsers(app);
22
26
  // Request logging
23
27
  app.use(loggerMiddleware);
24
28
  // Root routes (before version check)
@@ -36,7 +40,8 @@ try {
36
40
  catch {
37
41
  // No plugins bundled in this build
38
42
  }
39
- // Versioned routes
43
+ // Versioned routes, declared endpoints first
44
+ app.use(build(endpoints));
40
45
  app.use(getRoutes);
41
46
  app.use(postRoutes);
42
47
  app.use(patchRoutes);
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,OAAqD,MAAM,SAAS,CAAC;AAE5E,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,yCAAyC;AACzC,SAAS,CAAC,GAAG,CAAC,CAAC;AAEf,uBAAuB;AACvB,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAEhC,gBAAgB;AAChB,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAE7B,kBAAkB;AAClB,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE1B,qCAAqC;AACrC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAErB,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,sBAAsB,CAAC,CAAC;AAC7C,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,CAAC;AAExD,4BAA4B;AAC5B,MAAM,KAAK,GAAG,oBAAoB,CAAC;AACnC,IAAI,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAA0B,CAAC;IAC5E,KAAK,MAAM,MAAM,IAAI,OAAO;QAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAAC,MAAM,CAAC;IACL,mCAAmC;AACvC,CAAC;AAED,mBAAmB;AACnB,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACnB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACpB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACrB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,oBAAoB;AACpB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACpC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,GAAG,CAAC,IAAI,mBAAmB,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,eAAe;AACf,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;IAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,CACtB,OAAO,CAAC,GAAG,CAAC,6CAA6C,GAAG,CAAC,IAAI,4BAA4B,GAAG,CAAC,IAAI,EAAE,CAAC,CAC3G,CAAC;AACN,CAAC;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,OAAqD,MAAM,SAAS,CAAC;AAE5E,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAC5C,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,uCAAuC;AACvC,SAAS,CAAC,GAAG,CAAC,CAAC;AAEf,uBAAuB;AACvB,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAEhC,2EAA2E;AAC3E,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAE7B,eAAe;AACf,YAAY,CAAC,GAAG,CAAC,CAAC;AAElB,kBAAkB;AAClB,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE1B,qCAAqC;AACrC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAErB,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,sBAAsB,CAAC,CAAC;AAC7C,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,CAAC;AAExD,4BAA4B;AAC5B,MAAM,KAAK,GAAG,oBAAoB,CAAC;AACnC,IAAI,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAA0B,CAAC;IAC5E,KAAK,MAAM,MAAM,IAAI,OAAO;QAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAAC,MAAM,CAAC;IACL,mCAAmC;AACvC,CAAC;AAED,6CAA6C;AAC7C,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1B,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACnB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACpB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACrB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,oBAAoB;AACpB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACpC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,GAAG,CAAC,IAAI,mBAAmB,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,eAAe;AACf,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;IAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,CACtB,OAAO,CAAC,GAAG,CAAC,6CAA6C,GAAG,CAAC,IAAI,4BAA4B,GAAG,CAAC,IAAI,EAAE,CAAC,CAC3G,CAAC;AACN,CAAC;AAED,eAAe,GAAG,CAAC"}
@@ -1,22 +1,33 @@
1
1
  export declare const APP_VERSION: string;
2
2
  export declare const DEFAULT_PORT = 3000;
3
3
  export declare const START_TIME: number;
4
+ export declare const CHALLENGE_MAX_ATTEMPTS = 5;
4
5
  export declare const CHALLENGE_TTL = 300000;
5
6
  export declare const GAME_CLEANUP_TTL = 600000;
6
7
  export declare const MAX_LOG_ENTRIES = 1000;
8
+ export declare const RATE_LIMIT_IPV6_NETWORK = 48;
9
+ export declare const RATE_LIMIT_IPV6_PREFIX = 64;
10
+ export declare const RATE_LIMIT_NETWORK_FACTOR = 4;
7
11
  export declare const RATE_LIMIT_WINDOW = 10000;
8
12
  export declare const RATE_LIMIT_MAX = 50;
9
13
  export declare const SESSION_TTL = 3600000;
10
14
  export declare const STATUS_MESSAGES: Record<number, string>;
11
15
  export declare const EARTH_RADIUS_KM = 6371;
12
16
  export declare const MAX_ADDRESS_COUNT = 10;
17
+ export declare const MAX_BARCODE_DATA_LENGTH = 128;
13
18
  export declare const MAX_BASE_VALUE_LENGTH = 100;
19
+ export declare const MAX_CAPTCHA_TEXT_LENGTH = 32;
14
20
  export declare const MAX_CHART_DATASETS = 5;
15
21
  export declare const MAX_CHART_LABELS = 20;
22
+ export declare const MAX_CHAT_MESSAGES = 200;
16
23
  export declare const MAX_CSV_LENGTH = 50000;
17
24
  export declare const MAX_CSV_ROWS = 1000;
18
25
  export declare const MAX_CREDIT_COUNT = 10;
26
+ export declare const MAX_CONCURRENT_KEYGENS = 2;
19
27
  export declare const MAX_COUNTDOWN_YEARS = 100;
28
+ export declare const MAX_CRON_DURATION = 250;
29
+ export declare const MAX_CRON_DURATION_V4 = 1000;
30
+ export declare const MAX_CRON_GAP: number;
20
31
  export declare const MAX_CRON_ITERATIONS: number;
21
32
  export declare const MAX_CRON_RESULTS = 20;
22
33
  export declare const MAX_DIFF_LENGTH = 10000;
@@ -25,6 +36,10 @@ export declare const MAX_EXPR_DEPTH = 100;
25
36
  export declare const MAX_EXPR_LENGTH = 500;
26
37
  export declare const MAX_FACTORIAL = 170;
27
38
  export declare const MAX_GCD_VALUE = 100000;
39
+ export declare const ICS_FETCH_TIMEOUT = 10000;
40
+ export declare const MAX_ICS_BYTES: number;
41
+ export declare const MAX_ICS_EVENTS = 5000;
42
+ export declare const MAX_ID_LENGTH = 256;
28
43
  export declare const MAX_JWT_LENGTH = 8192;
29
44
  export declare const MAX_LEVENSHTEIN_LENGTH = 1000;
30
45
  export declare const MIN_PASSWORD_LENGTH = 8;
@@ -34,15 +49,22 @@ export declare const MAX_MATRIX_SIZE = 20;
34
49
  export declare const MAX_PATTERN_LENGTH = 200;
35
50
  export declare const MAX_POW_DIFFICULTY = 6;
36
51
  export declare const MAX_PRIME_LIST = 10000;
52
+ export declare const MAX_PRIVATE_CHATS = 1000;
37
53
  export declare const POW_DEFAULT_DIFFICULTY = 4;
54
+ export declare const MAX_QRCODE_ICON_PIXELS = 16000000;
38
55
  export declare const MAX_QRCODE_LOGO_BYTES: number;
56
+ export declare const QRCODE_ICON_TIMEOUT = 5000;
39
57
  export declare const MAX_READ_LENGTH = 50000;
40
58
  export declare const MAX_REGEX_MATCHES = 100;
59
+ export declare const REGEX_TIMEOUT = 25;
60
+ export declare const MAX_REMOTE_REDIRECTS = 10;
41
61
  export declare const MAX_RSA_MODULUS = 4096;
42
62
  export declare const MAX_SEMVER_LENGTH = 256;
43
63
  export declare const MAX_STRING_LENGTH = 1000;
64
+ export declare const MAX_TIMESTAMP_LENGTH = 128;
44
65
  export declare const MIN_TOKEN_LENGTH = 12;
45
66
  export declare const MAX_TOKEN_LENGTH = 4096;
46
67
  export declare const MAX_URL_LENGTH = 2048;
68
+ export declare const MAX_USERNAME_LENGTH = 100;
47
69
  export declare const MAX_UUID_COUNT = 50;
48
70
  export declare const ROMAN_VALUES: [number, string][];
package/dist/constants.js CHANGED
@@ -4,9 +4,13 @@ export const APP_VERSION = pkg.version;
4
4
  export const DEFAULT_PORT = 3000;
5
5
  export const START_TIME = Date.now();
6
6
  // Server
7
+ export const CHALLENGE_MAX_ATTEMPTS = 5;
7
8
  export const CHALLENGE_TTL = 300_000;
8
9
  export const GAME_CLEANUP_TTL = 600_000;
9
10
  export const MAX_LOG_ENTRIES = 1000;
11
+ export const RATE_LIMIT_IPV6_NETWORK = 48;
12
+ export const RATE_LIMIT_IPV6_PREFIX = 64;
13
+ export const RATE_LIMIT_NETWORK_FACTOR = 4;
10
14
  export const RATE_LIMIT_WINDOW = 10_000;
11
15
  export const RATE_LIMIT_MAX = 50;
12
16
  export const SESSION_TTL = 3_600_000;
@@ -22,13 +26,20 @@ export const STATUS_MESSAGES = {
22
26
  // Modules
23
27
  export const EARTH_RADIUS_KM = 6371;
24
28
  export const MAX_ADDRESS_COUNT = 10;
29
+ export const MAX_BARCODE_DATA_LENGTH = 128;
25
30
  export const MAX_BASE_VALUE_LENGTH = 100;
31
+ export const MAX_CAPTCHA_TEXT_LENGTH = 32;
26
32
  export const MAX_CHART_DATASETS = 5;
27
33
  export const MAX_CHART_LABELS = 20;
34
+ export const MAX_CHAT_MESSAGES = 200;
28
35
  export const MAX_CSV_LENGTH = 50_000;
29
36
  export const MAX_CSV_ROWS = 1000;
30
37
  export const MAX_CREDIT_COUNT = 10;
38
+ export const MAX_CONCURRENT_KEYGENS = 2;
31
39
  export const MAX_COUNTDOWN_YEARS = 100;
40
+ export const MAX_CRON_DURATION = 250;
41
+ export const MAX_CRON_DURATION_V4 = 1000;
42
+ export const MAX_CRON_GAP = 401 * 366 * 86_400_000;
32
43
  export const MAX_CRON_ITERATIONS = 365 * 24 * 60;
33
44
  export const MAX_CRON_RESULTS = 20;
34
45
  export const MAX_DIFF_LENGTH = 10_000;
@@ -37,6 +48,10 @@ export const MAX_EXPR_DEPTH = 100;
37
48
  export const MAX_EXPR_LENGTH = 500;
38
49
  export const MAX_FACTORIAL = 170;
39
50
  export const MAX_GCD_VALUE = 100_000;
51
+ export const ICS_FETCH_TIMEOUT = 10_000;
52
+ export const MAX_ICS_BYTES = 1024 * 1024;
53
+ export const MAX_ICS_EVENTS = 5000;
54
+ export const MAX_ID_LENGTH = 256;
40
55
  export const MAX_JWT_LENGTH = 8192;
41
56
  export const MAX_LEVENSHTEIN_LENGTH = 1000;
42
57
  export const MIN_PASSWORD_LENGTH = 8;
@@ -46,16 +61,23 @@ export const MAX_MATRIX_SIZE = 20;
46
61
  export const MAX_PATTERN_LENGTH = 200;
47
62
  export const MAX_POW_DIFFICULTY = 6;
48
63
  export const MAX_PRIME_LIST = 10_000;
64
+ export const MAX_PRIVATE_CHATS = 1000;
49
65
  export const POW_DEFAULT_DIFFICULTY = 4;
66
+ export const MAX_QRCODE_ICON_PIXELS = 16_000_000;
50
67
  export const MAX_QRCODE_LOGO_BYTES = 2 * 1024 * 1024;
68
+ export const QRCODE_ICON_TIMEOUT = 5000;
51
69
  export const MAX_READ_LENGTH = 50_000;
52
70
  export const MAX_REGEX_MATCHES = 100;
71
+ export const REGEX_TIMEOUT = 25;
72
+ export const MAX_REMOTE_REDIRECTS = 10;
53
73
  export const MAX_RSA_MODULUS = 4096;
54
74
  export const MAX_SEMVER_LENGTH = 256;
55
75
  export const MAX_STRING_LENGTH = 1000;
76
+ export const MAX_TIMESTAMP_LENGTH = 128;
56
77
  export const MIN_TOKEN_LENGTH = 12;
57
78
  export const MAX_TOKEN_LENGTH = 4096;
58
79
  export const MAX_URL_LENGTH = 2048;
80
+ export const MAX_USERNAME_LENGTH = 100;
59
81
  export const MAX_UUID_COUNT = 50;
60
82
  export const ROMAN_VALUES = [
61
83
  [1000, 'M'],
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAExD,MAAM;AACN,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAErC,SAAS;AACT,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AACrC,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,MAAM,CAAC,MAAM,eAAe,GAA2B;IACnD,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,oBAAoB;IACzB,GAAG,EAAE,mBAAmB;IACxB,GAAG,EAAE,mBAAmB;IACxB,GAAG,EAAE,uBAAuB;CAC/B,CAAC;AAEF,UAAU;AACV,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AACtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,MAAM,CAAC,MAAM,YAAY,GAAuB;IAC5C,CAAC,IAAI,EAAE,GAAG,CAAC;IACX,CAAC,GAAG,EAAE,IAAI,CAAC;IACX,CAAC,GAAG,EAAE,GAAG,CAAC;IACV,CAAC,GAAG,EAAE,IAAI,CAAC;IACX,CAAC,GAAG,EAAE,GAAG,CAAC;IACV,CAAC,EAAE,EAAE,IAAI,CAAC;IACV,CAAC,EAAE,EAAE,GAAG,CAAC;IACT,CAAC,EAAE,EAAE,IAAI,CAAC;IACV,CAAC,EAAE,EAAE,GAAG,CAAC;IACT,CAAC,CAAC,EAAE,IAAI,CAAC;IACT,CAAC,CAAC,EAAE,GAAG,CAAC;IACR,CAAC,CAAC,EAAE,IAAI,CAAC;IACT,CAAC,CAAC,EAAE,GAAG,CAAC;CACX,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Request } from 'express';
2
+ export type Category = 'security' | 'text' | 'images' | 'compute' | 'fakeData' | 'network' | 'realtime';
3
+ export interface Param {
4
+ required?: boolean;
5
+ missing?: string;
6
+ options?: readonly string[];
7
+ default?: string;
8
+ }
9
+ export type Values<P extends string> = Record<P, string | undefined>;
10
+ export interface Endpoint<P extends string = string> {
11
+ name: string;
12
+ method: 'get' | 'post';
13
+ path: string;
14
+ category: Category;
15
+ since?: number;
16
+ params: Record<P, Param>;
17
+ example: Partial<Record<P, string>>;
18
+ run(values: Values<P>, req: Request): unknown;
19
+ }
20
+ /**
21
+ * Declares an endpoint contract and infers its parameter names, so `run` receives typed values.
22
+ * The HTTP router and the documentation manifest are both built from it, so the two cannot drift apart.
23
+ *
24
+ * @param endpoint - Contract of the route: `since` is the first API version serving it (all of them when
25
+ * omitted), `params` are read from the query string on GET and from the JSON body on POST, `example` is the
26
+ * input the documentation runs to show a real output, and an error thrown by `run` becomes a 400 carrying its
27
+ * message
28
+ * @returns The same contract, typed with its parameter names
29
+ */
30
+ export declare function define<P extends string>(endpoint: Endpoint<P>): Endpoint<P>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Declares an endpoint contract and infers its parameter names, so `run` receives typed values.
3
+ * The HTTP router and the documentation manifest are both built from it, so the two cannot drift apart.
4
+ *
5
+ * @param endpoint - Contract of the route: `since` is the first API version serving it (all of them when
6
+ * omitted), `params` are read from the query string on GET and from the JSON body on POST, `example` is the
7
+ * input the documentation runs to show a real output, and an error thrown by `run` becomes a 400 carrying its
8
+ * message
9
+ * @returns The same contract, typed with its parameter names
10
+ */
11
+ export function define(endpoint) {
12
+ return endpoint;
13
+ }
14
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/endpoints/define.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAAmB,QAAqB;IAC1D,OAAO,QAAQ,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("./define.js").Endpoint<"text" | "method" | "encoding">;
2
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import hash from '../modules/v4/hash.js';
2
+ import { define } from './define.js';
3
+ export default define({
4
+ name: 'hash',
5
+ method: 'post',
6
+ path: '/hash',
7
+ category: 'security',
8
+ params: {
9
+ text: { required: true, missing: 'Please provide a text (?text={text})' },
10
+ method: { required: true, missing: 'Please provide a valid hash algorithm (&method={algorithm})' },
11
+ encoding: { options: ['hex', 'base64'], default: 'hex' },
12
+ },
13
+ example: { text: 'hello', method: 'sha256' },
14
+ run: ({ text, method, encoding }) => hash(text, method, encoding),
15
+ });
16
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/endpoints/hash.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,uBAAuB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,eAAe,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE;QACJ,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,sCAAsC,EAAE;QACzE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,6DAA6D,EAAE;QAClG,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE;KAC3D;IACD,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE;IAC5C,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAK,EAAE,MAAO,EAAE,QAAQ,CAAC;CACtE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Endpoint } from './define.js';
2
+ export declare const endpoints: Endpoint[];
@@ -0,0 +1,4 @@
1
+ import hash from './hash.js';
2
+ // Endpoints declared as contracts; the others are still hand-written in routes/ and move here one by one
3
+ export const endpoints = [hash];
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/endpoints/index.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,yGAAyG;AACzG,MAAM,CAAC,MAAM,SAAS,GAAe,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Router } from 'express';
2
+ import type { Endpoint } from './define.js';
3
+ /**
4
+ * Builds the HTTP routes of the declared endpoints, with the required parameter check and the shared error format.
5
+ * A POST-only endpoint also answers GET with a 405, and a version older than its `since` answers 404.
6
+ *
7
+ * @param endpoints - Contracts to serve, each mounted under `/:version`
8
+ * @returns Router to mount before the hand-written routes, so a declared endpoint takes precedence
9
+ */
10
+ export declare function build(endpoints: Endpoint[]): Router;
@@ -0,0 +1,50 @@
1
+ import { Router } from 'express';
2
+ import { since } from '../utils/helpers.js';
3
+ import { error } from '../utils/response.js';
4
+ /**
5
+ * Builds the HTTP routes of the declared endpoints, with the required parameter check and the shared error format.
6
+ * A POST-only endpoint also answers GET with a 405, and a version older than its `since` answers 404.
7
+ *
8
+ * @param endpoints - Contracts to serve, each mounted under `/:version`
9
+ * @returns Router to mount before the hand-written routes, so a declared endpoint takes precedence
10
+ */
11
+ export function build(endpoints) {
12
+ const router = Router();
13
+ for (const endpoint of endpoints) {
14
+ const path = `/:version${endpoint.path}`;
15
+ const served = (req) => !endpoint.since || since(req.version, endpoint.since);
16
+ router[endpoint.method](path, async (req, res) => {
17
+ if (!served(req)) {
18
+ error(res, 404, `Endpoint not available in ${req.version}.`);
19
+ return;
20
+ }
21
+ const source = ((endpoint.method === 'get' ? req.query : req.body) ?? {});
22
+ const values = {};
23
+ for (const [name, param] of Object.entries(endpoint.params)) {
24
+ const value = source[name];
25
+ if (param.required && !value) {
26
+ error(res, 400, param.missing ?? `Please provide a ${name}`);
27
+ return;
28
+ }
29
+ values[name] = value;
30
+ }
31
+ try {
32
+ res.jsonResponse(await endpoint.run(values, req));
33
+ }
34
+ catch (err) {
35
+ error(res, 400, err.message);
36
+ }
37
+ });
38
+ if (endpoint.method === 'post') {
39
+ router.get(path, (req, res) => {
40
+ if (!served(req)) {
41
+ error(res, 404, `Endpoint not available in ${req.version}.`);
42
+ return;
43
+ }
44
+ error(res, 405, 'This endpoint only supports POST requests.');
45
+ });
46
+ }
47
+ }
48
+ return router;
49
+ }
50
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/endpoints/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAG7C;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,SAAqB;IACvC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,YAAY,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,CAAC,GAAY,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAEvF,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;YAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACf,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,6BAA6B,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC;gBAC7D,OAAO;YACX,CAAC;YAED,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAA2B,CAAC;YACpG,MAAM,MAAM,GAAuC,EAAE,CAAC;YAEtD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC3B,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;oBAC3B,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,IAAI,oBAAoB,IAAI,EAAE,CAAC,CAAC;oBAC7D,OAAO;gBACX,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YACzB,CAAC;YAED,IAAI,CAAC;gBACD,GAAG,CAAC,YAAY,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,KAAK,CAAC,GAAG,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;YAC5C,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;gBAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBACf,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,6BAA6B,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC;oBAC7D,OAAO;gBACX,CAAC;gBACD,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,4CAA4C,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Category, Param } from './endpoints/define.js';
2
+ export interface ManifestEntry {
3
+ name: string;
4
+ method: 'get' | 'post';
5
+ path: string;
6
+ category: Category;
7
+ since?: number;
8
+ params: Record<string, Param>;
9
+ example: Record<string, string>;
10
+ }
11
+ export declare const manifest: ManifestEntry[];
@@ -0,0 +1,12 @@
1
+ import { endpoints } from './endpoints/index.js';
2
+ // Declared endpoints without their run function, for the documentation sidebar, parameter lists and playground
3
+ export const manifest = endpoints.map(({ name, method, path, category, since, params, example }) => ({
4
+ name,
5
+ method,
6
+ path,
7
+ category,
8
+ ...(since ? { since } : {}),
9
+ params,
10
+ example: example,
11
+ }));
12
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAYjD,+GAA+G;AAC/G,MAAM,CAAC,MAAM,QAAQ,GAAoB,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAClH,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,MAAM;IACN,OAAO,EAAE,OAAiC;CAC7C,CAAC,CAAC,CAAC"}
@@ -1,2 +1,17 @@
1
1
  import { type Express } from 'express';
2
+ /**
3
+ * Applies the proxy setting, the security headers, CORS and the robots.txt route.
4
+ *
5
+ * The security headers come first so that every response carries them,
6
+ * preflights and errors included. Body parsing is mounted separately by
7
+ * setupParsers, after the rate limiter.
8
+ *
9
+ * @param app - Express application to configure
10
+ */
2
11
  export declare function setupCors(app: Express): void;
12
+ /**
13
+ * Mounts the URL-encoded and JSON body parsers, both capped at 10 KB.
14
+ *
15
+ * @param app - Express application to configure
16
+ */
17
+ export declare function setupParsers(app: Express): void;
@@ -5,8 +5,25 @@ import { fileURLToPath } from 'url';
5
5
  import { env } from '../config/env.js';
6
6
  const __filename = fileURLToPath(import.meta.url);
7
7
  const __dirname = dirname(__filename);
8
+ /**
9
+ * Applies the proxy setting, the security headers, CORS and the robots.txt route.
10
+ *
11
+ * The security headers come first so that every response carries them,
12
+ * preflights and errors included. Body parsing is mounted separately by
13
+ * setupParsers, after the rate limiter.
14
+ *
15
+ * @param app - Express application to configure
16
+ */
8
17
  export function setupCors(app) {
9
18
  app.set('trust proxy', env.TRUSTED_PROXIES ?? 1);
19
+ if (!env.TRUSTED_PROXIES && process.env.NODE_ENV !== 'test') {
20
+ console.warn('TRUSTED_PROXIES is unset: client addresses are read assuming exactly one reverse proxy in front of the API.');
21
+ }
22
+ app.use((_req, res, next) => {
23
+ res.setHeader('X-Content-Type-Options', 'nosniff');
24
+ res.setHeader('X-Frame-Options', 'DENY');
25
+ next();
26
+ });
10
27
  app.use(cors({
11
28
  methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],
12
29
  exposedHeaders: [
@@ -18,14 +35,15 @@ export function setupCors(app) {
18
35
  'Retry-After',
19
36
  ],
20
37
  }));
38
+ app.use('/robots.txt', express.static(join(__dirname, '..', '..', 'robots.txt')));
39
+ }
40
+ /**
41
+ * Mounts the URL-encoded and JSON body parsers, both capped at 10 KB.
42
+ *
43
+ * @param app - Express application to configure
44
+ */
45
+ export function setupParsers(app) {
21
46
  app.use(express.urlencoded({ extended: true, limit: '10kb' }));
22
47
  app.use(express.json({ limit: '10kb' }));
23
- app.use((_req, res, next) => {
24
- res.setHeader('X-Content-Type-Options', 'nosniff');
25
- res.setHeader('X-Frame-Options', 'DENY');
26
- next();
27
- });
28
- app.use('/favicon.ico', express.static(join(__dirname, '..', '..', 'src', 'favicon.ico')));
29
- app.use('/robots.txt', express.static(join(__dirname, '..', '..', 'robots.txt')));
30
48
  }
31
49
  //# sourceMappingURL=cors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAyB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,UAAU,SAAS,CAAC,GAAY;IAClC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC;IAEjD,GAAG,CAAC,GAAG,CACH,IAAI,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;QACtD,cAAc,EAAE;YACZ,gBAAgB;YAChB,iBAAiB;YACjB,mBAAmB;YACnB,uBAAuB;YACvB,mBAAmB;YACnB,aAAa;SAChB;KACJ,CAAC,CACL,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/D,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAEzC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;QACnD,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC3F,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
1
+ {"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAyB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,GAAY;IAClC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC1D,OAAO,CAAC,IAAI,CACR,6GAA6G,CAChH,CAAC;IACN,CAAC;IAED,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;QACnD,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CACH,IAAI,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;QACtD,cAAc,EAAE;YACZ,gBAAgB;YAChB,iBAAiB;YACjB,mBAAmB;YACnB,uBAAuB;YACvB,mBAAmB;YACnB,aAAa;SAChB;KACJ,CAAC,CACL,CAAC;IAEF,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACrC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/D,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC"}