@web-ts-toolkit/access-router 0.44.0 → 0.45.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
@@ -126,12 +126,14 @@ Root entrypoint (`@web-ts-toolkit/access-router`):
126
126
  - `guard(...)` and `GuardModelCondition`, `GuardModelConditionID` types
127
127
  - `combineRoutes(...)` and `createOpenApiRouter(runtime, options)` (note: prefer `acl.createOpenApiRouter(options)` for the default runtime)
128
128
  - validation adapters: `defineRequestSchema(...)`, `fromZod(...)`, `fromYup(...)`, `fromJoi(...)`, `fromAjv(...)`, `fromStandardSchema(...)`, `fromValibot(...)`, `fromArkType(...)`, `fromIoTs(...)`, `fromSuperstruct(...)`, `fromVine(...)`
129
+ - `permissionsPlugin` (src/plugins.ts) — Mongoose schema plugin used as `schema.plugin(permissionsPlugin, { modelName })`
130
+ - logger helpers: `redactFilter`, `redactPayload`, `safeStringify`, `isLevelEnabled` + `OpLogContext` type
129
131
  - option helpers: `setGlobalOptions`, `setGlobalOption`, `getGlobalOptions`, `getGlobalOption`, `setModelOptions`, `setModelOption`, `getModelOptions`, `getModelOption`, `getModelNames`, `getModelJsonSchema`, `setDefaultModelOptions`, `setDefaultModelOption`, `getDefaultModelOptions`, `getDefaultModelOption`
130
132
 
131
133
  Subpath entrypoints:
132
134
 
133
- - `@web-ts-toolkit/access-router/advanced` — low-level runtime context, symbols (`MIDDLEWARE`, `PERMISSIONS`, ...), enums (`Codes`, `StatusCodes`), internals (`parseBody`, `parseQuery`, request schemas). Does NOT export `acl`, `defaultRuntime`, or router-creation helpers.
134
- - `@web-ts-toolkit/access-router/processors` — `copyAndDepopulate` and its `ProcessCopy` / `CopyAndDepopulateOptions` / `CopyAndDepopulateOutput` types for transforming populated documents.
135
+ - `@web-ts-toolkit/access-router/advanced` — low-level runtime context, symbols (`MIDDLEWARE`, `DATA_MIDDLEWARE`, `PERMISSIONS`, ...), enums (`Codes`, `StatusCodes`), internals (`parseBody`, `parseQuery`, `parseBodyWithSchema`, request schemas). Does NOT export `acl`, `defaultRuntime`, or router-creation helpers.
136
+ - `@web-ts-toolkit/access-router/processors` — `copyAndDepopulate`, `copyPaths`, `movePaths`, `sliceArrays`, `countPaths`, `maskPaths` and processor option types (`ProcessCopy`, `CopyAndDepopulateOptions`, `CopyAndDepopulateOutput`, `ProcessorOptions`, `SliceProcessorOptions`, `ProcessSlice`, `ProcessCount`, `ProcessMask`, `ProcessorOutput`) for transforming populated documents.
135
137
 
136
138
  ## Default runtime vs. isolated runtime
137
139
 
@@ -200,6 +202,79 @@ Advanced read/list requests can attach related model data with `include` entries
200
202
  `op: 'count'` returns exact authorized counts and ignores include pagination fields such as `args.limit`.
201
203
  `op: 'list'` materializes authorized related rows through the target model's normal list path, so target `limit`, `page`, `pageSize`, and `listHardLimit` bounds still apply to included rows.
202
204
 
205
+ ## Correlated Includes
206
+
207
+ Besides the legacy `localField`/`foreignField` joins, `include` entries accept
208
+ a correlated variant (`mode: 'correlated'`) that runs a target query **per
209
+ parent document** using values from that parent. References are explicit
210
+ structural markers — `{ "$parent": "<field>" }`, dotted paths allowed —
211
+ recognized only in filter _value_ positions (bare values, field operators
212
+ such as `$eq`/`$in`, `$in` array elements, `$and`/`$or`/`$nor` clauses,
213
+ `$elemMatch` subtrees) and as the whole `id` of identifier reads. Plain
214
+ strings are never references: `'$special'` keeps its literal query meaning.
215
+ Match a literal `{ "$parent": "x" }` object with
216
+ `{ "$escape": { "$parent": "x" } }` (use the `$eq`-wrapped form when the
217
+ literal sits in a bare field position).
218
+
219
+ Wire shapes (the originating method fixes `op`; there is no override):
220
+
221
+ ```json
222
+ { "mode": "correlated", "model": "Org", "op": "read", "path": "org", "id": { "$parent": "orgId" } }
223
+ { "mode": "correlated", "model": "Org", "op": "read", "path": "org",
224
+ "filter": { "_id": { "$parent": "orgId" }, "active": true }, "args": { "select": ["name"] } }
225
+ { "mode": "correlated", "model": "Post", "op": "list", "path": "posts",
226
+ "filter": { "authorId": { "$parent": "_id" }, "title": "$special" },
227
+ "args": { "select": ["title"], "sort": { "createdAt": -1 }, "limit": 5 } }
228
+ { "mode": "correlated", "model": "Post", "op": "count", "path": "postCount",
229
+ "filter": { "authorId": { "$parent": "_id" } } }
230
+ ```
231
+
232
+ Semantics:
233
+
234
+ - References resolve against the **immediate parent** document as fetched
235
+ from persistence (including internally loaded reference fields), before
236
+ include attachment, `decorate` hooks, and task mutation. Nested
237
+ `args.include` entries bind to the target doc of the enclosing include.
238
+ Sibling include output never feeds references.
239
+ - A missing (`undefined`) or `null` reference short-circuits that parent's
240
+ execution to the no-match shape with no target query: `read` attaches
241
+ `null`, `list` attaches `[]`, `count` attaches `0`. Target misses produce
242
+ the same shapes. The output path is always set.
243
+ - Identifier reads use the target's configured identifier behavior
244
+ (`resolveIdentifierFilter`/`genIDFilter`, custom `resolveIdFilter`
245
+ honored) with no read-to-list fallback. Counts use explicit count access
246
+ with count semantics, independent of any list limit. List pagination
247
+ (`skip`/`limit`/`page`/`pageSize`) applies **per parent** — contrast with
248
+ legacy batched list includes, whose pagination applies across the parent
249
+ set. Substituted parent values are data: objects match literally (never
250
+ become operators) and arrays are never flattened.
251
+ - Each correlated entry counts toward `maxIncludeCount`; expanded filters
252
+ are revalidated against `maxNodes`/`maxDepth`/`maxInValues`/
253
+ `maxLogicalClauses`. Total inner executions per request are bounded by
254
+ `maxCorrelatedQueries` (default `100`) and nesting by
255
+ `maxCorrelatedDepth` (default `5`); exceeding any bound fails the whole
256
+ request. Fan-out shares the request-scoped scheduler (no per-parent
257
+ reset). Target authorization denials fail the whole request with zero
258
+ target persistence queries; target runtime errors fail the whole request.
259
+ `path` must be a non-empty valid field path (not `_id`, not `$`-prefixed);
260
+ duplicates in one include array are `BadRequest`; collisions with parent
261
+ fields overwrite (legacy `setDocValue` parity).
262
+ - Referenced parent fields are added to the parent DB select like legacy
263
+ `localField`s, then trimmed unless allowed by field policy and selected.
264
+ A policy-forbidden reference still resolves — only its query effects are
265
+ visible, never the value.
266
+ - Direct count parents carry no `include`; correlated _count_ includes
267
+ attach to read/list parents only. Correlated entries are accepted in both
268
+ direct (`model-router.ts`) and root (`root-router.ts`) validators with
269
+ identical verdicts; malformed correlated input is a controlled
270
+ `BadRequest`, never a silent drop. Legacy entries are unchanged, and
271
+ legacy-shaped entries carrying `$parent` markers are rejected.
272
+
273
+ The TypeScript wire types (`ParentRef`, `CorrelatedInclude`, `Include`) are
274
+ reachable without deep imports via `@web-ts-toolkit/access-router/advanced`
275
+ (which re-exports `./interfaces`). See the website `services`,
276
+ `configuration`, `validation`, and `openapi` pages for the full contract.
277
+
203
278
  ## Import styles
204
279
 
205
280
  The package ships both a default export and named exports: