@emeryld/rrroutes-server 2.6.2 → 2.6.3

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
@@ -169,7 +169,7 @@ const server = createRRRoute(app, {
169
169
 
170
170
  ### Batch endpoint helper (`batchLeaf`)
171
171
 
172
- Use `batchLeaf` to register one endpoint that dispatches multiple already-registered controllers by encoded route keys.
172
+ Use `batchLeaf` to register one endpoint that dispatches multiple already-registered controllers by alias, while each entry carries its encoded route key.
173
173
 
174
174
  ```ts
175
175
  import { batchLeaf } from '@emeryld/rrroutes-server'
@@ -185,29 +185,31 @@ Client request body shape:
185
185
 
186
186
  ```ts
187
187
  {
188
- [encodeURIComponent('GET /v1/users/:userId')]: {
188
+ getUser: {
189
+ encodedLeaf: encodeURIComponent('GET /v1/users/:userId'),
189
190
  params: { userId: 'u_1' },
190
191
  },
191
- [encodeURIComponent('PATCH /v1/users/:userId')]: {
192
+ updateUser: {
193
+ encodedLeaf: encodeURIComponent('PATCH /v1/users/:userId'),
192
194
  params: { userId: 'u_1' },
193
195
  body: { name: 'Emery' },
194
196
  },
195
197
  }
196
198
  ```
197
199
 
198
- Response shape (same encoded keys):
200
+ Response shape (same alias keys):
199
201
 
200
202
  ```ts
201
203
  {
202
- [encodeURIComponent('GET /v1/users/:userId')]: { out: { ... }, meta: ... },
203
- [encodeURIComponent('PATCH /v1/users/:userId')]: { out: { ... }, meta: ... },
204
+ getUser: { out: { ... }, meta: ... },
205
+ updateUser: { out: { ... }, meta: ... },
204
206
  }
205
207
  ```
206
208
 
207
209
  Notes:
208
210
 
209
211
  - Register controllers before calling `batchLeaf(...)`; unknown keys fail at runtime.
210
- - Dispatch uses `server.invoke(...)` for each entry, so each sub-call runs per-leaf parsing, `buildCtx`, `route.before`, handler execution, and output validation.
212
+ - Dispatch uses `server.invoke(...)` in parallel for each entry, so each sub-call runs per-leaf parsing, `buildCtx`, `route.before`, handler execution, and output validation.
211
213
  - Batch dispatch does not replay the full global middleware chain of the original route registration (`sanitizer`, `preCtx`, `postCtx`, Multer).
212
214
 
213
215
  ### Middleware order and ctx usage
package/dist/index.cjs CHANGED
@@ -915,28 +915,36 @@ function batchLeaf(server, path, registry, options) {
915
915
  const body = req.body;
916
916
  if (!isPlainObject2(body)) {
917
917
  throw new Error(
918
- "Batch request body must be a plain object keyed by encoded route identifiers."
918
+ "Batch request body must be a plain object keyed by branch aliases."
919
919
  );
920
920
  }
921
- const output = {};
922
- for (const [encodedKey, value] of Object.entries(body)) {
923
- const decodedKey = decodeURIComponent(encodedKey);
924
- const leaf = registry.byKey[decodedKey];
925
- if (!leaf) {
926
- throw new Error(`Unknown batch route key: ${decodedKey}`);
927
- }
928
- const payload = isPlainObject2(value) ? value : {};
929
- output[encodedKey] = await server.invoke(decodedKey, {
930
- req,
931
- res,
932
- next,
933
- params: payload.params,
934
- query: payload.query,
935
- body: payload.body,
936
- bodyFiles: payload.bodyFiles
937
- });
938
- }
939
- res.json(output);
921
+ const entries = Object.entries(body);
922
+ const outputEntries = await Promise.all(
923
+ entries.map(async ([alias, value]) => {
924
+ const payload = isPlainObject2(value) ? value : {};
925
+ if (typeof payload.encodedLeaf !== "string" || payload.encodedLeaf.length === 0) {
926
+ throw new Error(
927
+ `Batch entry "${alias}" must include a non-empty "encodedLeaf" string.`
928
+ );
929
+ }
930
+ const decodedKey = decodeURIComponent(payload.encodedLeaf);
931
+ const leaf = registry.byKey[decodedKey];
932
+ if (!leaf) {
933
+ throw new Error(`Unknown batch route key: ${decodedKey}`);
934
+ }
935
+ const result = await server.invoke(decodedKey, {
936
+ req,
937
+ res,
938
+ next,
939
+ params: payload.params,
940
+ query: payload.query,
941
+ body: payload.body,
942
+ bodyFiles: payload.bodyFiles
943
+ });
944
+ return [alias, result];
945
+ })
946
+ );
947
+ res.json(Object.fromEntries(outputEntries));
940
948
  } catch (err) {
941
949
  next(err);
942
950
  }