@maroonedsoftware/koa 1.2.1 → 1.4.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,6 +1,6 @@
1
1
  # @maroonedsoftware/koa
2
2
 
3
- Koa utilities and middleware for ServerKit: typed context, router, CORS, error handling, rate limiting, body parsing, and request-scoped DI via [injectkit](https://www.npmjs.com/package/injectkit).
3
+ Koa utilities and middleware for ServerKit: typed context, router, CORS, error handling, rate limiting, authentication, body parsing, and request-scoped DI via [injectkit](https://www.npmjs.com/package/injectkit).
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,14 +12,16 @@ Peer dependencies: `koa`, `@koa/router`, `@koa/cors`.
12
12
 
13
13
  ## Features
14
14
 
15
- - **ServerKitContext** — Koa context extended with `container`, `logger`, `requestId`, `correlationId`, and related request metadata
15
+ - **ServerKitContext** — Koa context extended with `container`, `logger`, `requestId`, `correlationId`, `authenticationContext`, and related request metadata
16
16
  - **ServerKitRouter** — Router typed for `ServerKitContext`
17
17
  - **ServerKitMiddleware** — Middleware type bound to `ServerKitContext`
18
18
  - **serverKitContextMiddleware** — Populates context with scoped container, logger, and request/correlation IDs
19
19
  - **corsMiddleware** — CORS headers with `'*'`, string, or RegExp origin matching
20
20
  - **errorMiddleware** — Central error handler; maps HTTP errors to status/body, 404 for unmatched routes, 500 for unknown errors
21
21
  - **rateLimiterMiddleware** — Per-IP rate limiting via `rate-limiter-flexible` (429 when exceeded)
22
+ - **authenticationMiddleware** — Resolves the `Authorization` header via `AuthenticationSchemeHandler` and populates `ctx.authenticationContext`
22
23
  - **bodyParserMiddleware** — Parses JSON, form, text, multipart, or raw body by allowed content types
24
+ - **defaultParserMappings** — Pre-built MIME-type-to-parser map for use with `bodyParserMiddleware`
23
25
 
24
26
  ## Usage
25
27
 
@@ -29,7 +31,13 @@ Peer dependencies: `koa`, `@koa/router`, `@koa/cors`.
29
31
  import Koa from 'koa';
30
32
  import { InjectKitRegistry } from 'injectkit';
31
33
  import { Logger, ConsoleLogger } from '@maroonedsoftware/logger';
32
- import { ServerKitRouter, serverKitContextMiddleware, corsMiddleware, errorMiddleware, bodyParserMiddleware } from '@maroonedsoftware/koa';
34
+ import {
35
+ ServerKitRouter,
36
+ serverKitContextMiddleware,
37
+ corsMiddleware,
38
+ errorMiddleware,
39
+ bodyParserMiddleware,
40
+ } from '@maroonedsoftware/koa';
33
41
 
34
42
  const diRegistry = new InjectKitRegistry();
35
43
  diRegistry.register(Logger).useClass(ConsoleLogger).asSingleton();
@@ -64,6 +72,37 @@ router.get('/api/users/:id', async ctx => {
64
72
  });
65
73
  ```
66
74
 
75
+ ### Authentication
76
+
77
+ `authenticationMiddleware` reads the `Authorization` header, delegates resolution to the `AuthenticationSchemeHandler` registered in the DI container, and populates `ctx.authenticationContext`. The header is deleted from `ctx.req.headers` immediately after reading so it cannot be captured by downstream logging.
78
+
79
+ ```typescript
80
+ import {
81
+ AuthenticationSchemeHandler,
82
+ AuthenticationHandlerMap,
83
+ } from '@maroonedsoftware/authentication';
84
+ import { authenticationMiddleware } from '@maroonedsoftware/koa';
85
+
86
+ // Register your scheme handlers in DI
87
+ diRegistry
88
+ .register(AuthenticationHandlerMap)
89
+ .useMap()
90
+ .add('Bearer', BearerAuthHandler);
91
+
92
+ diRegistry.register(AuthenticationSchemeHandler).asSingleton();
93
+
94
+ // Add to the middleware stack after serverKitContextMiddleware
95
+ app.use(serverKitContextMiddleware(container));
96
+ app.use(authenticationMiddleware());
97
+
98
+ // Access the resolved context in route handlers
99
+ router.get('/api/me', async ctx => {
100
+ const { subject, isAuthenticated } = ctx.authenticationContext;
101
+ if (!isAuthenticated) throw httpError(401);
102
+ ctx.body = { subject };
103
+ });
104
+ ```
105
+
67
106
  ### CORS
68
107
 
69
108
  ```typescript
@@ -110,28 +149,67 @@ router.post('/api/json', bodyParserMiddleware(['application/json']), async ctx =
110
149
  });
111
150
  ```
112
151
 
152
+ ### Custom parser mappings
153
+
154
+ `defaultParserMappings` is the built-in MIME-type-to-parser map used by `bodyParserMiddleware`. You can extend or replace it to register additional parsers:
155
+
156
+ ```typescript
157
+ import { defaultParserMappings, BinaryParser } from '@maroonedsoftware/koa';
158
+ import { ServerKitBodyParser } from '@maroonedsoftware/koa';
159
+
160
+ const customMappings = {
161
+ ...defaultParserMappings,
162
+ pdf: BinaryParser,
163
+ };
164
+
165
+ // Pass to bodyParserMiddleware via a custom ServerKitBodyParser instance
166
+ ```
167
+
168
+ The default mappings are:
169
+
170
+ | MIME subtype | Parser |
171
+ | -------------------- | --------------- |
172
+ | `json` | `JsonParser` |
173
+ | `application/*+json` | `JsonParser` |
174
+ | `urlencoded` | `FormParser` |
175
+ | `text` | `TextParser` |
176
+ | `multipart` | `MultipartParser` |
177
+
113
178
  ## API
114
179
 
115
180
  ### ServerKitContext
116
181
 
117
- | Property | Type | Description |
118
- | --------------- | ----------- | ------------------------------------ |
119
- | `container` | `Container` | Request-scoped injectkit container |
120
- | `logger` | `Logger` | Request-scoped logger |
121
- | `loggerName` | `string` | Logger name (e.g. request path) |
122
- | `userAgent` | `string` | `User-Agent` header value |
123
- | `correlationId` | `string` | From `X-Correlation-Id` or generated |
124
- | `requestId` | `string` | From `X-Request-Id` or generated |
182
+ | Property | Type | Description |
183
+ | ----------------------- | ----------------------- | ------------------------------------------------------ |
184
+ | `container` | `Container` | Request-scoped injectkit container |
185
+ | `logger` | `Logger` | Request-scoped logger |
186
+ | `loggerName` | `string` | Logger name (e.g. request path) |
187
+ | `userAgent` | `string` | `User-Agent` header value |
188
+ | `correlationId` | `string` | From `X-Correlation-Id` header or generated |
189
+ | `requestId` | `string` | From `X-Request-Id` header or generated |
190
+ | `rawBody` | `unknown` | Raw (unparsed) request body |
191
+ | `authenticationContext` | `AuthenticationContext` | Resolved authentication context; set by `authenticationMiddleware` |
125
192
 
126
193
  ### Middleware
127
194
 
128
- | Middleware | Description |
129
- | --------------------------------------- | ------------------------------------------------------------------------------------------------- |
130
- | `serverKitContextMiddleware(container)` | Sets `ctx.container`, `ctx.logger`, IDs; sets `X-Correlation-Id`, `X-Request-Id` response headers |
131
- | `corsMiddleware(options?)` | CORS via `@koa/cors`; `origin`: `'*'`, string, or `(string \| RegExp)[]` |
132
- | `errorMiddleware()` | Catches errors, maps HTTP errors to status/body, 404/500, emits app events |
133
- | `rateLimiterMiddleware(rateLimiter)` | Consumes one token per request by IP; throws 429 when exceeded |
134
- | `bodyParserMiddleware(contentTypes)` | Parses body by allowed MIME types; throws 400/411/415/422 on invalid input |
195
+ | Middleware | Description |
196
+ | --------------------------------------- | -------------------------------------------------------------------------------------------------- |
197
+ | `serverKitContextMiddleware(container)` | Sets `ctx.container`, `ctx.logger`, IDs; sets `X-Correlation-Id`, `X-Request-Id` response headers |
198
+ | `corsMiddleware(options?)` | CORS via `@koa/cors`; `origin`: `'*'`, string, or `(string \| RegExp)[]` |
199
+ | `errorMiddleware()` | Catches errors, maps HTTP errors to status/body, 404/500, emits app events |
200
+ | `rateLimiterMiddleware(rateLimiter)` | Consumes one token per request by IP; throws 429 when exceeded |
201
+ | `authenticationMiddleware()` | Resolves `Authorization` header via `AuthenticationSchemeHandler`; populates `ctx.authenticationContext` |
202
+ | `bodyParserMiddleware(contentTypes)` | Parses body by allowed MIME types; throws 400/411/415/422 on invalid input |
203
+
204
+ ### Parser options
205
+
206
+ Parser options classes are registered with InjectKit and can be configured in the DI container:
207
+
208
+ | Class | Key options |
209
+ | -------------------- | ---------------------------------------------- |
210
+ | `JsonParserOptions` | `strict`, `protoAction`, `reviver`, `encoding`, `limit` |
211
+ | `FormParserOptions` | `allowDots`, `depth`, `parameterLimit`, `encoding`, `limit` |
212
+ | `TextParserOptions` | `encoding`, `limit` |
135
213
 
136
214
  ## License
137
215
 
package/dist/index.d.ts CHANGED
@@ -14,5 +14,5 @@ export * from './parsers/text.parser.js';
14
14
  export * from './parsers/form.parser.js';
15
15
  export * from './parsers/multipart.parser.js';
16
16
  export * from './parsers/binary.parser.js';
17
- export * from './parsers.setup.js';
17
+ export * from './parsers/serverkit.default.parsers.js';
18
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,kDAAkD,CAAC;AACjE,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,kDAAkD,CAAC;AACjE,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC"}
package/dist/index.js CHANGED
@@ -121,7 +121,7 @@ var authenticationMiddleware = /* @__PURE__ */ __name(() => {
121
121
  ctx.authenticationContext = invalidAuthenticationContext;
122
122
  const authorizationHeader = ctx.req.headers.authorization;
123
123
  delete ctx.req.headers.authorization;
124
- const schemeHandler = ctx.serviceLocator.get(AuthenticationSchemeHandler);
124
+ const schemeHandler = ctx.container.get(AuthenticationSchemeHandler);
125
125
  ctx.authenticationContext = await schemeHandler.handle(authorizationHeader);
126
126
  await next();
127
127
  };
@@ -163,6 +163,13 @@ var ServerKitBodyParser = class {
163
163
  this.parsers = parsers;
164
164
  this.mimeTypes = unique(Array.from(this.parsers.keys()));
165
165
  }
166
+ /**
167
+ * Matches the request's `Content-Type` to a registered parser and delegates parsing.
168
+ *
169
+ * @param ctx - The current {@link ServerKitContext}; used to inspect `Content-Type` and access `ctx.req`.
170
+ * @returns The {@link ServerKitParserResult} from the matched parser.
171
+ * @throws HTTP 415 if no parser is registered for the request's content type.
172
+ */
166
173
  async parse(ctx) {
167
174
  const mimeType = ctx.request.is(this.mimeTypes);
168
175
  if (!mimeType) {
@@ -260,15 +267,36 @@ function _ts_metadata2(k, v) {
260
267
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
261
268
  }
262
269
  __name(_ts_metadata2, "_ts_metadata");
270
+ var JsonParserOptions = class {
271
+ static {
272
+ __name(this, "JsonParserOptions");
273
+ }
274
+ strict;
275
+ protoAction;
276
+ reviver;
277
+ encoding;
278
+ limit;
279
+ length;
280
+ };
281
+ JsonParserOptions = _ts_decorate3([
282
+ Injectable3()
283
+ ], JsonParserOptions);
263
284
  var strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/;
264
285
  var JsonParser = class extends ServerKitParser {
265
286
  static {
266
287
  __name(this, "JsonParser");
267
288
  }
268
289
  options;
269
- constructor(options = {}) {
290
+ constructor(options) {
270
291
  super(), this.options = options;
271
292
  }
293
+ /**
294
+ * Reads, decompresses, and JSON-parses the request body.
295
+ *
296
+ * @param req - Incoming HTTP request whose body will be consumed.
297
+ * @returns `{ parsed: <object|array|undefined>, raw: <original string> }`.
298
+ * @throws HTTP 400 on malformed JSON or strict-mode violation.
299
+ */
272
300
  async parse(req) {
273
301
  const len = req.headers["content-length"];
274
302
  const contentEncoding = req.headers["content-encoding"] || "identity";
@@ -343,14 +371,28 @@ function _ts_metadata3(k, v) {
343
371
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
344
372
  }
345
373
  __name(_ts_metadata3, "_ts_metadata");
374
+ var TextParserOptions = class {
375
+ static {
376
+ __name(this, "TextParserOptions");
377
+ }
378
+ encoding;
379
+ limit;
380
+ length;
381
+ };
346
382
  var TextParser = class extends ServerKitParser {
347
383
  static {
348
384
  __name(this, "TextParser");
349
385
  }
350
386
  options;
351
- constructor(options = {}) {
387
+ constructor(options) {
352
388
  super(), this.options = options;
353
389
  }
390
+ /**
391
+ * Reads and decompresses the request body into a string.
392
+ *
393
+ * @param req - Incoming HTTP request whose body will be consumed.
394
+ * @returns `{ parsed: <string>, raw: <same string> }`.
395
+ */
354
396
  async parse(req) {
355
397
  const len = req.headers["content-length"];
356
398
  const contentEncoding = req.headers["content-encoding"] || "identity";
@@ -393,14 +435,35 @@ function _ts_metadata4(k, v) {
393
435
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
394
436
  }
395
437
  __name(_ts_metadata4, "_ts_metadata");
438
+ var FormParserOptions = class {
439
+ static {
440
+ __name(this, "FormParserOptions");
441
+ }
442
+ encoding;
443
+ limit;
444
+ length;
445
+ allowDots;
446
+ depth;
447
+ parameterLimit;
448
+ };
449
+ FormParserOptions = _ts_decorate5([
450
+ Injectable5()
451
+ ], FormParserOptions);
396
452
  var FormParser = class extends ServerKitParser {
397
453
  static {
398
454
  __name(this, "FormParser");
399
455
  }
400
456
  options;
401
- constructor(options = {}) {
457
+ constructor(options) {
402
458
  super(), this.options = options;
403
459
  }
460
+ /**
461
+ * Reads, decompresses, and URL-decodes the request body.
462
+ *
463
+ * @param req - Incoming HTTP request whose body will be consumed.
464
+ * @returns `{ parsed: <object>, raw: <original url-encoded string> }`.
465
+ * @throws HTTP 400 if parsing fails.
466
+ */
404
467
  async parse(req) {
405
468
  const len = req.headers["content-length"];
406
469
  const contentEncoding = req.headers["content-encoding"] || "identity";
@@ -444,6 +507,12 @@ var MultipartParser = class extends ServerKitParser {
444
507
  static {
445
508
  __name(this, "MultipartParser");
446
509
  }
510
+ /**
511
+ * Creates a {@link MultipartBody} around the request stream.
512
+ *
513
+ * @param req - Incoming HTTP request containing the multipart body.
514
+ * @returns `{ parsed: MultipartBody, raw: undefined }`.
515
+ */
447
516
  async parse(req) {
448
517
  return {
449
518
  parsed: new MultipartBody(req),
@@ -470,6 +539,12 @@ var BinaryParser = class extends ServerKitParser {
470
539
  static {
471
540
  __name(this, "BinaryParser");
472
541
  }
542
+ /**
543
+ * Buffers the (optionally compressed) request body into a `Buffer`.
544
+ *
545
+ * @param req - Incoming HTTP request to read.
546
+ * @returns `{ parsed: Buffer, raw: undefined }`.
547
+ */
473
548
  async parse(req) {
474
549
  return {
475
550
  parsed: await raw4(inflate4(req)),
@@ -481,55 +556,33 @@ BinaryParser = _ts_decorate7([
481
556
  Injectable7()
482
557
  ], BinaryParser);
483
558
 
484
- // src/parsers.setup.ts
559
+ // src/parsers/serverkit.default.parsers.ts
485
560
  var defaultParserMappings = {
486
- "json": JsonParser,
561
+ json: JsonParser,
487
562
  "application/*+json": JsonParser,
488
- "urlencoded": FormParser,
489
- "text": TextParser,
490
- "multipart": MultipartParser
563
+ urlencoded: FormParser,
564
+ text: TextParser,
565
+ multipart: MultipartParser
491
566
  };
492
- var defaultParserClasses = [
493
- JsonParser,
494
- FormParser,
495
- TextParser,
496
- MultipartParser,
497
- BinaryParser
498
- ];
499
- var setupParsers = /* @__PURE__ */ __name((registry, overrides = {}) => {
500
- const merged = {
501
- ...defaultParserMappings,
502
- ...overrides
503
- };
504
- const mapRegistration = registry.register(ServerKitParserMappings).useMap(ServerKitParserMappings);
505
- for (const [key, parser] of Object.entries(merged)) {
506
- mapRegistration.set(key, parser);
507
- }
508
- const parserClasses = /* @__PURE__ */ new Set([
509
- ...defaultParserClasses,
510
- ...Object.values(overrides)
511
- ]);
512
- for (const parserClass of parserClasses) {
513
- registry.register(parserClass).useClass(parserClass).asSingleton();
514
- }
515
- registry.register(ServerKitBodyParser).useClass(ServerKitBodyParser).asSingleton();
516
- }, "setupParsers");
517
567
  export {
518
568
  BinaryParser,
519
569
  FormParser,
570
+ FormParserOptions,
520
571
  JsonParser,
572
+ JsonParserOptions,
521
573
  MultipartParser,
522
574
  ServerKitBodyParser,
523
575
  ServerKitParser,
524
576
  ServerKitParserMappings,
525
577
  ServerKitRouter,
526
578
  TextParser,
579
+ TextParserOptions,
527
580
  authenticationMiddleware,
528
581
  bodyParserMiddleware,
529
582
  corsMiddleware,
583
+ defaultParserMappings,
530
584
  errorMiddleware,
531
585
  rateLimiterMiddleware,
532
- serverKitContextMiddleware,
533
- setupParsers
586
+ serverKitContextMiddleware
534
587
  };
535
588
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/serverkit.router.ts","../src/middleware/server/cors.middleware.ts","../src/middleware/server/error.middleware.ts","../src/middleware/server/rate.limiter.middleware.ts","../src/middleware/server/serverkit.context.middleware.ts","../src/middleware/server/authentication.middleware.ts","../src/middleware/router/body.parser.middleware.ts","../src/serverkit.bodyparser.ts","../src/parsers/serverkit.parser.ts","../src/parsers/json.parser.ts","../src/parsers/text.parser.ts","../src/parsers/form.parser.ts","../src/parsers/multipart.parser.ts","../src/parsers/binary.parser.ts","../src/parsers.setup.ts"],"sourcesContent":["import { DefaultState } from 'koa';\nimport Router from '@koa/router';\nimport { ServerKitContext } from './serverkit.context.js';\n\n/**\n * Creates a new Koa router typed for ServerKit state and context.\n * Use with {@link ServerKitContext} for full typing of `ctx` in route handlers.\n *\n * @typeParam StateT - Koa state type (defaults to `DefaultState`).\n * @typeParam ContextT - Context type (defaults to `ServerKitContext`).\n * @returns A new {@link Router} instance.\n */\nexport const ServerKitRouter = <StateT = DefaultState, ContextT = ServerKitContext>() => new Router<StateT, ContextT>();\n","import cors from '@koa/cors';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { Context } from 'koa';\n\n/**\n * CORS options for {@link corsMiddleware}.\n * Extends `@koa/cors` options with an `origin` that may be a string or array of strings/RegExps.\n */\nexport interface CorsOptions extends Omit<cors.Options, 'origin'> {\n /** Allowed origin(s): `'*'`, a single origin string, or an array of strings/RegExps to match. */\n origin?: string | (string | RegExp)[];\n}\n\n/**\n * Adds CORS headers to responses using `@koa/cors` with ServerKit-compatible origin matching.\n * Supports `'*'`, exact string origins, and RegExp patterns.\n *\n * @param options - Optional {@link CorsOptions}; defaults to `GET,HEAD,PUT,POST,DELETE,PATCH` methods.\n * @returns {@link ServerKitMiddleware} that applies CORS headers.\n */\nexport const corsMiddleware = (options?: CorsOptions): ServerKitMiddleware => {\n // return the request origin as its own matcher to support RegExp\n const originMatcher = (ctx: Context): string => {\n const origin = ctx.get('origin');\n const matchers = options?.origin ?? ['*'];\n for (const matcher of matchers) {\n if (matcher === '*') {\n return origin;\n }\n\n if (typeof matcher === 'string') {\n if (matcher === origin) {\n return origin;\n }\n continue;\n }\n\n if (matcher.test(origin)) {\n return origin;\n }\n }\n\n // return the zero value to prevent matches\n return '';\n };\n\n return cors({\n ...options,\n origin: originMatcher,\n allowMethods: options?.allowMethods ?? 'GET,HEAD,PUT,POST,DELETE,PATCH',\n secureContext: options?.secureContext ?? false,\n keepHeadersOnError: options?.keepHeadersOnError ?? false,\n privateNetworkAccess: options?.privateNetworkAccess ?? false,\n });\n};\n","import { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { IsHttpError } from '@maroonedsoftware/errors';\n\n/**\n * Central error handler: catches thrown errors, sets status/body from HTTP errors,\n * returns 404 for unmatched routes, and 500 for unknown errors.\n * Emits `error` or `warn` on the app for logging.\n *\n * @returns {@link ServerKitMiddleware} that wraps the stack in try/catch and normalizes responses.\n */\nexport const errorMiddleware = (): ServerKitMiddleware => {\n return async (ctx, next) => {\n try {\n await next();\n if (ctx.status === 404 && !ctx.body) {\n const body = {\n statusCode: 404,\n message: 'Not Found',\n details: { url: ctx.URL.toString() },\n };\n ctx.status = 404;\n ctx.body = body;\n ctx.app.emit('warn', body, ctx);\n }\n } catch (error) {\n if (IsHttpError(error)) {\n ctx.status = error.statusCode;\n ctx.body = {\n statusCode: error.statusCode,\n message: error.message,\n details: error.details,\n };\n if (error.headers) {\n for (const entry of Object.entries(error.headers)) {\n ctx.set(entry[0], entry[1]);\n }\n }\n } else {\n ctx.status = 500;\n ctx.body = {\n statusCode: 500,\n message: 'Internal Server Error',\n };\n }\n\n ctx.app.emit('error', error, ctx);\n }\n };\n};\n","import { RateLimiterAbstract } from 'rate-limiter-flexible';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { httpError } from '@maroonedsoftware/errors';\n\n/**\n * Enforces rate limiting per client IP using a `rate-limiter-flexible` instance.\n * Consumes one token per request; throws HTTP 429 when the limit is exceeded.\n *\n * @param rateLimiter - A {@link RateLimiterAbstract} instance (e.g. `RateLimiterMemory`, `RateLimiterRedis`).\n * @returns {@link ServerKitMiddleware} that consumes a token and continues or throws 429.\n */\nexport const rateLimiterMiddleware = (rateLimiter: RateLimiterAbstract): ServerKitMiddleware => {\n return async (ctx, next) => {\n try {\n await rateLimiter.consume(ctx.ip);\n } catch (error) {\n throw httpError(429).withCause(error as Error);\n }\n\n await next();\n };\n};\n","import crypto from 'crypto';\nimport { Container } from 'injectkit';\nimport { Logger } from '@maroonedsoftware/logger';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\n\n/**\n * Populates {@link ServerKitContext} for each request: scoped container, logger,\n * logger name, user-agent, correlation ID, and request ID.\n * Reads or generates `X-Correlation-Id` and `X-Request-Id` and sets response headers.\n * Should be applied early so downstream middleware and routes can use `ctx.container` and `ctx.logger`.\n *\n * @param container - Root injectkit {@link Container} used to create a scoped container and resolve {@link Logger}.\n * @returns {@link ServerKitMiddleware} that attaches ServerKit context to `ctx`.\n */\nexport const serverKitContextMiddleware = (container: Container): ServerKitMiddleware => {\n return async (ctx, next) => {\n ctx.container = container.createScopedContainer();\n ctx.logger = container.get(Logger);\n ctx.loggerName = ctx.path;\n\n ctx.userAgent = ctx.get('user-agent') ?? '';\n ctx.correlationId = ctx.get('x-correlation-id') ?? crypto.randomUUID();\n ctx.requestId = ctx.get('x-request-id') ?? crypto.randomUUID();\n\n ctx.headers['x-correlation-id'] = ctx.correlationId;\n ctx.set('x-correlation-id', ctx.correlationId);\n\n ctx.headers['x-request-id'] = ctx.requestId;\n ctx.set('x-request-id', ctx.requestId);\n\n await next();\n };\n};\n","import { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { AuthenticationSchemeHandler, invalidAuthenticationContext } from '@maroonedsoftware/authentication';\n\n/**\n * Resolves the `Authorization` request header into an {@link AuthenticationContext}\n * and attaches it to `ctx.authenticationContext`.\n *\n * The header is immediately removed from `ctx.req.headers` after being read so it\n * cannot be accidentally captured by downstream logging or serialization.\n *\n * Resolution is delegated to the {@link AuthenticationSchemeHandler} registered in\n * the DI container. `ctx.authenticationContext` is initialised to\n * {@link invalidAuthenticationContext} before delegation, ensuring that any error\n * thrown by the scheme handler leaves the context in a safe, unauthenticated state.\n *\n * @returns A {@link ServerKitMiddleware} that populates `ctx.authenticationContext`.\n *\n * @example\n * ```typescript\n * app.use(authenticationMiddleware());\n * ```\n */\nexport const authenticationMiddleware = (): ServerKitMiddleware => {\n return async (ctx, next) => {\n ctx.authenticationContext = invalidAuthenticationContext; // bad initial state so it will fail verification\n\n // NOTE: we delete the auth headers on the request here to ensure we don't accidentally log it\n const authorizationHeader = ctx.req.headers.authorization;\n delete ctx.req.headers.authorization;\n\n const schemeHandler = ctx.serviceLocator.get(AuthenticationSchemeHandler);\n\n ctx.authenticationContext = await schemeHandler.handle(authorizationHeader);\n\n await next();\n };\n};\n","import { httpError, IsHttpError } from '@maroonedsoftware/errors';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { ServerKitBodyParser } from '../../serverkit.bodyparser.js';\n\n/**\n * Parses the request body based on `Content-Type` and assigns it to `ctx.body`.\n * Rejects requests with unexpected or unsupported content types.\n *\n * Supported types: JSON, URL-encoded form, text, multipart, PDF (raw buffer).\n * Requires a body when `contentTypes` is non-empty; otherwise rejects bodies.\n *\n * @param contentTypes - Allowed MIME types (e.g. `['application/json', 'application/x-www-form-urlencoded']`).\n * Use an empty array to disallow any request body.\n * @returns {@link ServerKitMiddleware} that parses the body and sets `ctx.body`.\n * @throws HTTP 400 if body is present when no content types are allowed.\n * @throws HTTP 411 if body is required but missing.\n * @throws HTTP 415 if `Content-Type` is not in `contentTypes`.\n * @throws HTTP 422 if body is invalid or media type is unsupported.\n */\nexport const bodyParserMiddleware = (contentTypes: string[]): ServerKitMiddleware => {\n return async (ctx, next) => {\n if (contentTypes.length === 0) {\n if (ctx.request.length > 0) {\n throw httpError(400).withDetails({ body: 'Unexpected body' });\n }\n } else {\n if (ctx.request.length > 0) {\n if (!ctx.request.is(contentTypes)) {\n throw httpError(415).withDetails({\n 'content-type': `must be ${contentTypes.length > 1 ? 'one of ' : ''}${contentTypes.join(', ')}`,\n value: ctx.request.type,\n });\n }\n\n try {\n const parser = ctx.container.get(ServerKitBodyParser);\n const result = await parser.parse(ctx);\n ctx.body = result.parsed;\n ctx.rawBody = result.raw;\n } catch (error) {\n if (IsHttpError(error)) {\n throw error;\n }\n throw httpError(422)\n .withCause(error as Error)\n .withDetails({ body: 'Invalid request body format' });\n }\n } else {\n throw httpError(411);\n }\n }\n await next();\n };\n};\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './parsers/serverkit.parser.js';\nimport { ServerKitContext } from './serverkit.context.js';\nimport { unique } from '@maroonedsoftware/utilities';\nimport { httpError } from '@maroonedsoftware/errors';\n\n@Injectable()\nexport class ServerKitParserMappings extends Map<string, ServerKitParser> {}\n\n@Injectable()\nexport class ServerKitBodyParser {\n private readonly mimeTypes: string[];\n constructor(private readonly parsers: ServerKitParserMappings) {\n this.mimeTypes = unique(Array.from(this.parsers.keys()));\n }\n\n async parse(ctx: ServerKitContext): Promise<ServerKitParserResult> {\n const mimeType = ctx.request.is(this.mimeTypes);\n if (!mimeType) {\n throw httpError(415).withDetails({ body: 'Unsupported media type' });\n }\n const parser = this.parsers.get(mimeType);\n if (!parser) {\n throw httpError(415).withDetails({ body: 'Unsupported media type' });\n }\n return parser.parse(ctx.req);\n }\n}\n","import { IncomingMessage } from 'http';\nimport { Injectable } from 'injectkit';\n\nexport type ServerKitParserResult = {\n parsed: unknown;\n raw: unknown;\n};\n\n@Injectable()\nexport abstract class ServerKitParser {\n abstract parse(req: IncomingMessage): Promise<ServerKitParserResult>;\n}\n","import { parse, Reviver } from '@hapi/bourne';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\nimport { httpError } from '@maroonedsoftware/errors';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { Injectable } from 'injectkit';\n\nexport type JsonParserOptions = raw.Options & {\n strict?: boolean;\n protoAction?: 'error' | 'remove' | 'ignore';\n reviver?: Reviver;\n};\n\n// Allowed whitespace is defined in RFC 7159\n// http://www.rfc-editor.org/rfc/rfc7159.txt\n/* eslint-disable-next-line no-control-regex */\nconst strictJSONReg = /^[\\x20\\x09\\x0a\\x0d]*(\\[|\\{)/;\n\n@Injectable()\nexport class JsonParser extends ServerKitParser {\n constructor(private readonly options: JsonParserOptions = {}) {\n super();\n }\n\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '1mb';\n\n const strict = this.options.strict ?? true;\n const protoAction = this.options.protoAction ?? 'error';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n const doParse = (str: string) => {\n try {\n if (this.options.reviver) {\n return parse(str, this.options.reviver, { protoAction });\n }\n return parse(str, { protoAction });\n } catch (err) {\n throw httpError(400).withCause(err as Error);\n }\n };\n\n if (!strict) {\n return str ? { parsed: doParse(str), raw: str } : { parsed: undefined, raw: str };\n } else if (!str) {\n return { parsed: undefined, raw: str };\n } else if (!strictJSONReg.test(str)) {\n throw httpError(400).withDetails({ body: 'Invalid JSON, only supports object and array' });\n }\n return { parsed: doParse(str), raw: str };\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\nexport type TextParserOptions = raw.Options;\n\n@Injectable()\nexport class TextParser extends ServerKitParser {\n constructor(private readonly options: TextParserOptions = {}) {\n super();\n }\n\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '1mb';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n return { parsed: str, raw: str };\n }\n}\n","import { httpError } from '@maroonedsoftware/errors';\nimport { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport { parse, IParseOptions } from 'qs';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\nexport type FormParserOptions = raw.Options & IParseOptions;\n\n@Injectable()\nexport class FormParser extends ServerKitParser {\n constructor(private readonly options: FormParserOptions = {}) {\n super();\n }\n\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '56kb';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n try {\n return { parsed: parse(str, this.options), raw: str };\n } catch (err) {\n throw httpError(400).withCause(err as Error);\n }\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport { MultipartBody } from '@maroonedsoftware/multipart';\n\n@Injectable()\nexport class MultipartParser extends ServerKitParser {\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n return { parsed: new MultipartBody(req), raw: undefined };\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\n@Injectable()\nexport class BinaryParser extends ServerKitParser {\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n return { parsed: await raw(inflate(req)), raw: undefined };\n }\n}\n","import { Constructor, Registry } from 'injectkit';\nimport { ServerKitBodyParser, ServerKitParserMappings } from './serverkit.bodyparser.js';\nimport { ServerKitParser } from './parsers/serverkit.parser.js';\nimport { JsonParser } from './parsers/json.parser.js';\nimport { TextParser } from './parsers/text.parser.js';\nimport { FormParser } from './parsers/form.parser.js';\nimport { MultipartParser } from './parsers/multipart.parser.js';\nimport { BinaryParser } from './parsers/binary.parser.js';\n\nexport type ParserMappingOverrides = Record<string, Constructor<ServerKitParser>>;\n\nconst defaultParserMappings: ParserMappingOverrides = {\n 'json': JsonParser,\n 'application/*+json': JsonParser,\n 'urlencoded': FormParser,\n 'text': TextParser,\n 'multipart': MultipartParser,\n};\n\nconst defaultParserClasses: Constructor<ServerKitParser>[] = [\n JsonParser,\n FormParser,\n TextParser,\n MultipartParser,\n BinaryParser,\n];\n\nexport const setupParsers = (registry: Registry, overrides: ParserMappingOverrides = {}) => {\n const merged = { ...defaultParserMappings, ...overrides };\n\n const mapRegistration = registry\n .register(ServerKitParserMappings)\n .useMap(ServerKitParserMappings);\n\n for (const [key, parser] of Object.entries(merged)) {\n mapRegistration.set(key, parser);\n }\n\n const parserClasses = new Set<Constructor<ServerKitParser>>([\n ...defaultParserClasses,\n ...Object.values(overrides),\n ]);\n\n for (const parserClass of parserClasses) {\n registry.register(parserClass).useClass(parserClass).asSingleton();\n }\n\n registry.register(ServerKitBodyParser).useClass(ServerKitBodyParser).asSingleton();\n};\n"],"mappings":"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","names":["Router","ServerKitRouter","Router","cors","corsMiddleware","options","originMatcher","ctx","origin","get","matchers","matcher","test","cors","allowMethods","secureContext","keepHeadersOnError","privateNetworkAccess","IsHttpError","errorMiddleware","ctx","next","status","body","statusCode","message","details","url","URL","toString","app","emit","error","IsHttpError","headers","entry","Object","entries","set","httpError","rateLimiterMiddleware","rateLimiter","ctx","next","consume","ip","error","httpError","withCause","crypto","Logger","serverKitContextMiddleware","container","ctx","next","createScopedContainer","logger","get","Logger","loggerName","path","userAgent","correlationId","crypto","randomUUID","requestId","headers","set","AuthenticationSchemeHandler","invalidAuthenticationContext","authenticationMiddleware","ctx","next","authenticationContext","invalidAuthenticationContext","authorizationHeader","req","headers","authorization","schemeHandler","serviceLocator","get","AuthenticationSchemeHandler","handle","httpError","IsHttpError","Injectable","unique","httpError","ServerKitParserMappings","Map","ServerKitBodyParser","mimeTypes","parsers","unique","Array","from","keys","parse","ctx","mimeType","request","is","httpError","withDetails","body","parser","get","req","bodyParserMiddleware","contentTypes","ctx","next","length","request","httpError","withDetails","body","is","join","value","type","parser","container","get","ServerKitBodyParser","result","parse","parsed","rawBody","raw","error","IsHttpError","withCause","Injectable","ServerKitParser","parse","raw","inflate","httpError","Injectable","strictJSONReg","JsonParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","length","undefined","encoding","limit","strict","protoAction","str","raw","inflate","doParse","reviver","err","httpError","withCause","parsed","test","withDetails","body","Injectable","raw","inflate","TextParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","length","undefined","encoding","limit","str","raw","inflate","parsed","httpError","Injectable","parse","raw","inflate","FormParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","length","undefined","encoding","limit","str","raw","inflate","parsed","err","httpError","withCause","Injectable","MultipartBody","MultipartParser","ServerKitParser","parse","req","parsed","MultipartBody","raw","undefined","Injectable","raw","inflate","BinaryParser","ServerKitParser","parse","req","parsed","raw","inflate","undefined","defaultParserMappings","JsonParser","FormParser","TextParser","MultipartParser","defaultParserClasses","BinaryParser","setupParsers","registry","overrides","merged","mapRegistration","register","ServerKitParserMappings","useMap","key","parser","Object","entries","set","parserClasses","Set","values","parserClass","useClass","asSingleton","ServerKitBodyParser"]}
1
+ {"version":3,"sources":["../src/serverkit.router.ts","../src/middleware/server/cors.middleware.ts","../src/middleware/server/error.middleware.ts","../src/middleware/server/rate.limiter.middleware.ts","../src/middleware/server/serverkit.context.middleware.ts","../src/middleware/server/authentication.middleware.ts","../src/middleware/router/body.parser.middleware.ts","../src/serverkit.bodyparser.ts","../src/parsers/serverkit.parser.ts","../src/parsers/json.parser.ts","../src/parsers/text.parser.ts","../src/parsers/form.parser.ts","../src/parsers/multipart.parser.ts","../src/parsers/binary.parser.ts","../src/parsers/serverkit.default.parsers.ts"],"sourcesContent":["import { DefaultState } from 'koa';\nimport Router from '@koa/router';\nimport { ServerKitContext } from './serverkit.context.js';\n\n/**\n * Creates a new Koa router typed for ServerKit state and context.\n * Use with {@link ServerKitContext} for full typing of `ctx` in route handlers.\n *\n * @typeParam StateT - Koa state type (defaults to `DefaultState`).\n * @typeParam ContextT - Context type (defaults to `ServerKitContext`).\n * @returns A new {@link Router} instance.\n */\nexport const ServerKitRouter = <StateT = DefaultState, ContextT = ServerKitContext>() => new Router<StateT, ContextT>();\n","import cors from '@koa/cors';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { Context } from 'koa';\n\n/**\n * CORS options for {@link corsMiddleware}.\n * Extends `@koa/cors` options with an `origin` that may be a string or array of strings/RegExps.\n */\nexport interface CorsOptions extends Omit<cors.Options, 'origin'> {\n /** Allowed origin(s): `'*'`, a single origin string, or an array of strings/RegExps to match. */\n origin?: string | (string | RegExp)[];\n}\n\n/**\n * Adds CORS headers to responses using `@koa/cors` with ServerKit-compatible origin matching.\n * Supports `'*'`, exact string origins, and RegExp patterns.\n *\n * @param options - Optional {@link CorsOptions}; defaults to `GET,HEAD,PUT,POST,DELETE,PATCH` methods.\n * @returns {@link ServerKitMiddleware} that applies CORS headers.\n */\nexport const corsMiddleware = (options?: CorsOptions): ServerKitMiddleware => {\n // return the request origin as its own matcher to support RegExp\n const originMatcher = (ctx: Context): string => {\n const origin = ctx.get('origin');\n const matchers = options?.origin ?? ['*'];\n for (const matcher of matchers) {\n if (matcher === '*') {\n return origin;\n }\n\n if (typeof matcher === 'string') {\n if (matcher === origin) {\n return origin;\n }\n continue;\n }\n\n if (matcher.test(origin)) {\n return origin;\n }\n }\n\n // return the zero value to prevent matches\n return '';\n };\n\n return cors({\n ...options,\n origin: originMatcher,\n allowMethods: options?.allowMethods ?? 'GET,HEAD,PUT,POST,DELETE,PATCH',\n secureContext: options?.secureContext ?? false,\n keepHeadersOnError: options?.keepHeadersOnError ?? false,\n privateNetworkAccess: options?.privateNetworkAccess ?? false,\n });\n};\n","import { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { IsHttpError } from '@maroonedsoftware/errors';\n\n/**\n * Central error handler: catches thrown errors, sets status/body from HTTP errors,\n * returns 404 for unmatched routes, and 500 for unknown errors.\n * Emits `error` or `warn` on the app for logging.\n *\n * @returns {@link ServerKitMiddleware} that wraps the stack in try/catch and normalizes responses.\n */\nexport const errorMiddleware = (): ServerKitMiddleware => {\n return async (ctx, next) => {\n try {\n await next();\n if (ctx.status === 404 && !ctx.body) {\n const body = {\n statusCode: 404,\n message: 'Not Found',\n details: { url: ctx.URL.toString() },\n };\n ctx.status = 404;\n ctx.body = body;\n ctx.app.emit('warn', body, ctx);\n }\n } catch (error) {\n if (IsHttpError(error)) {\n ctx.status = error.statusCode;\n ctx.body = {\n statusCode: error.statusCode,\n message: error.message,\n details: error.details,\n };\n if (error.headers) {\n for (const entry of Object.entries(error.headers)) {\n ctx.set(entry[0], entry[1]);\n }\n }\n } else {\n ctx.status = 500;\n ctx.body = {\n statusCode: 500,\n message: 'Internal Server Error',\n };\n }\n\n ctx.app.emit('error', error, ctx);\n }\n };\n};\n","import { RateLimiterAbstract } from 'rate-limiter-flexible';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { httpError } from '@maroonedsoftware/errors';\n\n/**\n * Enforces rate limiting per client IP using a `rate-limiter-flexible` instance.\n * Consumes one token per request; throws HTTP 429 when the limit is exceeded.\n *\n * @param rateLimiter - A {@link RateLimiterAbstract} instance (e.g. `RateLimiterMemory`, `RateLimiterRedis`).\n * @returns {@link ServerKitMiddleware} that consumes a token and continues or throws 429.\n */\nexport const rateLimiterMiddleware = (rateLimiter: RateLimiterAbstract): ServerKitMiddleware => {\n return async (ctx, next) => {\n try {\n await rateLimiter.consume(ctx.ip);\n } catch (error) {\n throw httpError(429).withCause(error as Error);\n }\n\n await next();\n };\n};\n","import crypto from 'crypto';\nimport { Container } from 'injectkit';\nimport { Logger } from '@maroonedsoftware/logger';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\n\n/**\n * Populates {@link ServerKitContext} for each request: scoped container, logger,\n * logger name, user-agent, correlation ID, and request ID.\n * Reads or generates `X-Correlation-Id` and `X-Request-Id` and sets response headers.\n * Should be applied early so downstream middleware and routes can use `ctx.container` and `ctx.logger`.\n *\n * @param container - Root injectkit {@link Container} used to create a scoped container and resolve {@link Logger}.\n * @returns {@link ServerKitMiddleware} that attaches ServerKit context to `ctx`.\n */\nexport const serverKitContextMiddleware = (container: Container): ServerKitMiddleware => {\n return async (ctx, next) => {\n ctx.container = container.createScopedContainer();\n ctx.logger = container.get(Logger);\n ctx.loggerName = ctx.path;\n\n ctx.userAgent = ctx.get('user-agent') ?? '';\n ctx.correlationId = ctx.get('x-correlation-id') ?? crypto.randomUUID();\n ctx.requestId = ctx.get('x-request-id') ?? crypto.randomUUID();\n\n ctx.headers['x-correlation-id'] = ctx.correlationId;\n ctx.set('x-correlation-id', ctx.correlationId);\n\n ctx.headers['x-request-id'] = ctx.requestId;\n ctx.set('x-request-id', ctx.requestId);\n\n await next();\n };\n};\n","import { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { AuthenticationSchemeHandler, invalidAuthenticationContext } from '@maroonedsoftware/authentication';\n\n/**\n * Resolves the `Authorization` request header into an {@link AuthenticationContext}\n * and attaches it to `ctx.authenticationContext`.\n *\n * The header is immediately removed from `ctx.req.headers` after being read so it\n * cannot be accidentally captured by downstream logging or serialization.\n *\n * Resolution is delegated to the {@link AuthenticationSchemeHandler} registered in\n * the DI container. `ctx.authenticationContext` is initialised to\n * {@link invalidAuthenticationContext} before delegation, ensuring that any error\n * thrown by the scheme handler leaves the context in a safe, unauthenticated state.\n *\n * @returns A {@link ServerKitMiddleware} that populates `ctx.authenticationContext`.\n *\n * @example\n * ```typescript\n * app.use(authenticationMiddleware());\n * ```\n */\nexport const authenticationMiddleware = (): ServerKitMiddleware => {\n return async (ctx, next) => {\n ctx.authenticationContext = invalidAuthenticationContext; // bad initial state so it will fail verification\n\n // NOTE: we delete the auth headers on the request here to ensure we don't accidentally log it\n const authorizationHeader = ctx.req.headers.authorization;\n delete ctx.req.headers.authorization;\n\n const schemeHandler = ctx.container.get(AuthenticationSchemeHandler);\n\n ctx.authenticationContext = await schemeHandler.handle(authorizationHeader);\n\n await next();\n };\n};\n","import { httpError, IsHttpError } from '@maroonedsoftware/errors';\nimport { ServerKitMiddleware } from '../../serverkit.middleware.js';\nimport { ServerKitBodyParser } from '../../serverkit.bodyparser.js';\n\n/**\n * Parses the request body based on `Content-Type` and assigns it to `ctx.body`.\n * Rejects requests with unexpected or unsupported content types.\n *\n * Supported types: JSON, URL-encoded form, text, multipart, PDF (raw buffer).\n * Requires a body when `contentTypes` is non-empty; otherwise rejects bodies.\n *\n * @param contentTypes - Allowed MIME types (e.g. `['application/json', 'application/x-www-form-urlencoded']`).\n * Use an empty array to disallow any request body.\n * @returns {@link ServerKitMiddleware} that parses the body and sets `ctx.body`.\n * @throws HTTP 400 if body is present when no content types are allowed.\n * @throws HTTP 411 if body is required but missing.\n * @throws HTTP 415 if `Content-Type` is not in `contentTypes`.\n * @throws HTTP 422 if body is invalid or media type is unsupported.\n */\nexport const bodyParserMiddleware = (contentTypes: string[]): ServerKitMiddleware => {\n return async (ctx, next) => {\n if (contentTypes.length === 0) {\n if (ctx.request.length > 0) {\n throw httpError(400).withDetails({ body: 'Unexpected body' });\n }\n } else {\n if (ctx.request.length > 0) {\n if (!ctx.request.is(contentTypes)) {\n throw httpError(415).withDetails({\n 'content-type': `must be ${contentTypes.length > 1 ? 'one of ' : ''}${contentTypes.join(', ')}`,\n value: ctx.request.type,\n });\n }\n\n try {\n const parser = ctx.container.get(ServerKitBodyParser);\n const result = await parser.parse(ctx);\n ctx.body = result.parsed;\n ctx.rawBody = result.raw;\n } catch (error) {\n if (IsHttpError(error)) {\n throw error;\n }\n throw httpError(422)\n .withCause(error as Error)\n .withDetails({ body: 'Invalid request body format' });\n }\n } else {\n throw httpError(411);\n }\n }\n await next();\n };\n};\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './parsers/serverkit.parser.js';\nimport { ServerKitContext } from './serverkit.context.js';\nimport { unique } from '@maroonedsoftware/utilities';\nimport { httpError } from '@maroonedsoftware/errors';\n\n/**\n * DI-injectable map of MIME subtypes to {@link ServerKitParser} instances.\n *\n * Register parser instances against their MIME subtypes, then bind this map\n * in the InjectKit container so {@link ServerKitBodyParser} can resolve them.\n *\n * @example\n * ```typescript\n * registry\n * .register(ServerKitParserMappings)\n * .useMap()\n * .add('json', JsonParser)\n * .add('urlencoded', FormParser);\n * ```\n */\n@Injectable()\nexport class ServerKitParserMappings extends Map<string, ServerKitParser> {}\n\n/**\n * Selects and invokes the appropriate {@link ServerKitParser} for the incoming request\n * based on its `Content-Type` header.\n *\n * The set of supported MIME types is derived from the keys of the injected\n * {@link ServerKitParserMappings}. Duplicate keys are deduplicated automatically.\n *\n * @throws HTTP 415 if the request's `Content-Type` does not match any registered parser.\n *\n * @see {@link defaultParserMappings} – convenience map for the standard parsers\n * @see {@link bodyParserMiddleware} – the middleware that invokes this class\n */\n@Injectable()\nexport class ServerKitBodyParser {\n private readonly mimeTypes: string[];\n constructor(private readonly parsers: ServerKitParserMappings) {\n this.mimeTypes = unique(Array.from(this.parsers.keys()));\n }\n\n /**\n * Matches the request's `Content-Type` to a registered parser and delegates parsing.\n *\n * @param ctx - The current {@link ServerKitContext}; used to inspect `Content-Type` and access `ctx.req`.\n * @returns The {@link ServerKitParserResult} from the matched parser.\n * @throws HTTP 415 if no parser is registered for the request's content type.\n */\n async parse(ctx: ServerKitContext): Promise<ServerKitParserResult> {\n const mimeType = ctx.request.is(this.mimeTypes);\n if (!mimeType) {\n throw httpError(415).withDetails({ body: 'Unsupported media type' });\n }\n const parser = this.parsers.get(mimeType);\n if (!parser) {\n throw httpError(415).withDetails({ body: 'Unsupported media type' });\n }\n return parser.parse(ctx.req);\n }\n}\n","import { IncomingMessage } from 'http';\nimport { Injectable } from 'injectkit';\n\n/**\n * The result returned by every {@link ServerKitParser}.\n *\n * @property parsed - The structured/deserialized value derived from the body (e.g. a plain object for JSON).\n * @property raw - The unprocessed body as read from the stream (e.g. the original string or `Buffer`).\n * May be `undefined` for parsers that do not retain a raw representation (e.g. binary, multipart).\n */\nexport type ServerKitParserResult = {\n parsed: unknown;\n raw: unknown;\n};\n\n/**\n * Abstract base class for all ServerKit body parsers.\n *\n * Implementations read from an {@link IncomingMessage} stream and return a\n * {@link ServerKitParserResult} containing both the parsed value and the raw body.\n * Each parser is registered in the DI container and selected by MIME type via\n * {@link ServerKitBodyParser}.\n *\n * @see {@link ServerKitBodyParser} – dispatcher that selects the right parser by MIME type\n * @see {@link defaultParserMappings} – built-in MIME-type-to-parser map\n */\n@Injectable()\nexport abstract class ServerKitParser {\n /**\n * Reads and parses the body from the given request stream.\n *\n * @param req - The incoming HTTP request whose body should be consumed.\n * @returns A promise resolving to a {@link ServerKitParserResult}.\n */\n abstract parse(req: IncomingMessage): Promise<ServerKitParserResult>;\n}\n","import { parse, Reviver } from '@hapi/bourne';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\nimport { httpError } from '@maroonedsoftware/errors';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { Injectable } from 'injectkit';\n\n/**\n * Configuration options for {@link JsonParser}.\n *\n * All fields are optional; defaults are applied by the parser itself.\n *\n * @property strict - When `true` (default), only JSON objects `{}` and arrays `[]` are accepted.\n * When `false`, any valid JSON value (string, number, etc.) is allowed.\n * @property protoAction - How `@hapi/bourne` handles `__proto__` keys. Defaults to `'error'`.\n * @property reviver - Optional JSON reviver function passed to `@hapi/bourne`.\n * @property encoding - Body text encoding (default: `'utf8'`).\n * @property limit - Maximum body size (default: `'1mb'`).\n * @property length - Expected byte length from `Content-Length` (auto-set by the parser).\n */\n@Injectable()\nexport class JsonParserOptions implements raw.Options {\n strict?: boolean;\n protoAction?: 'error' | 'remove' | 'ignore';\n reviver?: Reviver;\n encoding?: string;\n limit?: string;\n length?: number;\n}\n\n// Allowed whitespace is defined in RFC 7159\n// http://www.rfc-editor.org/rfc/rfc7159.txt\n/* eslint-disable-next-line no-control-regex */\nconst strictJSONReg = /^[\\x20\\x09\\x0a\\x0d]*(\\[|\\{)/;\n\n/**\n * Parses a JSON request body using `@hapi/bourne` for prototype-pollution protection.\n *\n * In strict mode (default), only top-level objects `{}` and arrays `[]` are accepted;\n * any other value throws HTTP 400. In non-strict mode, any valid JSON value is accepted.\n * An empty body always resolves to `{ parsed: undefined }`.\n *\n * @throws HTTP 400 if the body is not valid JSON or fails the strict-mode check.\n *\n * @example\n * ```typescript\n * // Default strict mode (injectable)\n * const parser = new JsonParser(new JsonParserOptions());\n *\n * // Custom options\n * const lenient = new JsonParser({ strict: false, protoAction: 'remove' });\n * ```\n */\n@Injectable()\nexport class JsonParser extends ServerKitParser {\n constructor(private readonly options: JsonParserOptions) {\n super();\n }\n\n /**\n * Reads, decompresses, and JSON-parses the request body.\n *\n * @param req - Incoming HTTP request whose body will be consumed.\n * @returns `{ parsed: <object|array|undefined>, raw: <original string> }`.\n * @throws HTTP 400 on malformed JSON or strict-mode violation.\n */\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '1mb';\n\n const strict = this.options.strict ?? true;\n const protoAction = this.options.protoAction ?? 'error';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n const doParse = (str: string) => {\n try {\n if (this.options.reviver) {\n return parse(str, this.options.reviver, { protoAction });\n }\n return parse(str, { protoAction });\n } catch (err) {\n throw httpError(400).withCause(err as Error);\n }\n };\n\n if (!strict) {\n return str ? { parsed: doParse(str), raw: str } : { parsed: undefined, raw: str };\n } else if (!str) {\n return { parsed: undefined, raw: str };\n } else if (!strictJSONReg.test(str)) {\n throw httpError(400).withDetails({ body: 'Invalid JSON, only supports object and array' });\n }\n return { parsed: doParse(str), raw: str };\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\n/**\n * Configuration options for {@link TextParser}.\n *\n * All fields are optional; defaults are applied by the parser itself.\n *\n * @property encoding - Body text encoding (default: `'utf8'`).\n * @property limit - Maximum body size (default: `'1mb'`).\n * @property length - Expected byte length from `Content-Length` (auto-set by the parser).\n */\nexport class TextParserOptions implements raw.Options {\n encoding?: string;\n limit?: string;\n length?: number;\n}\n\n/**\n * Reads the request body as a plain string.\n *\n * No structural parsing is performed; the raw text is returned as both `parsed` and `raw`.\n * Suitable for `text/plain` and similar content types. Decompresses the stream via `inflation`\n * before buffering.\n *\n * @example\n * ```typescript\n * const parser = new TextParser(new TextParserOptions());\n * const { parsed } = await parser.parse(req); // parsed is a string\n * ```\n */\n@Injectable()\nexport class TextParser extends ServerKitParser {\n constructor(private readonly options: TextParserOptions) {\n super();\n }\n\n /**\n * Reads and decompresses the request body into a string.\n *\n * @param req - Incoming HTTP request whose body will be consumed.\n * @returns `{ parsed: <string>, raw: <same string> }`.\n */\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '1mb';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n return { parsed: str, raw: str };\n }\n}\n","import { httpError } from '@maroonedsoftware/errors';\nimport { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport { parse, IParseOptions } from 'qs';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\n/**\n * Configuration options for {@link FormParser}.\n *\n * Combines `raw-body` read options with `qs` parse options.\n * All fields are optional; defaults are applied by the parser itself.\n *\n * @property encoding - Body text encoding (default: `'utf8'`).\n * @property limit - Maximum body size (default: `'56kb'`).\n * @property length - Expected byte length from `Content-Length` (auto-set by the parser).\n * @property allowDots - When `true`, `qs` interprets dot notation (`user.name`) as nested objects.\n * @property depth - Maximum nesting depth for parsed objects (default: `qs` default of `5`).\n * @property parameterLimit - Maximum number of parameters to parse (default: `qs` default of `1000`).\n */\n@Injectable()\nexport class FormParserOptions implements raw.Options, IParseOptions {\n encoding?: string;\n limit?: string;\n length?: number;\n allowDots?: boolean;\n depth?: number;\n parameterLimit?: number;\n}\n\n/**\n * Parses a URL-encoded (`application/x-www-form-urlencoded`) request body using `qs`.\n *\n * Supports nested objects via bracket notation (`user[name]=alice`) by default.\n * Dot notation (`user.name=alice`) requires `allowDots: true` in the options.\n * An empty body resolves to `{ parsed: {} }`.\n *\n * @throws HTTP 400 if `qs.parse` throws unexpectedly.\n *\n * @example\n * ```typescript\n * // Default options (injectable)\n * const parser = new FormParser(new FormParserOptions());\n *\n * // Enable dot notation\n * const parser = new FormParser({ allowDots: true });\n * ```\n */\n@Injectable()\nexport class FormParser extends ServerKitParser {\n constructor(private readonly options: FormParserOptions) {\n super();\n }\n\n /**\n * Reads, decompresses, and URL-decodes the request body.\n *\n * @param req - Incoming HTTP request whose body will be consumed.\n * @returns `{ parsed: <object>, raw: <original url-encoded string> }`.\n * @throws HTTP 400 if parsing fails.\n */\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n const len = req.headers['content-length'];\n const contentEncoding = req.headers['content-encoding'] || 'identity';\n const length: number | undefined = len && contentEncoding === 'identity' ? ~~len : undefined;\n const encoding = this.options.encoding ?? 'utf8';\n const limit = this.options.limit ?? '56kb';\n\n const str = await raw(inflate(req), { encoding, limit, length });\n\n try {\n return { parsed: parse(str, this.options), raw: str };\n } catch (err) {\n throw httpError(400).withCause(err as Error);\n }\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport { MultipartBody } from '@maroonedsoftware/multipart';\n\n/**\n * Wraps the request stream in a {@link MultipartBody} for lazy multipart/form-data parsing.\n *\n * The body is **not** eagerly consumed; fields and files are read on-demand through the\n * `MultipartBody` API. `raw` is always `undefined` because the stream cannot be replayed\n * once consumed.\n */\n@Injectable()\nexport class MultipartParser extends ServerKitParser {\n /**\n * Creates a {@link MultipartBody} around the request stream.\n *\n * @param req - Incoming HTTP request containing the multipart body.\n * @returns `{ parsed: MultipartBody, raw: undefined }`.\n */\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n return { parsed: new MultipartBody(req), raw: undefined };\n }\n}\n","import { Injectable } from 'injectkit';\nimport { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';\nimport { IncomingMessage } from 'http';\nimport raw from 'raw-body';\nimport inflate from 'inflation';\n\n/**\n * Reads the request body as a raw `Buffer` without any text decoding or structural parsing.\n *\n * Useful for binary payloads (e.g. PDFs, images, protobuf) where the caller needs the\n * untransformed bytes. Decompresses the stream via `inflation` before buffering.\n *\n * `raw` is always `undefined` because the `Buffer` itself is both the parsed and raw form.\n */\n@Injectable()\nexport class BinaryParser extends ServerKitParser {\n /**\n * Buffers the (optionally compressed) request body into a `Buffer`.\n *\n * @param req - Incoming HTTP request to read.\n * @returns `{ parsed: Buffer, raw: undefined }`.\n */\n async parse(req: IncomingMessage): Promise<ServerKitParserResult> {\n return { parsed: await raw(inflate(req)), raw: undefined };\n }\n}\n","import { Identifier } from 'injectkit';\nimport { FormParser } from './form.parser.js';\nimport { JsonParser } from './json.parser.js';\nimport { MultipartParser } from './multipart.parser.js';\nimport { ServerKitParser } from './serverkit.parser.js';\nimport { TextParser } from './text.parser.js';\n\n/**\n * Built-in MIME-subtype-to-parser mappings used by {@link bodyParserMiddleware}.\n *\n * Each key is a MIME subtype (matched against `Content-Type` via Koa's `ctx.request.is()`)\n * and the value is the InjectKit {@link Identifier} for the corresponding {@link ServerKitParser}.\n *\n * | MIME subtype | Parser |\n * | --------------------- | ----------------- |\n * | `json` | {@link JsonParser} |\n * | `application/*+json` | {@link JsonParser} |\n * | `urlencoded` | {@link FormParser} |\n * | `text` | {@link TextParser} |\n * | `multipart` | {@link MultipartParser} |\n *\n * Extend by spreading into a new object and registering the result in the DI container:\n * ```typescript\n * const myMappings = { ...defaultParserMappings, pdf: BinaryParser };\n * ```\n */\nexport const defaultParserMappings: Record<string, Identifier<ServerKitParser>> = {\n json: JsonParser,\n 'application/*+json': JsonParser,\n urlencoded: FormParser,\n text: TextParser,\n multipart: MultipartParser,\n} as const;\n"],"mappings":"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","names":["Router","ServerKitRouter","Router","cors","corsMiddleware","options","originMatcher","ctx","origin","get","matchers","matcher","test","cors","allowMethods","secureContext","keepHeadersOnError","privateNetworkAccess","IsHttpError","errorMiddleware","ctx","next","status","body","statusCode","message","details","url","URL","toString","app","emit","error","IsHttpError","headers","entry","Object","entries","set","httpError","rateLimiterMiddleware","rateLimiter","ctx","next","consume","ip","error","httpError","withCause","crypto","Logger","serverKitContextMiddleware","container","ctx","next","createScopedContainer","logger","get","Logger","loggerName","path","userAgent","correlationId","crypto","randomUUID","requestId","headers","set","AuthenticationSchemeHandler","invalidAuthenticationContext","authenticationMiddleware","ctx","next","authenticationContext","invalidAuthenticationContext","authorizationHeader","req","headers","authorization","schemeHandler","container","get","AuthenticationSchemeHandler","handle","httpError","IsHttpError","Injectable","unique","httpError","ServerKitParserMappings","Map","ServerKitBodyParser","mimeTypes","parsers","unique","Array","from","keys","parse","ctx","mimeType","request","is","httpError","withDetails","body","parser","get","req","bodyParserMiddleware","contentTypes","ctx","next","length","request","httpError","withDetails","body","is","join","value","type","parser","container","get","ServerKitBodyParser","result","parse","parsed","rawBody","raw","error","IsHttpError","withCause","Injectable","ServerKitParser","parse","raw","inflate","httpError","Injectable","JsonParserOptions","strict","protoAction","reviver","encoding","limit","length","strictJSONReg","JsonParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","undefined","str","raw","inflate","doParse","err","httpError","withCause","parsed","test","withDetails","body","Injectable","raw","inflate","TextParserOptions","encoding","limit","length","TextParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","undefined","str","raw","inflate","parsed","httpError","Injectable","parse","raw","inflate","FormParserOptions","encoding","limit","length","allowDots","depth","parameterLimit","FormParser","ServerKitParser","options","parse","req","len","headers","contentEncoding","undefined","str","raw","inflate","parsed","err","httpError","withCause","Injectable","MultipartBody","MultipartParser","ServerKitParser","parse","req","parsed","MultipartBody","raw","undefined","Injectable","raw","inflate","BinaryParser","ServerKitParser","parse","req","parsed","raw","inflate","undefined","defaultParserMappings","json","JsonParser","urlencoded","FormParser","text","TextParser","multipart","MultipartParser"]}
@@ -1,6 +1,20 @@
1
1
  import { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';
2
2
  import { IncomingMessage } from 'http';
3
+ /**
4
+ * Reads the request body as a raw `Buffer` without any text decoding or structural parsing.
5
+ *
6
+ * Useful for binary payloads (e.g. PDFs, images, protobuf) where the caller needs the
7
+ * untransformed bytes. Decompresses the stream via `inflation` before buffering.
8
+ *
9
+ * `raw` is always `undefined` because the `Buffer` itself is both the parsed and raw form.
10
+ */
3
11
  export declare class BinaryParser extends ServerKitParser {
12
+ /**
13
+ * Buffers the (optionally compressed) request body into a `Buffer`.
14
+ *
15
+ * @param req - Incoming HTTP request to read.
16
+ * @returns `{ parsed: Buffer, raw: undefined }`.
17
+ */
4
18
  parse(req: IncomingMessage): Promise<ServerKitParserResult>;
5
19
  }
6
20
  //# sourceMappingURL=binary.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"binary.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/binary.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAIvC,qBACa,YAAa,SAAQ,eAAe;IACzC,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAGlE"}
1
+ {"version":3,"file":"binary.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/binary.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAIvC;;;;;;;GAOG;AACH,qBACa,YAAa,SAAQ,eAAe;IAC/C;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAGlE"}
@@ -2,10 +2,55 @@ import { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';
2
2
  import { IncomingMessage } from 'http';
3
3
  import { IParseOptions } from 'qs';
4
4
  import raw from 'raw-body';
5
- export type FormParserOptions = raw.Options & IParseOptions;
5
+ /**
6
+ * Configuration options for {@link FormParser}.
7
+ *
8
+ * Combines `raw-body` read options with `qs` parse options.
9
+ * All fields are optional; defaults are applied by the parser itself.
10
+ *
11
+ * @property encoding - Body text encoding (default: `'utf8'`).
12
+ * @property limit - Maximum body size (default: `'56kb'`).
13
+ * @property length - Expected byte length from `Content-Length` (auto-set by the parser).
14
+ * @property allowDots - When `true`, `qs` interprets dot notation (`user.name`) as nested objects.
15
+ * @property depth - Maximum nesting depth for parsed objects (default: `qs` default of `5`).
16
+ * @property parameterLimit - Maximum number of parameters to parse (default: `qs` default of `1000`).
17
+ */
18
+ export declare class FormParserOptions implements raw.Options, IParseOptions {
19
+ encoding?: string;
20
+ limit?: string;
21
+ length?: number;
22
+ allowDots?: boolean;
23
+ depth?: number;
24
+ parameterLimit?: number;
25
+ }
26
+ /**
27
+ * Parses a URL-encoded (`application/x-www-form-urlencoded`) request body using `qs`.
28
+ *
29
+ * Supports nested objects via bracket notation (`user[name]=alice`) by default.
30
+ * Dot notation (`user.name=alice`) requires `allowDots: true` in the options.
31
+ * An empty body resolves to `{ parsed: {} }`.
32
+ *
33
+ * @throws HTTP 400 if `qs.parse` throws unexpectedly.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Default options (injectable)
38
+ * const parser = new FormParser(new FormParserOptions());
39
+ *
40
+ * // Enable dot notation
41
+ * const parser = new FormParser({ allowDots: true });
42
+ * ```
43
+ */
6
44
  export declare class FormParser extends ServerKitParser {
7
45
  private readonly options;
8
- constructor(options?: FormParserOptions);
46
+ constructor(options: FormParserOptions);
47
+ /**
48
+ * Reads, decompresses, and URL-decodes the request body.
49
+ *
50
+ * @param req - Incoming HTTP request whose body will be consumed.
51
+ * @returns `{ parsed: <object>, raw: <original url-encoded string> }`.
52
+ * @throws HTTP 400 if parsing fails.
53
+ */
9
54
  parse(req: IncomingMessage): Promise<ServerKitParserResult>;
10
55
  }
11
56
  //# sourceMappingURL=form.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"form.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/form.parser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAS,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,OAAO,GAAG,aAAa,CAAC;AAE5D,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,iBAAsB;IAItD,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAelE"}
1
+ {"version":3,"file":"form.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/form.parser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAS,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B;;;;;;;;;;;;GAYG;AACH,qBACa,iBAAkB,YAAW,GAAG,CAAC,OAAO,EAAE,aAAa;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAIvD;;;;;;OAMG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAelE"}
@@ -2,14 +2,55 @@ import { Reviver } from '@hapi/bourne';
2
2
  import { IncomingMessage } from 'http';
3
3
  import raw from 'raw-body';
4
4
  import { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';
5
- export type JsonParserOptions = raw.Options & {
5
+ /**
6
+ * Configuration options for {@link JsonParser}.
7
+ *
8
+ * All fields are optional; defaults are applied by the parser itself.
9
+ *
10
+ * @property strict - When `true` (default), only JSON objects `{}` and arrays `[]` are accepted.
11
+ * When `false`, any valid JSON value (string, number, etc.) is allowed.
12
+ * @property protoAction - How `@hapi/bourne` handles `__proto__` keys. Defaults to `'error'`.
13
+ * @property reviver - Optional JSON reviver function passed to `@hapi/bourne`.
14
+ * @property encoding - Body text encoding (default: `'utf8'`).
15
+ * @property limit - Maximum body size (default: `'1mb'`).
16
+ * @property length - Expected byte length from `Content-Length` (auto-set by the parser).
17
+ */
18
+ export declare class JsonParserOptions implements raw.Options {
6
19
  strict?: boolean;
7
20
  protoAction?: 'error' | 'remove' | 'ignore';
8
21
  reviver?: Reviver;
9
- };
22
+ encoding?: string;
23
+ limit?: string;
24
+ length?: number;
25
+ }
26
+ /**
27
+ * Parses a JSON request body using `@hapi/bourne` for prototype-pollution protection.
28
+ *
29
+ * In strict mode (default), only top-level objects `{}` and arrays `[]` are accepted;
30
+ * any other value throws HTTP 400. In non-strict mode, any valid JSON value is accepted.
31
+ * An empty body always resolves to `{ parsed: undefined }`.
32
+ *
33
+ * @throws HTTP 400 if the body is not valid JSON or fails the strict-mode check.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Default strict mode (injectable)
38
+ * const parser = new JsonParser(new JsonParserOptions());
39
+ *
40
+ * // Custom options
41
+ * const lenient = new JsonParser({ strict: false, protoAction: 'remove' });
42
+ * ```
43
+ */
10
44
  export declare class JsonParser extends ServerKitParser {
11
45
  private readonly options;
12
- constructor(options?: JsonParserOptions);
46
+ constructor(options: JsonParserOptions);
47
+ /**
48
+ * Reads, decompresses, and JSON-parses the request body.
49
+ *
50
+ * @param req - Incoming HTTP request whose body will be consumed.
51
+ * @returns `{ parsed: <object|array|undefined>, raw: <original string> }`.
52
+ * @throws HTTP 400 on malformed JSON or strict-mode violation.
53
+ */
13
54
  parse(req: IncomingMessage): Promise<ServerKitParserResult>;
14
55
  }
15
56
  //# sourceMappingURL=json.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/json.parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG/E,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,OAAO,GAAG;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAOF,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,iBAAsB;IAItD,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAgClE"}
1
+ {"version":3,"file":"json.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/json.parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG/E;;;;;;;;;;;;GAYG;AACH,qBACa,iBAAkB,YAAW,GAAG,CAAC,OAAO;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAIvD;;;;;;OAMG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAgClE"}
@@ -1,6 +1,19 @@
1
1
  import { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';
2
2
  import { IncomingMessage } from 'http';
3
+ /**
4
+ * Wraps the request stream in a {@link MultipartBody} for lazy multipart/form-data parsing.
5
+ *
6
+ * The body is **not** eagerly consumed; fields and files are read on-demand through the
7
+ * `MultipartBody` API. `raw` is always `undefined` because the stream cannot be replayed
8
+ * once consumed.
9
+ */
3
10
  export declare class MultipartParser extends ServerKitParser {
11
+ /**
12
+ * Creates a {@link MultipartBody} around the request stream.
13
+ *
14
+ * @param req - Incoming HTTP request containing the multipart body.
15
+ * @returns `{ parsed: MultipartBody, raw: undefined }`.
16
+ */
4
17
  parse(req: IncomingMessage): Promise<ServerKitParserResult>;
5
18
  }
6
19
  //# sourceMappingURL=multipart.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"multipart.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/multipart.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,qBACa,eAAgB,SAAQ,eAAe;IAC5C,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAGlE"}
1
+ {"version":3,"file":"multipart.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/multipart.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC;;;;;;GAMG;AACH,qBACa,eAAgB,SAAQ,eAAe;IAClD;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAGlE"}
@@ -0,0 +1,23 @@
1
+ import { Identifier } from 'injectkit';
2
+ import { ServerKitParser } from './serverkit.parser.js';
3
+ /**
4
+ * Built-in MIME-subtype-to-parser mappings used by {@link bodyParserMiddleware}.
5
+ *
6
+ * Each key is a MIME subtype (matched against `Content-Type` via Koa's `ctx.request.is()`)
7
+ * and the value is the InjectKit {@link Identifier} for the corresponding {@link ServerKitParser}.
8
+ *
9
+ * | MIME subtype | Parser |
10
+ * | --------------------- | ----------------- |
11
+ * | `json` | {@link JsonParser} |
12
+ * | `application/*+json` | {@link JsonParser} |
13
+ * | `urlencoded` | {@link FormParser} |
14
+ * | `text` | {@link TextParser} |
15
+ * | `multipart` | {@link MultipartParser} |
16
+ *
17
+ * Extend by spreading into a new object and registering the result in the DI container:
18
+ * ```typescript
19
+ * const myMappings = { ...defaultParserMappings, pdf: BinaryParser };
20
+ * ```
21
+ */
22
+ export declare const defaultParserMappings: Record<string, Identifier<ServerKitParser>>;
23
+ //# sourceMappingURL=serverkit.default.parsers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverkit.default.parsers.d.ts","sourceRoot":"","sources":["../../src/parsers/serverkit.default.parsers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAIvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,CAMpE,CAAC"}
@@ -1,9 +1,33 @@
1
1
  import { IncomingMessage } from 'http';
2
+ /**
3
+ * The result returned by every {@link ServerKitParser}.
4
+ *
5
+ * @property parsed - The structured/deserialized value derived from the body (e.g. a plain object for JSON).
6
+ * @property raw - The unprocessed body as read from the stream (e.g. the original string or `Buffer`).
7
+ * May be `undefined` for parsers that do not retain a raw representation (e.g. binary, multipart).
8
+ */
2
9
  export type ServerKitParserResult = {
3
10
  parsed: unknown;
4
11
  raw: unknown;
5
12
  };
13
+ /**
14
+ * Abstract base class for all ServerKit body parsers.
15
+ *
16
+ * Implementations read from an {@link IncomingMessage} stream and return a
17
+ * {@link ServerKitParserResult} containing both the parsed value and the raw body.
18
+ * Each parser is registered in the DI container and selected by MIME type via
19
+ * {@link ServerKitBodyParser}.
20
+ *
21
+ * @see {@link ServerKitBodyParser} – dispatcher that selects the right parser by MIME type
22
+ * @see {@link defaultParserMappings} – built-in MIME-type-to-parser map
23
+ */
6
24
  export declare abstract class ServerKitParser {
25
+ /**
26
+ * Reads and parses the body from the given request stream.
27
+ *
28
+ * @param req - The incoming HTTP request whose body should be consumed.
29
+ * @returns A promise resolving to a {@link ServerKitParserResult}.
30
+ */
7
31
  abstract parse(req: IncomingMessage): Promise<ServerKitParserResult>;
8
32
  }
9
33
  //# sourceMappingURL=serverkit.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serverkit.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/serverkit.parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,8BACsB,eAAe;IACnC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CACrE"}
1
+ {"version":3,"file":"serverkit.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/serverkit.parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,8BACsB,eAAe;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CACrE"}
@@ -1,10 +1,42 @@
1
1
  import { ServerKitParser, ServerKitParserResult } from './serverkit.parser.js';
2
2
  import { IncomingMessage } from 'http';
3
3
  import raw from 'raw-body';
4
- export type TextParserOptions = raw.Options;
4
+ /**
5
+ * Configuration options for {@link TextParser}.
6
+ *
7
+ * All fields are optional; defaults are applied by the parser itself.
8
+ *
9
+ * @property encoding - Body text encoding (default: `'utf8'`).
10
+ * @property limit - Maximum body size (default: `'1mb'`).
11
+ * @property length - Expected byte length from `Content-Length` (auto-set by the parser).
12
+ */
13
+ export declare class TextParserOptions implements raw.Options {
14
+ encoding?: string;
15
+ limit?: string;
16
+ length?: number;
17
+ }
18
+ /**
19
+ * Reads the request body as a plain string.
20
+ *
21
+ * No structural parsing is performed; the raw text is returned as both `parsed` and `raw`.
22
+ * Suitable for `text/plain` and similar content types. Decompresses the stream via `inflation`
23
+ * before buffering.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const parser = new TextParser(new TextParserOptions());
28
+ * const { parsed } = await parser.parse(req); // parsed is a string
29
+ * ```
30
+ */
5
31
  export declare class TextParser extends ServerKitParser {
6
32
  private readonly options;
7
- constructor(options?: TextParserOptions);
33
+ constructor(options: TextParserOptions);
34
+ /**
35
+ * Reads and decompresses the request body into a string.
36
+ *
37
+ * @param req - Incoming HTTP request whose body will be consumed.
38
+ * @returns `{ parsed: <string>, raw: <same string> }`.
39
+ */
8
40
  parse(req: IncomingMessage): Promise<ServerKitParserResult>;
9
41
  }
10
42
  //# sourceMappingURL=text.parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/text.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAAC;AAE5C,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,iBAAsB;IAItD,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAWlE"}
1
+ {"version":3,"file":"text.parser.d.ts","sourceRoot":"","sources":["../../src/parsers/text.parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,YAAW,GAAG,CAAC,OAAO;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,qBACa,UAAW,SAAQ,eAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAIvD;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAWlE"}
@@ -1,11 +1,45 @@
1
1
  import { ServerKitParser, ServerKitParserResult } from './parsers/serverkit.parser.js';
2
2
  import { ServerKitContext } from './serverkit.context.js';
3
+ /**
4
+ * DI-injectable map of MIME subtypes to {@link ServerKitParser} instances.
5
+ *
6
+ * Register parser instances against their MIME subtypes, then bind this map
7
+ * in the InjectKit container so {@link ServerKitBodyParser} can resolve them.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * registry
12
+ * .register(ServerKitParserMappings)
13
+ * .useMap()
14
+ * .add('json', JsonParser)
15
+ * .add('urlencoded', FormParser);
16
+ * ```
17
+ */
3
18
  export declare class ServerKitParserMappings extends Map<string, ServerKitParser> {
4
19
  }
20
+ /**
21
+ * Selects and invokes the appropriate {@link ServerKitParser} for the incoming request
22
+ * based on its `Content-Type` header.
23
+ *
24
+ * The set of supported MIME types is derived from the keys of the injected
25
+ * {@link ServerKitParserMappings}. Duplicate keys are deduplicated automatically.
26
+ *
27
+ * @throws HTTP 415 if the request's `Content-Type` does not match any registered parser.
28
+ *
29
+ * @see {@link defaultParserMappings} – convenience map for the standard parsers
30
+ * @see {@link bodyParserMiddleware} – the middleware that invokes this class
31
+ */
5
32
  export declare class ServerKitBodyParser {
6
33
  private readonly parsers;
7
34
  private readonly mimeTypes;
8
35
  constructor(parsers: ServerKitParserMappings);
36
+ /**
37
+ * Matches the request's `Content-Type` to a registered parser and delegates parsing.
38
+ *
39
+ * @param ctx - The current {@link ServerKitContext}; used to inspect `Content-Type` and access `ctx.req`.
40
+ * @returns The {@link ServerKitParserResult} from the matched parser.
41
+ * @throws HTTP 415 if no parser is registered for the request's content type.
42
+ */
9
43
  parse(ctx: ServerKitContext): Promise<ServerKitParserResult>;
10
44
  }
11
45
  //# sourceMappingURL=serverkit.bodyparser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serverkit.bodyparser.d.ts","sourceRoot":"","sources":["../src/serverkit.bodyparser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI1D,qBACa,uBAAwB,SAAQ,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;CAAG;AAE5E,qBACa,mBAAmB;IAElB,OAAO,CAAC,QAAQ,CAAC,OAAO;IADpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;gBACR,OAAO,EAAE,uBAAuB;IAIvD,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAWnE"}
1
+ {"version":3,"file":"serverkit.bodyparser.d.ts","sourceRoot":"","sources":["../src/serverkit.bodyparser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI1D;;;;;;;;;;;;;;GAcG;AACH,qBACa,uBAAwB,SAAQ,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;CAAG;AAE5E;;;;;;;;;;;GAWG;AACH,qBACa,mBAAmB;IAElB,OAAO,CAAC,QAAQ,CAAC,OAAO;IADpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;gBACR,OAAO,EAAE,uBAAuB;IAI7D;;;;;;OAMG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAWnE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maroonedsoftware/koa",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Koa middleware, body parsing, and utilities for ServerKit",
5
5
  "author": {
6
6
  "name": "Marooned Software",
@@ -38,10 +38,10 @@
38
38
  "rate-limiter-flexible": "^9.1.1",
39
39
  "raw-body": "^3.0.2",
40
40
  "@maroonedsoftware/errors": "1.2.0",
41
+ "@maroonedsoftware/logger": "1.0.0",
41
42
  "@maroonedsoftware/multipart": "1.0.2",
42
43
  "@maroonedsoftware/utilities": "1.1.0",
43
- "@maroonedsoftware/authentication": "0.0.1",
44
- "@maroonedsoftware/logger": "1.0.0"
44
+ "@maroonedsoftware/authentication": "0.0.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@koa/cors": "^5.0.0",
@@ -57,8 +57,8 @@
57
57
  "@types/koa__cors": "^5.0.1",
58
58
  "@types/qs": "^6.15.0",
59
59
  "koa": "^3.1.2",
60
- "@repo/config-typescript": "0.0.0",
61
- "@repo/config-eslint": "0.1.0"
60
+ "@repo/config-eslint": "0.1.0",
61
+ "@repo/config-typescript": "0.0.0"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration",
@@ -1,5 +0,0 @@
1
- import { Constructor, Registry } from 'injectkit';
2
- import { ServerKitParser } from './parsers/serverkit.parser.js';
3
- export type ParserMappingOverrides = Record<string, Constructor<ServerKitParser>>;
4
- export declare const setupParsers: (registry: Registry, overrides?: ParserMappingOverrides) => void;
5
- //# sourceMappingURL=parsers.setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parsers.setup.d.ts","sourceRoot":"","sources":["../src/parsers.setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAOhE,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;AAkBlF,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ,EAAE,YAAW,sBAA2B,SAqBtF,CAAC"}