@molecule/api-resource 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-26T04:23:42.740Z
6
+ Generated: 2026-11-06T22:08:33.809Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-resource
@@ -30,6 +30,33 @@ res.status(statusCode).send(body)
30
30
  Monkey-patching `res.json` (or `res.send`/`res.end`) to re-shape a handler's answer intercepts
31
31
  nothing the handler produces, so the request it was meant to answer is left unanswered and hangs.
32
32
 
33
+ **A different request or response envelope is configuration.** Pass
34
+ `mapRequest` and/or `mapResponse` as the second argument to `createRequestHandler`.
35
+ Both may be asynchronous. The existing handler still validates and authorizes
36
+ the adapted input. A response mapper sees the handler's errors and partial
37
+ responses too: preserve them and its headers instead of treating every result
38
+ as a success. `adaptHandler(handler, options)` offers the same mappings when
39
+ another HTTP adapter will send the structured response.
40
+ A request mapper can return `{ response: { statusCode, body } }` to answer
41
+ before calling the handler, for example when extra validation fails or a
42
+ filter lookup finds nothing. That response still passes through mapResponse.
43
+
44
+ ```typescript
45
+ import { createRequestHandler } from '@molecule/api-resource'
46
+ import { handlers } from '@molecule/api-resource-user'
47
+
48
+ const register = createRequestHandler(handlers.create({ name: 'User', tableName: 'users' }), {
49
+ // Return overrides. The adapter retains framework methods and getters.
50
+ mapRequest: (req) => ({ body: req.body?.account }),
51
+ mapResponse: (result) =>
52
+ result.statusCode === 201
53
+ ? { ...result, body: { account: result.body.props, accessToken: result.body.accessToken } }
54
+ : result,
55
+ })
56
+ // Keep the route's existing rate limiter and authorization around register.
57
+ // No mapping is inferred: a flat-body API keeps createRequestHandler(handler).
58
+ ```
59
+
33
60
  > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
34
61
  > It is written to be read by coding agents as much as by people, and is generated from this
35
62
  > package's source — edit `src/index.ts` JSDoc, not this file.
@@ -114,6 +141,38 @@ interface Response {
114
141
  }
115
142
  ```
116
143
 
144
+ #### `RequestHandlerOptions`
145
+
146
+ Per-route wire-format adapters. Validation and authorization still belong to
147
+ the original handler. Omit these options to retain its native contract.
148
+
149
+ ```typescript
150
+ interface RequestHandlerOptions {
151
+ /**
152
+ * Return the fields to replace before the handler validates the request.
153
+ * The adapter preserves the original request's methods and getters, and does
154
+ * not mutate it. Returning `{ body }` or `{ query }` is sufficient, including
155
+ * frameworks whose request properties have read-only accessors.
156
+ * Return `{ response: { statusCode, headers?, body } }` to answer before the
157
+ * handler runs. That result still goes through mapResponse, with the original request.
158
+ */
159
+ mapRequest?: (
160
+ req: MoleculeRequest,
161
+ res: MoleculeResponse,
162
+ ) => RequestMappingResult | Promise<RequestMappingResult>
163
+ /**
164
+ * Adapt the returned status, headers or body. This sees error and partial
165
+ * responses too: narrow the success shape before changing it, and preserve
166
+ * headers with `{ ...response, body }`. It never runs for fall-through.
167
+ */
168
+ mapResponse?: (
169
+ response: Response,
170
+ req: MoleculeRequest,
171
+ res: MoleculeResponse,
172
+ ) => Response | Promise<Response>
173
+ }
174
+ ```
175
+
117
176
  #### `MoleculeRequest`
118
177
 
119
178
  Framework-agnostic HTTP request interface. Structurally compatible with Express's
@@ -258,6 +317,16 @@ type Handler = (
258
317
  - `req` — The incoming HTTP request.
259
318
  - `res` — The HTTP response (available but handlers should return a Response instead of calling `res` directly).
260
319
 
320
+ #### `RequestMappingResult`
321
+
322
+ Request overrides, or an explicit response that stops before the handler.
323
+ The response form supports extra validation and unresolved external filters
324
+ without writing data or inventing a placeholder identifier.
325
+
326
+ ```typescript
327
+ type RequestMappingResult = Partial<MoleculeRequest> | { response: Response }
328
+ ```
329
+
261
330
  #### `MoleculeNextFunction`
262
331
 
263
332
  Framework-agnostic next function. Call with no arguments to pass to the next
@@ -333,7 +402,22 @@ function create<
333
402
 
334
403
  **Returns:** A curried async function that accepts `{ props, id? }` and returns a `{ statusCode, body }` response.
335
404
 
336
- #### `createRequestHandler(handler)`
405
+ #### `adaptHandler(handler, options)`
406
+
407
+ Compose wire-format adapters around a structured resource handler without
408
+ sending its response. The response mapper receives the adapted request.
409
+ Exceptions propagate to the caller's normal error boundary.
410
+
411
+ ```typescript
412
+ function adaptHandler(handler: Handler, options?: RequestHandlerOptions): Handler
413
+ ```
414
+
415
+ - `handler` — The existing resource handler, including its checks.
416
+ - `options` — Request and response mappings for this route only.
417
+
418
+ **Returns:** A structured handler that keeps null/undefined fall-through intact.
419
+
420
+ #### `createRequestHandler(handler, options)`
337
421
 
338
422
  Wraps a resource handler into Express-compatible middleware. Calls the handler,
339
423
  then applies `statusCode`, `headers`, and `body` to the response. If the handler
@@ -343,10 +427,12 @@ i18n-translated error messages.
343
427
  ```typescript
344
428
  function createRequestHandler(
345
429
  handler: Handler,
430
+ options?: RequestHandlerOptions,
346
431
  ): (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>
347
432
  ```
348
433
 
349
434
  - `handler` — The resource handler function to wrap.
435
+ - `options` — Optional wire-format mappings; neither mutates the shared handler.
350
436
 
351
437
  **Returns:** An Express-compatible middleware function `(req, res, next) => void`.
352
438
 
@@ -35,6 +35,45 @@ export interface Response {
35
35
  */
36
36
  body: any;
37
37
  }
38
+ /**
39
+ * Request overrides, or an explicit response that stops before the handler.
40
+ * The response form supports extra validation and unresolved external filters
41
+ * without writing data or inventing a placeholder identifier.
42
+ */
43
+ export type RequestMappingResult = Partial<MoleculeRequest> | {
44
+ response: Response;
45
+ };
46
+ /**
47
+ * Per-route wire-format adapters. Validation and authorization still belong to
48
+ * the original handler. Omit these options to retain its native contract.
49
+ */
50
+ export interface RequestHandlerOptions {
51
+ /**
52
+ * Return the fields to replace before the handler validates the request.
53
+ * The adapter preserves the original request's methods and getters, and does
54
+ * not mutate it. Returning `{ body }` or `{ query }` is sufficient, including
55
+ * frameworks whose request properties have read-only accessors.
56
+ * Return `{ response: { statusCode, headers?, body } }` to answer before the
57
+ * handler runs. That result still goes through mapResponse, with the original request.
58
+ */
59
+ mapRequest?: (req: MoleculeRequest, res: MoleculeResponse) => RequestMappingResult | Promise<RequestMappingResult>;
60
+ /**
61
+ * Adapt the returned status, headers or body. This sees error and partial
62
+ * responses too: narrow the success shape before changing it, and preserve
63
+ * headers with `{ ...response, body }`. It never runs for fall-through.
64
+ */
65
+ mapResponse?: (response: Response, req: MoleculeRequest, res: MoleculeResponse) => Response | Promise<Response>;
66
+ }
67
+ /**
68
+ * Compose wire-format adapters around a structured resource handler without
69
+ * sending its response. The response mapper receives the adapted request.
70
+ * Exceptions propagate to the caller's normal error boundary.
71
+ *
72
+ * @param handler - The existing resource handler, including its checks.
73
+ * @param options - Request and response mappings for this route only.
74
+ * @returns A structured handler that keeps null/undefined fall-through intact.
75
+ */
76
+ export declare const adaptHandler: (handler: Handler, options?: RequestHandlerOptions) => Handler;
38
77
  /**
39
78
  * Wraps a resource handler into Express-compatible middleware. Calls the handler,
40
79
  * then applies `statusCode`, `headers`, and `body` to the response. If the handler
@@ -42,7 +81,8 @@ export interface Response {
42
81
  * i18n-translated error messages.
43
82
  *
44
83
  * @param handler - The resource handler function to wrap.
84
+ * @param options - Optional wire-format mappings; neither mutates the shared handler.
45
85
  * @returns An Express-compatible middleware function `(req, res, next) => void`.
46
86
  */
47
- export declare const createRequestHandler: (handler: Handler) => (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>;
87
+ export declare const createRequestHandler: (handler: Handler, options?: RequestHandlerOptions) => (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>;
48
88
  //# sourceMappingURL=createRequestHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createRequestHandler.d.ts","sourceRoot":"","sources":["../src/createRequestHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAI9F;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAA;AAEvE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;OAEG;IACH,IAAI,EAAE,GAAG,CAAA;CACV;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC9B,SAAS,OAAO,MACV,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,kBAkB7E,CAAA"}
1
+ {"version":3,"file":"createRequestHandler.d.ts","sourceRoot":"","sources":["../src/createRequestHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAI9F;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAA;AAEvE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;OAEG;IACH,IAAI,EAAE,GAAG,CAAA;CACV;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEpF;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CACX,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,gBAAgB,KAClB,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACzD;;;;OAIG;IACH,WAAW,CAAC,EAAE,CACZ,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,gBAAgB,KAClB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACtB,SAAS,OAAO,EAAE,UAAS,qBAA0B,KAAG,OAYxD,CAAA;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,EAAE,UAAS,qBAA0B,MAE1E,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,kBAmBtF,CAAA"}
@@ -11,6 +11,26 @@
11
11
  import { getLogger } from '@molecule/api-bond';
12
12
  const logger = getLogger();
13
13
  import { t } from '@molecule/api-i18n';
14
+ /**
15
+ * Compose wire-format adapters around a structured resource handler without
16
+ * sending its response. The response mapper receives the adapted request.
17
+ * Exceptions propagate to the caller's normal error boundary.
18
+ *
19
+ * @param handler - The existing resource handler, including its checks.
20
+ * @param options - Request and response mappings for this route only.
21
+ * @returns A structured handler that keeps null/undefined fall-through intact.
22
+ */
23
+ export const adaptHandler = (handler, options = {}) => async (req, res) => {
24
+ const overrides = options.mapRequest ? await options.mapRequest(req, res) : undefined;
25
+ const earlyResponse = overrides && 'response' in overrides ? overrides.response : undefined;
26
+ const request = overrides && !earlyResponse
27
+ ? Object.create(req, Object.getOwnPropertyDescriptors(overrides))
28
+ : req;
29
+ const response = earlyResponse ?? (await handler(request, res));
30
+ return response && options.mapResponse
31
+ ? await options.mapResponse(response, request, res)
32
+ : response;
33
+ };
14
34
  /**
15
35
  * Wraps a resource handler into Express-compatible middleware. Calls the handler,
16
36
  * then applies `statusCode`, `headers`, and `body` to the response. If the handler
@@ -18,25 +38,29 @@ import { t } from '@molecule/api-i18n';
18
38
  * i18n-translated error messages.
19
39
  *
20
40
  * @param handler - The resource handler function to wrap.
41
+ * @param options - Optional wire-format mappings; neither mutates the shared handler.
21
42
  * @returns An Express-compatible middleware function `(req, res, next) => void`.
22
43
  */
23
- export const createRequestHandler = (handler) => async (req, res, next) => {
24
- try {
25
- const response = await handler(req, res);
26
- if (response) {
27
- if (response.headers) {
28
- res.set(response.headers);
44
+ export const createRequestHandler = (handler, options = {}) => {
45
+ const adapted = adaptHandler(handler, options);
46
+ return async (req, res, next) => {
47
+ try {
48
+ const response = await adapted(req, res);
49
+ if (response) {
50
+ if (response.headers) {
51
+ res.set(response.headers);
52
+ }
53
+ res.status(response.statusCode);
54
+ res.send(response.body);
55
+ }
56
+ else {
57
+ next();
29
58
  }
30
- res.status(response.statusCode);
31
- res.send(response.body);
32
59
  }
33
- else {
34
- next();
60
+ catch (error) {
61
+ logger.error(error);
62
+ next(t('resource.error.unknownError', undefined, { defaultValue: 'Something went wrong.' }));
35
63
  }
36
- }
37
- catch (error) {
38
- logger.error(error);
39
- next(t('resource.error.unknownError', undefined, { defaultValue: 'Something went wrong.' }));
40
- }
64
+ };
41
65
  };
42
66
  //# sourceMappingURL=createRequestHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createRequestHandler.js","sourceRoot":"","sources":["../src/createRequestHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uDAAuD;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAG9C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAkCtC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAC/B,CAAC,OAAgB,EAAE,EAAE,CACrB,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;IAChF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QAExC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YAC3B,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YAC/B,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,EAAE,CAAA;QACR,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACnB,IAAI,CAAC,CAAC,CAAC,6BAA6B,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAA;IAC9F,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"createRequestHandler.js","sourceRoot":"","sources":["../src/createRequestHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uDAAuD;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAG9C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAsEtC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,CAAC,OAAgB,EAAE,UAAiC,EAAE,EAAW,EAAE,CACnE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACrF,MAAM,aAAa,GAAG,SAAS,IAAI,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAA;IAC3F,MAAM,OAAO,GACX,SAAS,IAAI,CAAC,aAAa;QACzB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC,CAAC,GAAG,CAAA;IACT,MAAM,QAAQ,GAAG,aAAa,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;IAC/D,OAAO,QAAQ,IAAI,OAAO,CAAC,WAAW;QACpC,CAAC,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC;QACnD,CAAC,CAAC,QAAQ,CAAA;AACd,CAAC,CAAA;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAgB,EAAE,UAAiC,EAAE,EAAE,EAAE;IAC5F,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC9C,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;QACvF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAExC,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;oBACrB,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gBAC3B,CAAC;gBAED,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;gBAC/B,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;iBAAM,CAAC;gBACN,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YACnB,IAAI,CAAC,CAAC,CAAC,6BAA6B,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAA;QAC9F,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
package/dist/index.d.ts CHANGED
@@ -21,6 +21,32 @@
21
21
  * Monkey-patching `res.json` (or `res.send`/`res.end`) to re-shape a handler's answer intercepts
22
22
  * nothing the handler produces, so the request it was meant to answer is left unanswered and hangs.
23
23
  *
24
+ * **A different request or response envelope is configuration.** Pass
25
+ * `mapRequest` and/or `mapResponse` as the second argument to `createRequestHandler`.
26
+ * Both may be asynchronous. The existing handler still validates and authorizes
27
+ * the adapted input. A response mapper sees the handler's errors and partial
28
+ * responses too: preserve them and its headers instead of treating every result
29
+ * as a success. `adaptHandler(handler, options)` offers the same mappings when
30
+ * another HTTP adapter will send the structured response.
31
+ * A request mapper can return `{ response: { statusCode, body } }` to answer
32
+ * before calling the handler, for example when extra validation fails or a
33
+ * filter lookup finds nothing. That response still passes through mapResponse.
34
+ *
35
+ * ```typescript
36
+ * import { createRequestHandler } from '@molecule/api-resource'
37
+ * import { handlers } from '@molecule/api-resource-user'
38
+ *
39
+ * const register = createRequestHandler(handlers.create({ name: 'User', tableName: 'users' }), {
40
+ * // Return overrides. The adapter retains framework methods and getters.
41
+ * mapRequest: (req) => ({ body: req.body?.account }),
42
+ * mapResponse: (result) => result.statusCode === 201
43
+ * ? { ...result, body: { account: result.body.props, accessToken: result.body.accessToken } }
44
+ * : result,
45
+ * })
46
+ * // Keep the route's existing rate limiter and authorization around register.
47
+ * // No mapping is inferred: a flat-body API keeps createRequestHandler(handler).
48
+ * ```
49
+ *
24
50
  * @example
25
51
  * ```typescript
26
52
  * import { z } from 'zod'
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,2BAA2B,CAAA;AACzC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,2BAA2B,CAAA;AACzC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -21,6 +21,32 @@
21
21
  * Monkey-patching `res.json` (or `res.send`/`res.end`) to re-shape a handler's answer intercepts
22
22
  * nothing the handler produces, so the request it was meant to answer is left unanswered and hangs.
23
23
  *
24
+ * **A different request or response envelope is configuration.** Pass
25
+ * `mapRequest` and/or `mapResponse` as the second argument to `createRequestHandler`.
26
+ * Both may be asynchronous. The existing handler still validates and authorizes
27
+ * the adapted input. A response mapper sees the handler's errors and partial
28
+ * responses too: preserve them and its headers instead of treating every result
29
+ * as a success. `adaptHandler(handler, options)` offers the same mappings when
30
+ * another HTTP adapter will send the structured response.
31
+ * A request mapper can return `{ response: { statusCode, body } }` to answer
32
+ * before calling the handler, for example when extra validation fails or a
33
+ * filter lookup finds nothing. That response still passes through mapResponse.
34
+ *
35
+ * ```typescript
36
+ * import { createRequestHandler } from '@molecule/api-resource'
37
+ * import { handlers } from '@molecule/api-resource-user'
38
+ *
39
+ * const register = createRequestHandler(handlers.create({ name: 'User', tableName: 'users' }), {
40
+ * // Return overrides. The adapter retains framework methods and getters.
41
+ * mapRequest: (req) => ({ body: req.body?.account }),
42
+ * mapResponse: (result) => result.statusCode === 201
43
+ * ? { ...result, body: { account: result.body.props, accessToken: result.body.accessToken } }
44
+ * : result,
45
+ * })
46
+ * // Keep the route's existing rate limiter and authorization around register.
47
+ * // No mapping is inferred: a flat-body API keeps createRequestHandler(handler).
48
+ * ```
49
+ *
24
50
  * @example
25
51
  * ```typescript
26
52
  * import { z } from 'zod'
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,2BAA2B,CAAA;AACzC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,2BAA2B,CAAA;AACzC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-resource",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Resource CRUD operations and utilities for molecule.dev.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@molecule/api-bond": "1.0.4",
43
- "@molecule/api-database": "1.2.1",
43
+ "@molecule/api-database": "1.2.2",
44
44
  "@molecule/api-locales-resource": "1.0.2",
45
45
  "@molecule/api-utilities-validation": "1.0.4",
46
46
  "@types/node": "26.6.5",