@web-ts-toolkit/access-router 0.50.0 → 0.52.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 +1582 -117
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +693 -302
- package/advanced.mjs +641 -250
- package/index.d.mts +85 -2
- package/index.d.ts +85 -2
- package/index.js +10873 -7616
- package/index.mjs +10887 -7632
- package/llms.txt +36 -314
- package/package.json +3 -3
- package/{parsers-DhUuvj9B.d.mts → parsers-BnmChTfF.d.mts} +2621 -1934
- package/{parsers-DhUuvj9B.d.ts → parsers-BnmChTfF.d.ts} +2621 -1934
package/llms.txt
CHANGED
|
@@ -1,316 +1,38 @@
|
|
|
1
1
|
# @web-ts-toolkit/access-router
|
|
2
2
|
|
|
3
|
-
ACL-aware Express routers
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Use root `OperationAccess` / `ModelRouterOptions` / `DataRouterOptions` types;
|
|
40
|
-
`Validation` = boolean | string | string[] | GuardHook. Root imports are canonical:
|
|
41
|
-
default `acl` for the shared runtime, named `createAccessRuntime` for isolation.
|
|
42
|
-
`PermissionSchema` / `FieldOperationAccess` grant fields by base operation, not variants.
|
|
43
|
-
|
|
44
|
-
<!-- doc-example: partial -->
|
|
45
|
-
```ts
|
|
46
|
-
import acl from '@web-ts-toolkit/access-router';
|
|
47
|
-
|
|
48
|
-
// Register User before constructing this model router.
|
|
49
|
-
const router = acl.createRouter('User', {
|
|
50
|
-
basePath: '/users',
|
|
51
|
-
operationAccess: { list: true, read: true, basicRead: false, advancedList: 'isAdmin' },
|
|
52
|
-
permissionSchema: { name: true },
|
|
53
|
-
});
|
|
54
|
-
router.operationAccess('basicRead', false); // Live per-key update, not object replacement.
|
|
55
|
-
router.setOption('operationAccess.basicRead', undefined); // Inherit exact model default, then read.
|
|
56
|
-
router.operationAccess({ list: true, read: true, advancedList: false }); // Replace whole rule object.
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Model pairs (paths relative to basePath, default segments):
|
|
60
|
-
|
|
61
|
-
| Base | Basic key / endpoint | Advanced key / endpoint |
|
|
62
|
-
| --- | --- | --- |
|
|
63
|
-
| list | basicList: GET / | advancedList: POST /__query |
|
|
64
|
-
| read | basicRead: GET /:id | advancedRead: POST /__query/:id and /__query/__filter |
|
|
65
|
-
| create | basicCreate: POST / | advancedCreate: POST /__mutation |
|
|
66
|
-
| update | basicUpdate: PATCH /:id | advancedUpdate: PATCH /__mutation/:id |
|
|
67
|
-
| upsert | basicUpsert: PUT / | advancedUpsert: PUT /__mutation |
|
|
68
|
-
| count | basicCount: GET /count | advancedCount: POST /count |
|
|
69
|
-
| distinct | basicDistinct: GET /distinct/:field | advancedDistinct: POST /distinct/:field |
|
|
70
|
-
|
|
71
|
-
Data supports only four list/read keys and both advanced read paths; set idParam: 'id'
|
|
72
|
-
explicitly for the illustrated data identifier paths. Each model sub
|
|
73
|
-
field supports subs.<field>.basicList / advancedList / basicRead / advancedRead for
|
|
74
|
-
GET /:id/<field>, POST /:id/<field>/__query, GET /:id/<field>/:subId, and
|
|
75
|
-
POST /:id/<field>/:subId/__query respectively. GET /new, DELETE /:id and subdocument
|
|
76
|
-
create/delete/both PATCH routes keep new/delete/sub create/delete/update base guards.
|
|
77
|
-
|
|
78
|
-
- Exact live variant replaces the base; only undefined inherits base exact →
|
|
79
|
-
operationAccess.default → scalar shorthand. False/empty-array/false-returning hooks
|
|
80
|
-
are terminal. Strings AND space-separated names; arrays OR entries; async hooks use
|
|
81
|
-
request `this` and AccessRouterPermissions. Operational errors do not fall back.
|
|
82
|
-
- Model exact lookup includes the runtime-default same path before model base/default/
|
|
83
|
-
shorthand. Options assign shallowly; no recursive default merge or broadcast of new
|
|
84
|
-
defaults over stored model values. Clearing a stored variant may expose a live default
|
|
85
|
-
variant. getDefaultModelOption is exact, not alias/default resolution. Data has no
|
|
86
|
-
model defaults. Use operationAccess: true or { default: true } for broad base fallback.
|
|
87
|
-
- Sub precedence: exact field variant → field base → defined scalar/closed field
|
|
88
|
-
object → top variant only for an absent field → top base. { items: { read: true } }
|
|
89
|
-
closes items list even with top list: true. No nested .default fallback. Legacy scalar
|
|
90
|
-
subs is accepted but does not guard absent fields; use explicit per-field rules.
|
|
91
|
-
- Denial is HTTP 401, not route omission. getEndpoints()/OpenAPI retain denied routes.
|
|
92
|
-
HEAD follows its basic GET. { list: true, read: true, basicRead: false } denies only
|
|
93
|
-
GET-by-id/HEAD among top-level routes while both advanced reads work; sub fields use
|
|
94
|
-
their documented precedence. { list: true, advancedList: false }
|
|
95
|
-
denies only advanced list; other endpoint guards remain independent.
|
|
96
|
-
- Setters mutate live policy. Dotted paths are setter arguments, not flat constructor
|
|
97
|
-
properties. operationAccess(key, definedValue) updates one key; object forms replace
|
|
98
|
-
the entire operationAccess value shallowly. Use setOption(path, undefined) to clear.
|
|
99
|
-
router.options is a frozen construction snapshot; getter results are frozen snapshots.
|
|
100
|
-
Read current runtime values again; generic getModelOption/getDataOption is not the
|
|
101
|
-
route resolver (exact getters expose variant paths, including model-default fallback).
|
|
102
|
-
- Variants do not grant fields, row/filter/hook access, target access, or mutation-output
|
|
103
|
-
visibility. Model read misses/tryList still require base list; advancedList: true cannot
|
|
104
|
-
replace denied list, basicList: false cannot block base-list retry. Forbidden/BadRequest
|
|
105
|
-
never retry. Upsert branches do not require create/update route variants. Root entries
|
|
106
|
-
retain base op names/checks (entry 401 within HTTP 200); variant op names are invalid.
|
|
107
|
-
Trusted service calls retain existing checks, with no req.method variant inference.
|
|
108
|
-
- Wire populateAccess/descriptor access is only list/read (PopulateAccess from /advanced).
|
|
109
|
-
Shared Core populate rejects all fourteen reserved variants in effective options and
|
|
110
|
-
descriptors, including generic legacy include/subquery options, before target work.
|
|
111
|
-
Other trusted non-reserved custom strings keep runtime compatibility. BadRequest maps
|
|
112
|
-
to direct 400/root entry 400 bad_request; legacy read/list target BadRequest propagates
|
|
113
|
-
instead of silent omission. See README "Basic and advanced operation access" for the
|
|
114
|
-
full fallback examples, closed-field caveat, setters, and runnable data-router split.
|
|
115
|
-
|
|
116
|
-
Virtuals (computed fields): package-level async request-aware derived output
|
|
117
|
-
fields, reachable from the root and the existing `./advanced` barrel (no new
|
|
118
|
-
subpaths). One public `select` covers persisted + virtual fields; `dependsOn`
|
|
119
|
-
names are top-level persisted fields relative to the definition scope.
|
|
120
|
-
Selected virtual outputs are optional (`Partial`): authorization, absent
|
|
121
|
-
dependencies, `undefined`, and fail-closed throws can omit them. Virtuals
|
|
122
|
-
compute for lean and hydrated results after internal document permissions and
|
|
123
|
-
before `decorate`/`decorateAll`/tasks; internal-only dependencies are stripped
|
|
124
|
-
before those hooks. Every document-bearing output is covered (direct
|
|
125
|
-
list/read/create/update/upsert/new, populate/include targets incl. nested,
|
|
126
|
-
embedded `sub` scopes, subdocument routes) with target-model trimming on both
|
|
127
|
-
populate and include. Virtuals are output-only: never in persisted
|
|
128
|
-
projections, write admission, or DB sort/filter/distinct. `new()` ignores
|
|
129
|
-
`args.select`; select grammar is unchanged (arbitrary strings accepted).
|
|
130
|
-
Performance: prefer finalized populate/include data over per-row DB calls
|
|
131
|
-
(measured 24 rows: per-row `findById` = 25 driver reads vs populated getter =
|
|
132
|
-
2 reads, same peak bound); `maxHookConcurrency` (default 10) bounds
|
|
133
|
-
concurrently awaited getter + row-finalization orchestration per
|
|
134
|
-
request/runtime only, not query counts. Parent getters see only finalized
|
|
135
|
-
related/embedded outputs, never stripped private fields. See README "Virtuals
|
|
136
|
-
(computed fields)" and "Virtuals compatibility notes" (populate trimming now
|
|
137
|
-
applies incl. no-virtual targets — intended change, not silent compat).
|
|
138
|
-
|
|
139
|
-
<!-- doc-example: partial -->
|
|
140
|
-
```ts
|
|
141
|
-
import type { ModelRouterOptions } from '@web-ts-toolkit/access-router';
|
|
142
|
-
import type { SelectedPublicOutput } from '@web-ts-toolkit/access-router/advanced';
|
|
143
|
-
|
|
144
|
-
interface VirtUser {
|
|
145
|
-
name: string;
|
|
146
|
-
address: string;
|
|
147
|
-
}
|
|
148
|
-
interface VirtUserVirtuals {
|
|
149
|
-
fullAddress: string;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Persisted VirtUser has no fullAddress; the virtual computes it.
|
|
153
|
-
const virtualOptions: ModelRouterOptions<VirtUser, VirtUserVirtuals> = {
|
|
154
|
-
permissionSchema: {
|
|
155
|
-
name: { read: true },
|
|
156
|
-
address: { read: 'canViewAddress' },
|
|
157
|
-
fullAddress: { read: 'canViewAddress' },
|
|
158
|
-
},
|
|
159
|
-
virtuals: {
|
|
160
|
-
fullAddress: {
|
|
161
|
-
dependsOn: ['address'],
|
|
162
|
-
read: async (doc) => (doc.address === undefined ? undefined : `addr:${doc.address}`),
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
type VirtualSelected = SelectedPublicOutput<VirtUser, ['name', 'fullAddress'], VirtUserVirtuals>;
|
|
167
|
-
const virtualPresent: VirtualSelected = { name: 'Ada', fullAddress: 'addr:a1' };
|
|
168
|
-
const virtualOmitted: VirtualSelected = { name: 'Ada' };
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Validation adapters:
|
|
172
|
-
|
|
173
|
-
<!-- doc-example: complete-runtime -->
|
|
174
|
-
```ts
|
|
175
|
-
import { z } from 'zod';
|
|
176
|
-
import { fromZod, defineRequestSchema } from '@web-ts-toolkit/access-router';
|
|
177
|
-
const validator = fromZod(z.object({ name: z.string() }));
|
|
178
|
-
const schema = defineRequestSchema(validator);
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
AJV (`fromAjv`, `AjvValidatorLike<T>` from the root export; install AJV in your app):
|
|
182
|
-
sync boolean verdicts use same-turn snapshotted `validate.errors`; true returns input,
|
|
183
|
-
false returns issues. `$async: true` means data-returning promises/thenables: every
|
|
184
|
-
fulfillment (false/true/null/scalars/objects) succeeds with that value. Async diagnostics
|
|
185
|
-
come only from rejection-carried AJV ValidationError (`ajv: true`, `validation: true`,
|
|
186
|
-
errors array), never shared errors; other exceptions preserve operational identity.
|
|
187
|
-
Untagged promises/thenables now fail with TypeError and are safely observed; sync
|
|
188
|
-
non-booleans also fail. Migration: retain the real compiled validator/tag, or use a
|
|
189
|
-
literal `$async: true as const` on structural data-returning validators; async boolean
|
|
190
|
-
verdict wrappers must switch to data/rejection semantics or RequestSchemaValidator
|
|
191
|
-
results. An errors property alone does not identify validation failure. Real AJV
|
|
192
|
-
overloads infer output without casts; fromAjv<T>(sync) specifies output for a structural
|
|
193
|
-
boolean validator without a type guard, structural async output is inferred from its
|
|
194
|
-
promise. AJV types can erase the async tag to their sync base interface,
|
|
195
|
-
so the runtime tag remains authoritative. Use an AJV `AsyncSchema`-typed schema for
|
|
196
|
-
compile/compileAsync to select its async overload; inline schemas may match AJV's
|
|
197
|
-
earlier sync overload even with `$async: true`. See README "AJV validation contract".
|
|
198
|
-
|
|
199
|
-
Isolated runtime (no global state shared with default `acl`):
|
|
200
|
-
|
|
201
|
-
<!-- doc-example: complete-runtime -->
|
|
202
|
-
```ts
|
|
203
|
-
import { createAccessRuntime } from '@web-ts-toolkit/access-router';
|
|
204
|
-
import mongoose, { type Model } from 'mongoose';
|
|
205
|
-
|
|
206
|
-
type User = { name?: string };
|
|
207
|
-
|
|
208
|
-
const runtime = createAccessRuntime();
|
|
209
|
-
runtime.setGlobalOptions({ globalPermissions: () => [] });
|
|
210
|
-
const userSchema = new mongoose.Schema<User>({ name: String });
|
|
211
|
-
const UserModel: Model<User> = mongoose.model<User>('User', userSchema);
|
|
212
|
-
runtime.createRouter(UserModel, { basePath: '/users' });
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
Subpath imports:
|
|
216
|
-
|
|
217
|
-
<!-- doc-example: complete-runtime -->
|
|
218
|
-
```ts
|
|
219
|
-
import { copyAndDepopulate, type CopyAndDepopulateOutput } from '@web-ts-toolkit/access-router/processors';
|
|
220
|
-
import { parseBody, Codes, MIDDLEWARE } from '@web-ts-toolkit/access-router/advanced';
|
|
221
|
-
|
|
222
|
-
type DepopulatedItems = { items: string[]; snapshot: Array<{ _id: string; name: string }> };
|
|
223
|
-
const typed = copyAndDepopulate<DepopulatedItems>(
|
|
224
|
-
{ items: [{ _id: 'x', name: 'x' }] },
|
|
225
|
-
[{ src: 'items', dest: 'snapshot' }],
|
|
226
|
-
{ mutable: false },
|
|
227
|
-
);
|
|
228
|
-
const conservative: CopyAndDepopulateOutput = copyAndDepopulate({ items: [{ _id: 'x' }] }, [
|
|
229
|
-
{ src: 'items', dest: 'snapshot' },
|
|
230
|
-
]);
|
|
231
|
-
void [typed, conservative];
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Correlated includes: besides legacy `localField`/`foreignField` joins,
|
|
235
|
-
`include` entries accept `mode: 'correlated'` with `{ "$parent": "<field>" }`
|
|
236
|
-
markers in filter value positions (or as the whole `id`). Each entry runs per
|
|
237
|
-
parent against the immediate parent snapshot: reads attach doc-or-`null`,
|
|
238
|
-
lists attach per-parent paginated arrays, counts attach numbers (missing/null
|
|
239
|
-
refs attach `null`/`[]`/`0` with no query). Plain `'$special'` strings stay
|
|
240
|
-
literal; match literal `{ "$parent": "x" }` objects with
|
|
241
|
-
`{ "$escape": { "$parent": "x" } }`. A bare field escape resolves to literal
|
|
242
|
-
equality: `{ note: { $escape: { $parent: 'x' } } }` becomes
|
|
243
|
-
`{ note: { $eq: { $parent: 'x' } } }`. Explicit `$eq` stays equivalent;
|
|
244
|
-
operator operands and array elements stay literal values without double wrapping.
|
|
245
|
-
Resolution is single-pass and parent-independent, including missing/null parent
|
|
246
|
-
fields; malformed escapes are rejected and expanded complexity budgets still apply.
|
|
247
|
-
Older servers with the bare-escape defect need explicit `$eq`; upgrade the server
|
|
248
|
-
to use the corrected bare form. Identifier behavior, per-op target
|
|
249
|
-
authorization (denials fail the whole request with zero target queries), and
|
|
250
|
-
bounds (`maxIncludeCount`, revalidated `maxNodes`/`maxDepth`/`maxInValues`/
|
|
251
|
-
`maxLogicalClauses`, `maxCorrelatedQueries` default 100,
|
|
252
|
-
`maxCorrelatedDepth` default 5) follow the README "Correlated Includes"
|
|
253
|
-
section. Wire types (`ParentRef`, `CorrelatedInclude`, `Include`) are
|
|
254
|
-
available from `@web-ts-toolkit/access-router/advanced` without deep
|
|
255
|
-
imports.
|
|
256
|
-
|
|
257
|
-
Business boundary contracts (see the corresponding README sections):
|
|
258
|
-
|
|
259
|
-
- Include `path` is output-only. Legacy/correlated read/list/count paths cannot equal,
|
|
260
|
-
descend from, or contain the receiving model's `documentPermissionField` (default
|
|
261
|
-
`_permissions`), including equivalent legacy bracket paths. Nested receiving model
|
|
262
|
-
settings apply; preflight rejects before target persistence. Direct HTTP 400 / root
|
|
263
|
-
entry 400 `bad_request`; ordinary output collisions remain allowed.
|
|
264
|
-
- Nested updates patch authorized policy leaves, preserving omitted/protected siblings.
|
|
265
|
-
Whole-authorized objects/arrays replace; explicit indexed grants patch that leaf.
|
|
266
|
-
Send nested JSON, not literal dotted keys or Mongo update operators. Model prepare
|
|
267
|
-
output is trusted and not re-filtered; at partial policy ancestors omitted children
|
|
268
|
-
survive. Use explicit null/undefined to clear/unset, or trusted transform document
|
|
269
|
-
setters for whole replacement. Never blindly copy `context.originalData` back in.
|
|
270
|
-
- Subdocument create/update output requires post-save parent read access. Full-array
|
|
271
|
-
create also requires sub list AND read guards/row filters; targeted single/bulk
|
|
272
|
-
updates require sub read only. All use read fields plus `_id`, stored array order,
|
|
273
|
-
and visible counts. Hidden create: success/list/created, `data: [], count: 0`, HTTP
|
|
274
|
-
201; bulk update: success/list/success, empty/0, HTTP 200; single update:
|
|
275
|
-
success/single/success, `data: null`, HTTP 200. Root entries use those statuses inside
|
|
276
|
-
HTTP 200. These are completed writes: do not retry merely for empty output.
|
|
277
|
-
- Legacy include counts use one grouped aggregate per include execution, exact distinct
|
|
278
|
-
authorized target-document counts beyond list limits/pagination, and target-schema
|
|
279
|
-
casting of ACL filters and foreign operands with original parent-key association.
|
|
280
|
-
Cast failures are BadRequest; terminal false count rows retain legacy omitted output.
|
|
281
|
-
Query setters/casting do not execute countDocuments middleware/plugins; arbitrary
|
|
282
|
-
aggregate pipelines are not auto-cast.
|
|
283
|
-
- `maxBulkConcurrency` (default 10) admits awaited adapter/document persistence per
|
|
284
|
-
request AND owning runtime, across root entries and nested includes; pools are
|
|
285
|
-
independent across requests/runtimes. Bulk create admits singleton arrays per item,
|
|
286
|
-
keeps result order and submits all items despite rejection. Recursive orchestration
|
|
287
|
-
holds no permit; limit 1 works. Root `maxConcurrentOperations` bounds whole operations
|
|
288
|
-
per order group separately. Permits release on errors without refunding cumulative
|
|
289
|
-
`maxCorrelatedQueries` (100); `maxCorrelatedDepth` (5) is a separate nesting bound.
|
|
290
|
-
One admitted operation can issue multiple driver commands through Mongoose middleware
|
|
291
|
-
or populate. This is not a global connection limit, throughput guarantee, transaction,
|
|
292
|
-
or governance of direct DB/network calls in trusted application hooks.
|
|
293
|
-
|
|
294
|
-
## Gotchas
|
|
295
|
-
|
|
296
|
-
- peer dependencies: `express >= 5` and `mongoose >= 8`
|
|
297
|
-
- default export `acl` is preferred for the default-runtime API; named exports are also available
|
|
298
|
-
- `acl.createOpenApiRouter(options)` uses the default runtime; the standalone `createOpenApiRouter(runtime, options)` export requires the runtime as the first argument
|
|
299
|
-
- `createRouter('User', ...)` accepts a Mongoose model name OR a `mongoose.Model` instance — passing the instance registers the model with the runtime and preserves non-default `mongoose.createConnection()` instances
|
|
300
|
-
- isolated runtimes do not silently read process-global `mongoose.models`; register the model first or pass the `mongoose.Model` instance to `runtime.createRouter(...)`
|
|
301
|
-
- `/advanced` exports interfaces/wire types, symbols, enums and request validation helpers, not runtime-context functions or runtime instances/factories. Use root `createAccessRuntime`/`AccessRuntime`/`defaultRuntime`, or `acl.createRouter(...)` for the default runtime.
|
|
302
|
-
- `/processors` `copyAndDepopulate(...)` defaults to conservative `CopyAndDepopulateOutput`; pass an explicit output type when the transformed shape is known. Unsafe paths and missing id fields throw plain `Error` instances.
|
|
303
|
-
- `createDataRouter(...)` holds data in-memory; use it for lightweight/non-persisted resources
|
|
304
|
-
- ACL filter value `false` is a terminal denial. Trusted `overrideFilter` hooks may replace ordinary filters before base-filter composition, but are not called for an existing denial; returning `false` also denies the query.
|
|
305
|
-
- Model service `args.overrides.filter` (findOne/find/updateOne/upsert) and `args.overrides.idFilter` (findById/updateById): false returns Forbidden without fallback filter/ID generation or persistence, including client subqueries. Nullish uses normal generation; trusted object filters replace generated policy, while object idFilters still receive row policy.
|
|
306
|
-
- `Service.exists`: terminal explicit/base/override false now returns a Forbidden ErrorResult before adapter dispatch in both includeId modes (previously successful false/null). Allowed matches/misses remain true/false, or `{ _id }`/null with includeId. Check success before data.
|
|
307
|
-
- Public `_read`/`_readFilter` tryList never retries Forbidden/BadRequest. Authorized misses retain list fallback and its operation guard. `_upsert` with _id propagates denied exists as Forbidden (previously Unauthorized); allowed miss remains Unauthorized, with no create in either case. Generated direct/root-entry denial status is 403 (root envelope 200). Custom Express routes must map ErrorResult codes to HTTP status themselves; res.json does not do this automatically.
|
|
308
|
-
- Shared-request cross-runtime composition keeps independent state: runtime B never reuses runtime A's permissions or base-filter cache, even with the same `requestPermissionField` name. Same-runtime middleware repeats reuse state without rerunning `globalPermissions`. Pre-populated request permission fields are application-supplied and preserved.
|
|
309
|
-
- Advanced mutation routes persist the final parsed body: nested `data` validators run before the whole-body (`default`) validator, and only final `data`/`select`/`populate`/`tasks` plus allowed `options` (`includePermissions`/`includeFieldPermissions`/`populateAccess`, plus `returningAll` for update/upsert) reach the service. Missing body options fall back to `returning_all`/`include_permissions` query params. Simple GET routes also accept `include_field_permissions` query params.
|
|
310
|
-
- Router permission-output postures (absolute over client input and per-operation `defaults`): `stripPermissionsField: true` removes the whole permissions field from outputs (output matches `includePermissions: false` with the field, including its placeholder, deleted; enforcement still runs); `disableFieldPermissions: true` never computes `_view`/`_edit` maps while `includePermissions` still controls `docPermissions`-hook keys.
|
|
311
|
-
- `package.json` declares `sideEffects` array listing the bundled runtime entries that run the lazy `mongoose-schema-jsonschema` patch on first runtime construction — bundlers must retain those entries
|
|
312
|
-
|
|
313
|
-
## Pointers
|
|
314
|
-
|
|
315
|
-
- README: installation, quickstart, exports, operation-access matrix/fallback/live updates, runnable data split, runtime isolation, virtuals (computed fields) + access matrix + N+1/bounded-concurrency guidance + compatibility notes, include safety/counts, nested updates/trusted hooks, hidden subdocument responses, persistence limits and compatibility notes
|
|
316
|
-
- website/docs/packages/access-router/: full documentation (routing, configuration, hooks, validation, openapi)
|
|
3
|
+
> ACL-aware Express/Mongoose routers, in-memory data routes and ordered root batches.
|
|
4
|
+
> Requires Node >=22, Express >=5 and Mongoose >=8. The installed README is the guide;
|
|
5
|
+
> package.json selects matching CJS/ESM JavaScript and .d.ts/.d.mts declarations.
|
|
6
|
+
|
|
7
|
+
## Entry points
|
|
8
|
+
|
|
9
|
+
- Root `@web-ts-toolkit/access-router`: default `acl` (shared runtime; named `acl` is identical), named `createAccessRuntime()` (isolated API), routers, guards, validators and public policy/configuration types. The factory's `api.runtime` is its AccessRuntime owner.
|
|
10
|
+
- `@web-ts-toolkit/access-router/advanced`: request/service/wire types, parsers, symbols and enums; no runtime factory or standalone Service export. Acquire services through an initialized router/request.
|
|
11
|
+
- `@web-ts-toolkit/access-router/processors`: named copyAndDepopulate/copyPaths/movePaths/sliceArrays/countPaths/maskPaths and their option/output types.
|
|
12
|
+
- [Imports and CJS form](README.md#import-styles) · [Full public exports](README.md#main-exports) · [Typed processor example](README.md#subpath-import-example)
|
|
13
|
+
|
|
14
|
+
## Start and configure
|
|
15
|
+
|
|
16
|
+
- [Installation](README.md#installation) · [Shortest public-read router](README.md#quick-start)
|
|
17
|
+
- [Configuration-layer map](README.md#configuration-layers) · [Assignment, precedence and capture](README.md#assignment-precedence-and-capture) · [Wire placement](README.md#wire-placement-and-option-names)
|
|
18
|
+
- [Names with different jobs](README.md#names-with-different-jobs) · [skim: deprecated compatibility no-op](README.md#skim-compatibility-no-op)
|
|
19
|
+
- [Runtime isolation and nullish grants](README.md#default-runtime-vs-isolated-runtime) · [Factory overloads](README.md#createrouter-overloads) · [Optional metadata-alias plugin](README.md#optional-permissions-plugin)
|
|
20
|
+
- [Operation matrix and fallback](README.md#basic-and-advanced-operation-access) · [Live setters](README.md#live-updates-and-option-snapshots)
|
|
21
|
+
- [Data list/read](README.md#data-routers) · [Data ownership/native values](README.md#in-memory-data-ownership) · [Runnable data example](README.md#runnable-data-router-split)
|
|
22
|
+
- [Root batches: maxOrderGroups bounds both group count and numeric order](README.md#root-batch-bounds)
|
|
23
|
+
- [Request validation and OpenAPI](README.md#request-validation-and-openapi) · [AJV sync/async contract](README.md#ajv-validation-contract)
|
|
24
|
+
|
|
25
|
+
## Business and migration contracts
|
|
26
|
+
|
|
27
|
+
- [Trusted service authorization](README.md#trusted-service-authorization) · [Request admission](README.md#public-request-admission-and-backend-options): policy is server-only; wire/default control slots reject it.
|
|
28
|
+
- [Selection, grants and document handles](README.md#selection-grants-and-document-results) · [Runnable Mongo authorization example](README.md#runnable-authorization-and-handle-reuse)
|
|
29
|
+
- [Independent mutation input](README.md#independent-mutation-input-authority) · [Scoped subdocuments](README.md#dedicated-subdocument-authorization) · [Successful hidden-write responses](README.md#subdocument-writes-with-hidden-responses)
|
|
30
|
+
- [Row scope vs query transformation](README.md#backend-row-authorization-and-filter-migration) · [Denial vs miss](README.md#denial-and-request-validation) · [Whole-value distinct](README.md#distinct-whole-value-request-authorization)
|
|
31
|
+
- [Removed overrides migration](README.md#breaking-release-service-overrides-migration) · [Named helper migration and data compatibility](README.md#breaking-release-named-helpers-and-default-behavior)
|
|
32
|
+
- [Includes/count cardinality](README.md#include-cardinality) · [Bounded numeric Map counts and Set-to-number migration](README.md#numeric-grouped-count-service-api-breaking-migration) · [Source/attachment provenance](README.md#safe-include-output-paths)
|
|
33
|
+
- [Correlated includes and literal escapes](README.md#correlated-includes) · [Nested writes and trusted hooks](README.md#nested-updates-and-trusted-hooks)
|
|
34
|
+
- [Virtuals, literal-filter preservation and output compatibility](README.md#virtuals-computed-fields) · [Request/runtime reentrant hook chains](README.md#hook-admission-and-reentrant-service-calls) · [Separate persistence limits](README.md#persistence-concurrency-and-resource-limits)
|
|
35
|
+
|
|
36
|
+
## Additional examples
|
|
37
|
+
|
|
38
|
+
- [Website guide](https://web-ts-toolkit.pages.dev/docs/packages/access-router): routing, configuration, services, hooks, validation, OpenAPI and processors. The installed README/declarations remain sufficient for the contracts above; use package-boundary imports.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@web-ts-toolkit/access-router",
|
|
3
3
|
"description": "ACL-aware Express routers and in-memory data services for Mongoose-backed APIs",
|
|
4
4
|
"homepage": "https://web-ts-toolkit.pages.dev/docs/packages/access-router",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.52.0",
|
|
6
6
|
"sideEffects": [
|
|
7
7
|
"./**/index.js",
|
|
8
8
|
"./**/index.mjs",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"node": ">=22"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@web-ts-toolkit/express-json-router": "0.
|
|
61
|
-
"@web-ts-toolkit/utils": "0.
|
|
60
|
+
"@web-ts-toolkit/express-json-router": "0.52.0",
|
|
61
|
+
"@web-ts-toolkit/utils": "0.52.0",
|
|
62
62
|
"just-diff": "^6.0.2",
|
|
63
63
|
"mongoose-schema-jsonschema": "^4.0.1",
|
|
64
64
|
"sift": "^17.1.3",
|