@web-ts-toolkit/access-router 0.45.0 → 0.47.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 +255 -9
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +40 -17
- package/advanced.mjs +42 -18
- package/index.d.mts +5 -186
- package/index.d.ts +5 -186
- package/index.js +622 -291
- package/index.mjs +485 -152
- package/llms.txt +68 -3
- package/package.json +3 -3
- package/{parsers-DppKW-Fd.d.mts → parsers-BidvJUu7.d.mts} +353 -9
- package/{parsers-DppKW-Fd.d.ts → parsers-BidvJUu7.d.ts} +353 -9
package/README.md
CHANGED
|
@@ -110,6 +110,29 @@ filter before the base filter is applied, but it is not called for an existing `
|
|
|
110
110
|
that denial. Returning `false` from `overrideFilter` also denies the query. Empty or absent filters retain their
|
|
111
111
|
normal unrestricted meaning unless a base filter restricts them.
|
|
112
112
|
|
|
113
|
+
**Model service denial contract:** internal `findOne`/`find`/`updateOne`/`upsert`
|
|
114
|
+
`args.overrides.filter` and `findById`/`updateById` `args.overrides.idFilter` distinguish
|
|
115
|
+
`false` from omitted/`undefined`/`null`. False returns a `forbidden` ErrorResult without
|
|
116
|
+
fallback filter/identifier generation or persistence (including client subqueries).
|
|
117
|
+
Nullish overrides use normal generation. Trusted object filter overrides replace the
|
|
118
|
+
generated filter; object **idFilter** overrides replace identifier resolution but still
|
|
119
|
+
receive row policy. These service arguments are trusted application inputs.
|
|
120
|
+
|
|
121
|
+
`Service.exists(filter, { access: 'read', includeId: false })` returns a successful
|
|
122
|
+
boolean for allowed queries; `includeId: true` returns an `{ _id }` record or `null`.
|
|
123
|
+
Explicit/base/override terminal false now returns `forbidden` **before adapter dispatch**
|
|
124
|
+
in both modes, rather than a successful false/null. An allowed filter that matches no
|
|
125
|
+
visible row is still a successful miss. Check `result.success` before using `result.data`.
|
|
126
|
+
|
|
127
|
+
Public `_read` and `_readFilter` with `tryList: true` (the default) never retry Forbidden
|
|
128
|
+
or BadRequest. An authorized read miss can still fall back to list policy, subject to
|
|
129
|
+
list operation access. This deliberately removes list fallback for terminal read denial.
|
|
130
|
+
Public `_upsert` with an `_id` propagates denied update-policy existence as Forbidden
|
|
131
|
+
(previously Unauthorized); an allowed miss remains Unauthorized and neither case creates.
|
|
132
|
+
Generated direct routes map Forbidden to HTTP 403; root batches use per-entry 403 within
|
|
133
|
+
HTTP 200. **Custom Express routes own HTTP serialization:** returning an ErrorResult or
|
|
134
|
+
calling `res.json(result)` does not automatically set HTTP 403; map its code explicitly.
|
|
135
|
+
|
|
113
136
|
Advanced mutation validators drive dispatch: nested `data` validators run first, then the whole-body (`default`)
|
|
114
137
|
validator sees that transformed envelope, and the service persists only the final parsed `data`, `select`,
|
|
115
138
|
`populate`, `tasks`, and allowed `options`. Missing body options fall back to `returning_all`/`include_permissions`
|
|
@@ -132,9 +155,62 @@ Root entrypoint (`@web-ts-toolkit/access-router`):
|
|
|
132
155
|
|
|
133
156
|
Subpath entrypoints:
|
|
134
157
|
|
|
135
|
-
- `@web-ts-toolkit/access-router/advanced` —
|
|
158
|
+
- `@web-ts-toolkit/access-router/advanced` — request/service/option interfaces and include wire types, symbols (`MIDDLEWARE`, `DATA_MIDDLEWARE`, `PERMISSIONS`, ...), enums (`Codes`, `StatusCodes`), and validation helpers (`parseBody`, `parseQuery`, `parseBodyWithSchema`, request schemas). Runtime-context functions are internal; use root exports such as `createAccessRuntime`, `AccessRuntime`, and `defaultRuntime` for runtime ownership. `/advanced` does not export runtime instances or router-creation helpers.
|
|
136
159
|
- `@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.
|
|
137
160
|
|
|
161
|
+
## AJV validation contract
|
|
162
|
+
|
|
163
|
+
Install `ajv` in your application when using `fromAjv`; it is not a mandatory
|
|
164
|
+
access-router runtime dependency. Import the helper and `AjvValidatorLike` type
|
|
165
|
+
from `@web-ts-toolkit/access-router`.
|
|
166
|
+
|
|
167
|
+
- Synchronous validators return boolean **verdicts**: true returns the input
|
|
168
|
+
(including any in-place AJV mutations), false returns normalized issues. Mutable
|
|
169
|
+
`validate.errors` are snapshotted before any suspension or next validation call.
|
|
170
|
+
- Validators tagged `$async: true` return promises/thenables of **validated data**.
|
|
171
|
+
Every fulfillment succeeds, including `false`, `true`, `null`, scalars and objects;
|
|
172
|
+
the fulfilled value is returned, not substituted with the input. Shared
|
|
173
|
+
`validate.errors` is never read on this path. Real AJV `ValidationError` rejections
|
|
174
|
+
(`ajv: true`, `validation: true`, an `errors` array) provide input-local diagnostics
|
|
175
|
+
under concurrency. Other exceptions propagate unchanged as operational failures.
|
|
176
|
+
- Untagged promises/thenables now reject with a configuration `TypeError`; their
|
|
177
|
+
rejections are observed to avoid unhandled rejection. Sync non-boolean results
|
|
178
|
+
also reject with `TypeError`.
|
|
179
|
+
|
|
180
|
+
<!-- doc-example: complete-runtime -->
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { Ajv, type AsyncSchema } from 'ajv';
|
|
184
|
+
import { fromAjv } from '@web-ts-toolkit/access-router';
|
|
185
|
+
|
|
186
|
+
const ajv = new Ajv();
|
|
187
|
+
const sync = fromAjv<boolean>(ajv.compile<boolean>({ type: 'boolean' }));
|
|
188
|
+
const asyncSchema: AsyncSchema = { $async: true, type: 'boolean' };
|
|
189
|
+
const asyncData = fromAjv(ajv.compile<boolean>(asyncSchema));
|
|
190
|
+
const syncResult = await sync(false);
|
|
191
|
+
const asyncResult = await asyncData(false);
|
|
192
|
+
if (!syncResult.success || syncResult.data !== false || !asyncResult.success || asyncResult.data !== false) {
|
|
193
|
+
throw new Error('Both AJV validators must preserve valid false data');
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Migration:** pass genuine compiled AJV sync/async validators directly, without
|
|
198
|
+
casts or wrappers that discard `$async`. `AjvValidatorLike<T>` now separates sync
|
|
199
|
+
boolean and tagged async data signatures; use a literal tag (`$async: true as const`)
|
|
200
|
+
for structural async adapters. An async boolean-verdict wrapper must instead return
|
|
201
|
+
the original validated data or reject with an AJV validation error; merely adding
|
|
202
|
+
the tag changes false/true into successful data. Alternatively implement a
|
|
203
|
+
`RequestSchemaValidator` returning `{ success, data }` / `{ success: false, issues }`.
|
|
204
|
+
Rejections with only an `errors` property are operational, not AJV validation errors.
|
|
205
|
+
AJV's overloaded `AsyncValidateFunction<T>` extends its sync `ValidateFunction<T>`;
|
|
206
|
+
TypeScript can erase the tag in that base type, so runtime checks remain necessary.
|
|
207
|
+
Genuine AJV sync/async output types are inferred via their type-guard overload;
|
|
208
|
+
structural async output is inferred from the promise. Use `fromAjv<T>(syncValidator)`
|
|
209
|
+
for a structural boolean validator without a type guard. An inline schema can select AJV's earlier
|
|
210
|
+
sync overload even with `$async: true`; an `AsyncSchema`-typed schema, as above,
|
|
211
|
+
selects its async overload without casts (also for `compileAsync`). Runtime
|
|
212
|
+
behavior follows the validator's actual tag in either case.
|
|
213
|
+
|
|
138
214
|
## Default runtime vs. isolated runtime
|
|
139
215
|
|
|
140
216
|
The default export `acl` is bound to a single shared `AccessRuntime`, which is fine for most services.
|
|
@@ -202,6 +278,42 @@ Advanced read/list requests can attach related model data with `include` entries
|
|
|
202
278
|
`op: 'count'` returns exact authorized counts and ignores include pagination fields such as `args.limit`.
|
|
203
279
|
`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.
|
|
204
280
|
|
|
281
|
+
Legacy count joins use one grouped aggregate for all parents per include execution.
|
|
282
|
+
They count distinct target documents in the authorized count scope: duplicate local
|
|
283
|
+
keys, repeated foreign array entries, or one document matching multiple local keys
|
|
284
|
+
do not inflate a parent's count. No matches produce `0`.
|
|
285
|
+
|
|
286
|
+
The target Mongoose schema casts both the complete authorized filter (including
|
|
287
|
+
tenant filters) and foreign-key operands, including after array unwind. ObjectId/String
|
|
288
|
+
joins and query setters retain association to the original parent keys. Invalid
|
|
289
|
+
schema operands/filters produce controlled `BadRequest` (direct HTTP 400; root entry
|
|
290
|
+
`statusCode: 400`, `code: 'bad_request'`) rather than misleading zero counts. Count
|
|
291
|
+
operation denials still prevent target work; a terminal `false` count row policy
|
|
292
|
+
retains legacy omitted include output and a grouped-service Forbidden result.
|
|
293
|
+
|
|
294
|
+
This is query-schema/setter parity, not execution of `countDocuments` middleware or
|
|
295
|
+
plugins: casting is synchronous and performs no query. Aggregate middleware still
|
|
296
|
+
belongs to the aggregate operation. Express router count authorization through
|
|
297
|
+
`operationAccess` and row-filter hooks. Arbitrary pipelines passed to
|
|
298
|
+
`router.model.aggregate(...)` are not automatically cast.
|
|
299
|
+
|
|
300
|
+
### Safe include output paths
|
|
301
|
+
|
|
302
|
+
Treat `path` as output only: put an invoice's related count at `lineItemCount`, for
|
|
303
|
+
example, rather than inside permission metadata. Legacy and correlated read/list/count
|
|
304
|
+
paths must not equal, descend from, or be an ancestor of the receiving model's
|
|
305
|
+
`documentPermissionField` (default `_permissions`). With `auth.policy.permissions`,
|
|
306
|
+
`auth`, `auth.policy`, and `auth.policy.permissions.canReadSecret` all reject;
|
|
307
|
+
`auth.policy.permissionsExtra` is an ordinary sibling. Equivalent legacy bracket
|
|
308
|
+
paths follow the same rule.
|
|
309
|
+
|
|
310
|
+
The include tree is preflighted before target persistence, including every supported
|
|
311
|
+
nested level using that receiving target's configuration. Overlaps return BadRequest
|
|
312
|
+
(direct HTTP 400; affected root entry 400/`bad_request` within its HTTP 200 batch
|
|
313
|
+
envelope), even if the relationship would be empty. Malformed reads do not retry
|
|
314
|
+
through list access. Ordinary output-field replacement remains supported; generated
|
|
315
|
+
include data cannot grant source-field permission.
|
|
316
|
+
|
|
205
317
|
## Correlated Includes
|
|
206
318
|
|
|
207
319
|
Besides the legacy `localField`/`foreignField` joins, `include` entries accept
|
|
@@ -213,8 +325,17 @@ such as `$eq`/`$in`, `$in` array elements, `$and`/`$or`/`$nor` clauses,
|
|
|
213
325
|
`$elemMatch` subtrees) and as the whole `id` of identifier reads. Plain
|
|
214
326
|
strings are never references: `'$special'` keeps its literal query meaning.
|
|
215
327
|
Match a literal `{ "$parent": "x" }` object with
|
|
216
|
-
`{ "$escape": { "$parent": "x" } }
|
|
217
|
-
|
|
328
|
+
`{ "$escape": { "$parent": "x" } }`. In a bare field position,
|
|
329
|
+
`{ "note": { "$escape": { "$parent": "x" } } }` resolves to
|
|
330
|
+
`{ "note": { "$eq": { "$parent": "x" } } }`, just like bare substituted
|
|
331
|
+
parent objects. The explicit spelling
|
|
332
|
+
`{ "note": { "$eq": { "$escape": { "$parent": "x" } } } }` remains
|
|
333
|
+
equivalent. Operator operands and array elements receive the literal object
|
|
334
|
+
without another `$eq` wrapper. Escapes are resolved once and never read parent
|
|
335
|
+
data; missing, null or changed `parent.x` cannot change the literal. Malformed
|
|
336
|
+
escapes are rejected, and expanded operands still consume complexity budgets.
|
|
337
|
+
This bare-position correction requires an updated server; older servers with
|
|
338
|
+
the bare-escape defect still require the explicit `$eq` spelling.
|
|
218
339
|
|
|
219
340
|
Wire shapes (the originating method fixes `op`; there is no override):
|
|
220
341
|
|
|
@@ -250,15 +371,16 @@ Semantics:
|
|
|
250
371
|
become operators) and arrays are never flattened.
|
|
251
372
|
- Each correlated entry counts toward `maxIncludeCount`; expanded filters
|
|
252
373
|
are revalidated against `maxNodes`/`maxDepth`/`maxInValues`/
|
|
253
|
-
`maxLogicalClauses`. Total inner executions per request are bounded by
|
|
374
|
+
`maxLogicalClauses`. Total inner executions per request/runtime are bounded by
|
|
254
375
|
`maxCorrelatedQueries` (default `100`) and nesting by
|
|
255
376
|
`maxCorrelatedDepth` (default `5`); exceeding any bound fails the whole
|
|
256
|
-
request. Fan-out shares
|
|
257
|
-
|
|
258
|
-
target persistence queries; target
|
|
377
|
+
request/affected root entry. Fan-out shares request-and-runtime-owned
|
|
378
|
+
persistence admission (see below). Target authorization denials fail the
|
|
379
|
+
request/affected root entry with zero target persistence queries; target
|
|
380
|
+
runtime errors also fail that request/entry.
|
|
259
381
|
`path` must be a non-empty valid field path (not `_id`, not `$`-prefixed);
|
|
260
382
|
duplicates in one include array are `BadRequest`; collisions with parent
|
|
261
|
-
fields overwrite
|
|
383
|
+
fields overwrite except for the permission-metadata overlaps described above.
|
|
262
384
|
- Referenced parent fields are added to the parent DB select like legacy
|
|
263
385
|
`localField`s, then trimmed unless allowed by field policy and selected.
|
|
264
386
|
A policy-forbidden reference still resolves — only its query effects are
|
|
@@ -267,7 +389,7 @@ Semantics:
|
|
|
267
389
|
attach to read/list parents only. Correlated entries are accepted in both
|
|
268
390
|
direct (`model-router.ts`) and root (`root-router.ts`) validators with
|
|
269
391
|
identical verdicts; malformed correlated input is a controlled
|
|
270
|
-
`BadRequest`, never a silent drop. Legacy entries
|
|
392
|
+
`BadRequest`, never a silent drop. Legacy entries also enforce safe output paths, and
|
|
271
393
|
legacy-shaped entries carrying `$parent` markers are rejected.
|
|
272
394
|
|
|
273
395
|
The TypeScript wire types (`ParentRef`, `CorrelatedInclude`, `Include`) are
|
|
@@ -275,6 +397,130 @@ reachable without deep imports via `@web-ts-toolkit/access-router/advanced`
|
|
|
275
397
|
(which re-exports `./interfaces`). See the website `services`,
|
|
276
398
|
`configuration`, `validation`, and `openapi` pages for the full contract.
|
|
277
399
|
|
|
400
|
+
## Nested updates and trusted hooks
|
|
401
|
+
|
|
402
|
+
Partial updates follow **authorized policy paths**, not a generic deep merge. For a
|
|
403
|
+
customer profile editor, grant only the editable leaf and send nested JSON:
|
|
404
|
+
|
|
405
|
+
<!-- doc-example: partial -->
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
import type { ModelRouterOptions } from '@web-ts-toolkit/access-router';
|
|
409
|
+
|
|
410
|
+
// Non-generic options accept dotted policy paths.
|
|
411
|
+
const options: ModelRouterOptions = {
|
|
412
|
+
operationAccess: { update: true },
|
|
413
|
+
permissionSchema: { 'profile.public': { update: true } },
|
|
414
|
+
};
|
|
415
|
+
const updateBody = { profile: { public: 'New display name' } };
|
|
416
|
+
// Pass options to acl.createRouter(CustomerModel, options); send updateBody to PATCH.
|
|
417
|
+
// Existing profile.secret and omitted siblings survive.
|
|
418
|
+
void [options, updateBody];
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
This applies to `updateOne`, `updateById`, existing-row upsert, and single/bulk
|
|
422
|
+
subdocument updates, including Mongoose nested and single-nested schemas. Authorized
|
|
423
|
+
leaves can initialize missing/null containers. Authorizing `profile` itself instead
|
|
424
|
+
allows whole replacement and removes omitted children; whole grants take priority
|
|
425
|
+
over overlapping leaf grants. An authorized object leaf replaces just that leaf.
|
|
426
|
+
Arrays replace as a whole when authorized as a whole; an explicit `items.0.public`
|
|
427
|
+
grant patches that indexed leaf. Omitted fields remain unchanged, admitted `null`
|
|
428
|
+
clears the selected field, and authorized empty objects/arrays replace it (Mongoose
|
|
429
|
+
`minimize` may omit empty objects in storage). A null/empty/malformed client container
|
|
430
|
+
does not supply descendant leaves and cannot clear a protected parent. Casting and
|
|
431
|
+
validation still apply. Literal dotted/bracket client keys are ignored by field
|
|
432
|
+
selection; they are not `$set`/`$unset` instructions.
|
|
433
|
+
|
|
434
|
+
Model `prepare` and `transform` hooks are trusted application code. Validation sees
|
|
435
|
+
selected client data before prepare. Prepare output is not re-filtered: it can add
|
|
436
|
+
protected/server-only fields, including dotted Mongoose paths. At strict ancestors of
|
|
437
|
+
admitted paths, supplied children of plain-object/array prepare output are applied
|
|
438
|
+
while omitted siblings survive. Other prepare paths remain whole assignments.
|
|
439
|
+
An empty partial container or omitted parent does not delete siblings; explicit
|
|
440
|
+
`{ profile: null }` / `{ profile: undefined }` can clear/unset it. An entirely
|
|
441
|
+
null/undefined prepare result supplies no assignments.
|
|
442
|
+
|
|
443
|
+
For deliberate parent replacement, trusted `transform.update` can call
|
|
444
|
+
`doc.set('profile', replacement)`. Do not blindly reintroduce unfiltered
|
|
445
|
+
`context.originalData`. The update order remains validation → prepare →
|
|
446
|
+
assigned-document transform → save → afterPersist → changes; snapshots/diffs reflect
|
|
447
|
+
persisted changes. Subdocument operations retain their own lifecycle and do not gain
|
|
448
|
+
model prepare/transform hooks.
|
|
449
|
+
|
|
450
|
+
## Subdocument writes with hidden responses
|
|
451
|
+
|
|
452
|
+
Appending an attachment or updating a line item can succeed without permission to
|
|
453
|
+
read it. After saving, mutation responses require the parent's read operation/row
|
|
454
|
+
access, checked against post-save state. Full-array **create** responses (including
|
|
455
|
+
POST `[]`) additionally require both `subs.<sub>.list` and `.read` operation guards
|
|
456
|
+
and row filters. **Single/bulk update** responses require subdocument read access
|
|
457
|
+
only and return readable mutation targets; they do not require list access.
|
|
458
|
+
|
|
459
|
+
All three use read-field projection plus `_id`. Counts reflect visible returned
|
|
460
|
+
rows, not stored/updated totals. Filtering preserves stored array order, including
|
|
461
|
+
service `addFirst` insertion order; bulk request order does not reorder the response.
|
|
462
|
+
`addFirst` remains a service option, not a new generated HTTP option.
|
|
463
|
+
|
|
464
|
+
When a response guard denies access, a row policy returns `false`, or no readable
|
|
465
|
+
parent/rows match, the committed write still succeeds:
|
|
466
|
+
|
|
467
|
+
| Operation | Service/root result fields | Direct HTTP body/status; root entry status |
|
|
468
|
+
| ------------- | ------------------------------------------------------------------ | ------------------------------------------ |
|
|
469
|
+
| Create | `success: true, kind: 'list', code: 'created', data: [], count: 0` | `[]`, 201; 201 |
|
|
470
|
+
| Bulk update | `success: true, kind: 'list', code: 'success', data: [], count: 0` | `[]`, 200; 200 |
|
|
471
|
+
| Single update | `success: true, kind: 'single', code: 'success', data: null` | JSON `null`, 200; 200 |
|
|
472
|
+
|
|
473
|
+
Root batches retain their HTTP 200 envelope. **Do not retry a successful write just
|
|
474
|
+
because its output is empty.** Actual write denials and validation/persistence failures
|
|
475
|
+
retain their errors. Response visibility needs at most one extra parent query and
|
|
476
|
+
resolves applicable row policies once per mutation, rather than once per returned row.
|
|
477
|
+
|
|
478
|
+
## Persistence concurrency and resource limits
|
|
479
|
+
|
|
480
|
+
`requestComplexity.maxBulkConcurrency` (default `10`) bounds simultaneous awaited
|
|
481
|
+
**adapter/document persistence operations** for access-router model services sharing
|
|
482
|
+
one request and runtime. Direct service calls, root entries, subqueries, legacy
|
|
483
|
+
includes and nested correlated includes share admission. Different requests and
|
|
484
|
+
different runtimes have independent pools and correlated-query totals. Services
|
|
485
|
+
capture their owning runtime at construction; later ambient runtime changes do not
|
|
486
|
+
move their persistence to another pool.
|
|
487
|
+
|
|
488
|
+
Admission covers lazy adapter query settlement, document save/delete, and
|
|
489
|
+
service-triggered post-write populate. Bulk create submits one admitted singleton-array
|
|
490
|
+
adapter call per item, preserves result order, and still submits every item if one
|
|
491
|
+
rejects. Internal custom adapters must preserve array-in/array-out create semantics.
|
|
492
|
+
Their optional synchronous `castFilter` must cast a copy and preserve `$in` operand
|
|
493
|
+
order/cardinality; without it they retain identity casting.
|
|
494
|
+
|
|
495
|
+
Recursive orchestration holds no persistence permit while waiting for descendants,
|
|
496
|
+
so limit `1` supports nested includes. Internal map/run loops remain bounded per
|
|
497
|
+
invocation. `RootRouter.maxConcurrentOperations` separately bounds whole root
|
|
498
|
+
operations within each order group, with results in input-index order. Permits
|
|
499
|
+
release on success or error and pass FIFO to waiting work; release neither cancels
|
|
500
|
+
submitted siblings nor refunds the cumulative `maxCorrelatedQueries` budget (default
|
|
501
|
+
`100`). `maxCorrelatedDepth` (default `5`) separately bounds template nesting.
|
|
502
|
+
|
|
503
|
+
The ceiling is **not a MongoDB driver-command count**: one admitted operation can
|
|
504
|
+
run Mongoose middleware/populate that issues multiple commands. Router ACL, prepare,
|
|
505
|
+
afterPersist, decorate and recursive include orchestration are outside the permit;
|
|
506
|
+
Mongoose's own middleware runs inside the awaited operation. Direct database/network
|
|
507
|
+
calls from trusted hooks are not governed. This is neither a process-wide connection
|
|
508
|
+
pool limit nor a throughput guarantee. Controlled nested-query instrumentation reduced
|
|
509
|
+
peak active collection calls from 9 to 3 at limit 3 for the tested paths; it is not a
|
|
510
|
+
production benchmark or a guarantee about middleware/populate fan-out.
|
|
511
|
+
|
|
512
|
+
### Compatibility notes
|
|
513
|
+
|
|
514
|
+
These boundary fixes preserve public entrypoints and require no new dependency or
|
|
515
|
+
option. Move computed include output away from permission metadata. Nested leaf edits
|
|
516
|
+
now preserve protected siblings; prepare hooks relying on omission to delete a parent
|
|
517
|
+
must replace it explicitly in transform. Handle successful null/empty subdocument
|
|
518
|
+
responses as completed writes. Legacy counts now cast schema values and report invalid
|
|
519
|
+
operands as BadRequest; they remain grouped and independent of list pagination.
|
|
520
|
+
Persistence admission is shared across nested work within each request/runtime, so
|
|
521
|
+
timing may change. It does not provide transactions, idempotency or optimistic
|
|
522
|
+
concurrency protection for competing edits.
|
|
523
|
+
|
|
278
524
|
## Import styles
|
|
279
525
|
|
|
280
526
|
The package ships both a default export and named exports:
|
package/advanced.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AccessRouterBaseRequest, a as AccessRouterFieldKey, b as AccessRouterLogger, c as AccessRouterRequest, d as AccessRouterRequestExtensions, e as AdvancedCreateBody, f as AdvancedListBody, g as AdvancedReadBody, h as AdvancedReadFilterBody, i as AdvancedUpdateBody, j as AdvancedUpsertBody, k as AfterPersistAccess, l as AjvErrorObjectLike, m as AjvValidationErrorLike, n as AjvValidatorLike, o as ArkTypeErrorsLike, p as ArkTypeLike, q as ArkTypeProblemLike, B as BaseFilterAccess, C as Change, r as Codes, s as CorrelatedCountInclude, t as CorrelatedFilter, u as CorrelatedInclude, v as CorrelatedIncludeArgs, w as CorrelatedListInclude, x as CorrelatedReadByFilterInclude, y as CorrelatedReadByIdInclude, z as CountBody, D as CreateArgs, E as CreateOptions, F as CreateQueryInput, G as CustomHeaders, H as DATA_MIDDLEWARE, I as DataBaseFilterHook, J as DataFilter, K as DataFindArgs, L as DataFindOneArgs, M as DataFindOneOptions, N as DataFindOptions, O as DataHook, P as DataHookContext, Q as DataIdentifierHook, R as DataListHook, S as DataOverrideFilterHook, T as DataRequest, U as DataRequestSchemas, V as DataRouterOptions, W as DecorateAccess, X as DecorateAllAccess, Y as DeepFieldPath, Z as DefaultModelRouterOptions, _ as Defaults, $ as DistinctArgs, a0 as DistinctBody, a1 as DocPermissionsAccess, a2 as ErrorResult, a3 as ExistsOptions, a4 as ExtendedDataRouterOptions, a5 as ExtendedDefaultModelRouterOptions, a6 as ExtendedModelRouterOptions, a7 as Filter, a8 as FilterOperator, a9 as FindAccess, aa as FindArgs, ab as FindByIdArgs, ac as FindByIdOptions, ad as FindOneArgs, ae as FindOneOptions, af as FindOptions, ag as GlobalOptions, ah as GlobalPermissionValue, ai as GuardHook, aj as IdentifierHook, ak as Include, al as IoTsContextEntryLike, am as IoTsDecodeErrorLike, an as IoTsDecoderLike, ao as JoiSchemaLike, ap as JoiValidationErrorDetailLike, aq as KeyValueProjection, ar as LegacyInclude, as as ListQueryInput, at as ListResult, au as MIDDLEWARE, av as MaybePromise, aw as ModelBaseFilterHook, ax as ModelChangeHook, ay as ModelDeleteHook, az as ModelDocPermissionsHook, aA as ModelDocument, aB as ModelDocumentHook, aC as ModelHook, aD as ModelHookContext, aE as ModelIdentifierHook, aF as ModelListHook, aG as ModelOverrideFilterHook, aH as ModelRequest, aI as ModelRouterOptions, aJ as ModelValidateHook, aK as PERMISSIONS, aL as PERMISSION_KEYS, aM as ParentRef, aN as PathValue, aO as PermissionSchema, aP as Populate, aQ as PopulateAccess, aR as PrepareAccess, aS as Projection, aT as PublicCreateArgs, aU as PublicCreateOptions, aV as PublicErrorResult, aW as PublicListArgs, aX as PublicListOptions, aY as PublicListResult, aZ as PublicOutput, a_ as PublicReadArgs, a$ as PublicReadOptions, b0 as PublicServiceResult, b1 as PublicSingleResult, b2 as PublicUpdateArgs, b3 as PublicUpdateOptions, b4 as PublicUpsertArgs, b5 as PublicUpsertOptions, b6 as ReadQueryInput, b7 as Request, b8 as RequestSchemaAdapter, b9 as RequestSchemaFailure, ba as RequestSchemaIssue, bb as RequestSchemaLike, bc as RequestSchemaOptions, bd as RequestSchemaResult, be as RequestSchemaSuccess, bf as RequestSchemaValidator, bg as RequestSchemas, bh as RootDataListQueryEntry, bi as RootDataOperation, bj as RootDataReadByFilterQueryEntry, bk as RootDataReadByIdQueryEntry, bl as RootModelCountQueryEntry, bm as RootModelCreateQueryEntry, bn as RootModelDeleteQueryEntry, bo as RootModelDistinctQueryEntry, bp as RootModelListQueryEntry, bq as RootModelNewQueryEntry, br as RootModelOperation, bs as RootModelReadByFilterQueryEntry, bt as RootModelReadByIdQueryEntry, bu as RootModelSubBulkUpdateQueryEntry, bv as RootModelSubCreateQueryEntry, bw as RootModelSubDeleteQueryEntry, bx as RootModelSubListQueryEntry, by as RootModelSubReadQueryEntry, bz as RootModelSubUpdateQueryEntry, bA as RootModelUpdateQueryEntry, bB as RootModelUpsertQueryEntry, bC as RootOperationResult, bD as RootQueryEntry, bE as RootRouterOptions, bF as RootSubOperation, bG as RootTarget, bH as RouteGuardAccess, bI as SelectAccess, bJ as SelectedPopulatedPublicOutput, bK as SelectedPublicOutput, bL as ServiceResult, bM as SingleResult, bN as Sort, bO as SortOrder, bP as StandardSchemaFailure, bQ as StandardSchemaInferOutput, bR as StandardSchemaIssue, bS as StandardSchemaPathSegment, bT as StandardSchemaResult, bU as StandardSchemaSuccess, bV as StandardSchemaV1, bW as StatusCodes, bX as SubListBody, bY as SubPopulate, bZ as SubQueryEntry, b_ as SubReadBody, b$ as SubdocumentBulkRecord, c0 as SubdocumentBulkUpdateInput, c1 as SubdocumentCreateInput, c2 as SubdocumentCreateOptions, c3 as SubdocumentId, c4 as SubdocumentListOptions, c5 as SubdocumentName, c6 as SubdocumentParentArgs, c7 as SubdocumentParentOptions, c8 as SubdocumentReadOptions, c9 as SubdocumentRecord, ca as SuperstructFailureLike, cb as SuperstructValidateLike, cc as Task, cd as TaskType, ce as TransformAccess, cf as TypedFilter, cg as UpdateByIdArgs, ch as UpdateByIdOptions, ci as UpdateOneArgs, cj as UpdateOneOptions, ck as UpdateQueryInput, cl as UpsertArgs, cm as UpsertOptions, cn as UpsertQueryInput, co as ValibotIssueLike, cp as ValibotPathItemLike, cq as ValibotSafeParseLike, cr as ValibotSafeParseResult, cs as ValidateAccess, ct as ValidateRule, cu as Validation, cv as ValidationError, cw as VineValidationErrorLike, cx as VineValidationMessageLike, cy as VineValidatorLike, cz as YupSchemaLike, cA as YupValidationErrorLike, cB as defineRequestSchema, cC as fromAjv, cD as fromArkType, cE as fromIoTs, cF as fromJoi, cG as fromStandardSchema, cH as fromSuperstruct, cI as fromValibot, cJ as fromVine, cK as fromYup, cL as fromZod, cM as parseBody, cN as parseBodyWithSchema, cO as parseNestedBodyWithSchema, cP as parsePathParam, cQ as parseQuery } from './parsers-
|
|
1
|
+
export { A as AccessRouterBaseRequest, a as AccessRouterFieldKey, b as AccessRouterLogger, c as AccessRouterRequest, d as AccessRouterRequestExtensions, e as AdvancedCreateBody, f as AdvancedListBody, g as AdvancedReadBody, h as AdvancedReadFilterBody, i as AdvancedUpdateBody, j as AdvancedUpsertBody, k as AfterPersistAccess, l as AjvErrorObjectLike, m as AjvValidationErrorLike, n as AjvValidatorLike, o as ArkTypeErrorsLike, p as ArkTypeLike, q as ArkTypeProblemLike, B as BaseFilterAccess, C as Change, r as Codes, s as CorrelatedCountInclude, t as CorrelatedFilter, u as CorrelatedInclude, v as CorrelatedIncludeArgs, w as CorrelatedListInclude, x as CorrelatedReadByFilterInclude, y as CorrelatedReadByIdInclude, z as CountBody, D as CreateArgs, E as CreateOptions, F as CreateQueryInput, G as CustomHeaders, H as DATA_MIDDLEWARE, I as DataBaseFilterHook, J as DataFilter, K as DataFindArgs, L as DataFindOneArgs, M as DataFindOneOptions, N as DataFindOptions, O as DataHook, P as DataHookContext, Q as DataIdentifierHook, R as DataListHook, S as DataOverrideFilterHook, T as DataRequest, U as DataRequestSchemas, V as DataRouterOptions, W as DecorateAccess, X as DecorateAllAccess, Y as DeepFieldPath, Z as DefaultModelRouterOptions, _ as Defaults, $ as DistinctArgs, a0 as DistinctBody, a1 as DocPermissionsAccess, a2 as ErrorResult, a3 as ExistsOptions, a4 as ExtendedDataRouterOptions, a5 as ExtendedDefaultModelRouterOptions, a6 as ExtendedModelRouterOptions, a7 as Filter, a8 as FilterOperator, a9 as FindAccess, aa as FindArgs, ab as FindByIdArgs, ac as FindByIdOptions, ad as FindOneArgs, ae as FindOneOptions, af as FindOptions, ag as GlobalOptions, ah as GlobalPermissionValue, ai as GuardHook, aj as IdentifierHook, ak as Include, al as IoTsContextEntryLike, am as IoTsDecodeErrorLike, an as IoTsDecoderLike, ao as JoiSchemaLike, ap as JoiValidationErrorDetailLike, aq as KeyValueProjection, ar as LegacyInclude, as as ListQueryInput, at as ListResult, au as MIDDLEWARE, av as MaybePromise, aw as ModelBaseFilterHook, ax as ModelChangeHook, ay as ModelDeleteHook, az as ModelDocPermissionsHook, aA as ModelDocument, aB as ModelDocumentHook, aC as ModelHook, aD as ModelHookContext, aE as ModelIdentifierHook, aF as ModelListHook, aG as ModelOverrideFilterHook, aH as ModelRequest, aI as ModelRouterOptions, aJ as ModelValidateHook, aK as PERMISSIONS, aL as PERMISSION_KEYS, aM as ParentRef, aN as PathValue, aO as PermissionSchema, aP as Populate, aQ as PopulateAccess, aR as PrepareAccess, aS as Projection, aT as PublicCreateArgs, aU as PublicCreateOptions, aV as PublicErrorResult, aW as PublicListArgs, aX as PublicListOptions, aY as PublicListResult, aZ as PublicOutput, a_ as PublicReadArgs, a$ as PublicReadOptions, b0 as PublicServiceResult, b1 as PublicSingleResult, b2 as PublicUpdateArgs, b3 as PublicUpdateOptions, b4 as PublicUpsertArgs, b5 as PublicUpsertOptions, b6 as ReadQueryInput, b7 as Request, b8 as RequestSchemaAdapter, b9 as RequestSchemaFailure, ba as RequestSchemaIssue, bb as RequestSchemaLike, bc as RequestSchemaOptions, bd as RequestSchemaResult, be as RequestSchemaSuccess, bf as RequestSchemaValidator, bg as RequestSchemas, bh as RootDataListQueryEntry, bi as RootDataOperation, bj as RootDataReadByFilterQueryEntry, bk as RootDataReadByIdQueryEntry, bl as RootModelCountQueryEntry, bm as RootModelCreateQueryEntry, bn as RootModelDeleteQueryEntry, bo as RootModelDistinctQueryEntry, bp as RootModelListQueryEntry, bq as RootModelNewQueryEntry, br as RootModelOperation, bs as RootModelReadByFilterQueryEntry, bt as RootModelReadByIdQueryEntry, bu as RootModelSubBulkUpdateQueryEntry, bv as RootModelSubCreateQueryEntry, bw as RootModelSubDeleteQueryEntry, bx as RootModelSubListQueryEntry, by as RootModelSubReadQueryEntry, bz as RootModelSubUpdateQueryEntry, bA as RootModelUpdateQueryEntry, bB as RootModelUpsertQueryEntry, bC as RootOperationResult, bD as RootQueryEntry, bE as RootRouterOptions, bF as RootSubOperation, bG as RootTarget, bH as RouteGuardAccess, bI as SelectAccess, bJ as SelectedPopulatedPublicOutput, bK as SelectedPublicOutput, bL as ServiceResult, bM as SingleResult, bN as Sort, bO as SortOrder, bP as StandardSchemaFailure, bQ as StandardSchemaInferOutput, bR as StandardSchemaIssue, bS as StandardSchemaPathSegment, bT as StandardSchemaResult, bU as StandardSchemaSuccess, bV as StandardSchemaV1, bW as StatusCodes, bX as SubListBody, bY as SubPopulate, bZ as SubQueryEntry, b_ as SubReadBody, b$ as SubdocumentBulkRecord, c0 as SubdocumentBulkUpdateInput, c1 as SubdocumentCreateInput, c2 as SubdocumentCreateOptions, c3 as SubdocumentId, c4 as SubdocumentListOptions, c5 as SubdocumentName, c6 as SubdocumentParentArgs, c7 as SubdocumentParentOptions, c8 as SubdocumentReadOptions, c9 as SubdocumentRecord, ca as SuperstructFailureLike, cb as SuperstructValidateLike, cc as Task, cd as TaskType, ce as TransformAccess, cf as TypedFilter, cg as UpdateByIdArgs, ch as UpdateByIdOptions, ci as UpdateOneArgs, cj as UpdateOneOptions, ck as UpdateQueryInput, cl as UpsertArgs, cm as UpsertOptions, cn as UpsertQueryInput, co as ValibotIssueLike, cp as ValibotPathItemLike, cq as ValibotSafeParseLike, cr as ValibotSafeParseResult, cs as ValidateAccess, ct as ValidateRule, cu as Validation, cv as ValidationError, cw as VineValidationErrorLike, cx as VineValidationMessageLike, cy as VineValidatorLike, cz as YupSchemaLike, cA as YupValidationErrorLike, cB as defineRequestSchema, cC as fromAjv, cD as fromArkType, cE as fromIoTs, cF as fromJoi, cG as fromStandardSchema, cH as fromSuperstruct, cI as fromValibot, cJ as fromVine, cK as fromYup, cL as fromZod, cM as parseBody, cN as parseBodyWithSchema, cO as parseNestedBodyWithSchema, cP as parsePathParam, cQ as parseQuery } from './parsers-BidvJUu7.mjs';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import 'mongoose';
|
|
4
4
|
import 'express';
|
package/advanced.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AccessRouterBaseRequest, a as AccessRouterFieldKey, b as AccessRouterLogger, c as AccessRouterRequest, d as AccessRouterRequestExtensions, e as AdvancedCreateBody, f as AdvancedListBody, g as AdvancedReadBody, h as AdvancedReadFilterBody, i as AdvancedUpdateBody, j as AdvancedUpsertBody, k as AfterPersistAccess, l as AjvErrorObjectLike, m as AjvValidationErrorLike, n as AjvValidatorLike, o as ArkTypeErrorsLike, p as ArkTypeLike, q as ArkTypeProblemLike, B as BaseFilterAccess, C as Change, r as Codes, s as CorrelatedCountInclude, t as CorrelatedFilter, u as CorrelatedInclude, v as CorrelatedIncludeArgs, w as CorrelatedListInclude, x as CorrelatedReadByFilterInclude, y as CorrelatedReadByIdInclude, z as CountBody, D as CreateArgs, E as CreateOptions, F as CreateQueryInput, G as CustomHeaders, H as DATA_MIDDLEWARE, I as DataBaseFilterHook, J as DataFilter, K as DataFindArgs, L as DataFindOneArgs, M as DataFindOneOptions, N as DataFindOptions, O as DataHook, P as DataHookContext, Q as DataIdentifierHook, R as DataListHook, S as DataOverrideFilterHook, T as DataRequest, U as DataRequestSchemas, V as DataRouterOptions, W as DecorateAccess, X as DecorateAllAccess, Y as DeepFieldPath, Z as DefaultModelRouterOptions, _ as Defaults, $ as DistinctArgs, a0 as DistinctBody, a1 as DocPermissionsAccess, a2 as ErrorResult, a3 as ExistsOptions, a4 as ExtendedDataRouterOptions, a5 as ExtendedDefaultModelRouterOptions, a6 as ExtendedModelRouterOptions, a7 as Filter, a8 as FilterOperator, a9 as FindAccess, aa as FindArgs, ab as FindByIdArgs, ac as FindByIdOptions, ad as FindOneArgs, ae as FindOneOptions, af as FindOptions, ag as GlobalOptions, ah as GlobalPermissionValue, ai as GuardHook, aj as IdentifierHook, ak as Include, al as IoTsContextEntryLike, am as IoTsDecodeErrorLike, an as IoTsDecoderLike, ao as JoiSchemaLike, ap as JoiValidationErrorDetailLike, aq as KeyValueProjection, ar as LegacyInclude, as as ListQueryInput, at as ListResult, au as MIDDLEWARE, av as MaybePromise, aw as ModelBaseFilterHook, ax as ModelChangeHook, ay as ModelDeleteHook, az as ModelDocPermissionsHook, aA as ModelDocument, aB as ModelDocumentHook, aC as ModelHook, aD as ModelHookContext, aE as ModelIdentifierHook, aF as ModelListHook, aG as ModelOverrideFilterHook, aH as ModelRequest, aI as ModelRouterOptions, aJ as ModelValidateHook, aK as PERMISSIONS, aL as PERMISSION_KEYS, aM as ParentRef, aN as PathValue, aO as PermissionSchema, aP as Populate, aQ as PopulateAccess, aR as PrepareAccess, aS as Projection, aT as PublicCreateArgs, aU as PublicCreateOptions, aV as PublicErrorResult, aW as PublicListArgs, aX as PublicListOptions, aY as PublicListResult, aZ as PublicOutput, a_ as PublicReadArgs, a$ as PublicReadOptions, b0 as PublicServiceResult, b1 as PublicSingleResult, b2 as PublicUpdateArgs, b3 as PublicUpdateOptions, b4 as PublicUpsertArgs, b5 as PublicUpsertOptions, b6 as ReadQueryInput, b7 as Request, b8 as RequestSchemaAdapter, b9 as RequestSchemaFailure, ba as RequestSchemaIssue, bb as RequestSchemaLike, bc as RequestSchemaOptions, bd as RequestSchemaResult, be as RequestSchemaSuccess, bf as RequestSchemaValidator, bg as RequestSchemas, bh as RootDataListQueryEntry, bi as RootDataOperation, bj as RootDataReadByFilterQueryEntry, bk as RootDataReadByIdQueryEntry, bl as RootModelCountQueryEntry, bm as RootModelCreateQueryEntry, bn as RootModelDeleteQueryEntry, bo as RootModelDistinctQueryEntry, bp as RootModelListQueryEntry, bq as RootModelNewQueryEntry, br as RootModelOperation, bs as RootModelReadByFilterQueryEntry, bt as RootModelReadByIdQueryEntry, bu as RootModelSubBulkUpdateQueryEntry, bv as RootModelSubCreateQueryEntry, bw as RootModelSubDeleteQueryEntry, bx as RootModelSubListQueryEntry, by as RootModelSubReadQueryEntry, bz as RootModelSubUpdateQueryEntry, bA as RootModelUpdateQueryEntry, bB as RootModelUpsertQueryEntry, bC as RootOperationResult, bD as RootQueryEntry, bE as RootRouterOptions, bF as RootSubOperation, bG as RootTarget, bH as RouteGuardAccess, bI as SelectAccess, bJ as SelectedPopulatedPublicOutput, bK as SelectedPublicOutput, bL as ServiceResult, bM as SingleResult, bN as Sort, bO as SortOrder, bP as StandardSchemaFailure, bQ as StandardSchemaInferOutput, bR as StandardSchemaIssue, bS as StandardSchemaPathSegment, bT as StandardSchemaResult, bU as StandardSchemaSuccess, bV as StandardSchemaV1, bW as StatusCodes, bX as SubListBody, bY as SubPopulate, bZ as SubQueryEntry, b_ as SubReadBody, b$ as SubdocumentBulkRecord, c0 as SubdocumentBulkUpdateInput, c1 as SubdocumentCreateInput, c2 as SubdocumentCreateOptions, c3 as SubdocumentId, c4 as SubdocumentListOptions, c5 as SubdocumentName, c6 as SubdocumentParentArgs, c7 as SubdocumentParentOptions, c8 as SubdocumentReadOptions, c9 as SubdocumentRecord, ca as SuperstructFailureLike, cb as SuperstructValidateLike, cc as Task, cd as TaskType, ce as TransformAccess, cf as TypedFilter, cg as UpdateByIdArgs, ch as UpdateByIdOptions, ci as UpdateOneArgs, cj as UpdateOneOptions, ck as UpdateQueryInput, cl as UpsertArgs, cm as UpsertOptions, cn as UpsertQueryInput, co as ValibotIssueLike, cp as ValibotPathItemLike, cq as ValibotSafeParseLike, cr as ValibotSafeParseResult, cs as ValidateAccess, ct as ValidateRule, cu as Validation, cv as ValidationError, cw as VineValidationErrorLike, cx as VineValidationMessageLike, cy as VineValidatorLike, cz as YupSchemaLike, cA as YupValidationErrorLike, cB as defineRequestSchema, cC as fromAjv, cD as fromArkType, cE as fromIoTs, cF as fromJoi, cG as fromStandardSchema, cH as fromSuperstruct, cI as fromValibot, cJ as fromVine, cK as fromYup, cL as fromZod, cM as parseBody, cN as parseBodyWithSchema, cO as parseNestedBodyWithSchema, cP as parsePathParam, cQ as parseQuery } from './parsers-
|
|
1
|
+
export { A as AccessRouterBaseRequest, a as AccessRouterFieldKey, b as AccessRouterLogger, c as AccessRouterRequest, d as AccessRouterRequestExtensions, e as AdvancedCreateBody, f as AdvancedListBody, g as AdvancedReadBody, h as AdvancedReadFilterBody, i as AdvancedUpdateBody, j as AdvancedUpsertBody, k as AfterPersistAccess, l as AjvErrorObjectLike, m as AjvValidationErrorLike, n as AjvValidatorLike, o as ArkTypeErrorsLike, p as ArkTypeLike, q as ArkTypeProblemLike, B as BaseFilterAccess, C as Change, r as Codes, s as CorrelatedCountInclude, t as CorrelatedFilter, u as CorrelatedInclude, v as CorrelatedIncludeArgs, w as CorrelatedListInclude, x as CorrelatedReadByFilterInclude, y as CorrelatedReadByIdInclude, z as CountBody, D as CreateArgs, E as CreateOptions, F as CreateQueryInput, G as CustomHeaders, H as DATA_MIDDLEWARE, I as DataBaseFilterHook, J as DataFilter, K as DataFindArgs, L as DataFindOneArgs, M as DataFindOneOptions, N as DataFindOptions, O as DataHook, P as DataHookContext, Q as DataIdentifierHook, R as DataListHook, S as DataOverrideFilterHook, T as DataRequest, U as DataRequestSchemas, V as DataRouterOptions, W as DecorateAccess, X as DecorateAllAccess, Y as DeepFieldPath, Z as DefaultModelRouterOptions, _ as Defaults, $ as DistinctArgs, a0 as DistinctBody, a1 as DocPermissionsAccess, a2 as ErrorResult, a3 as ExistsOptions, a4 as ExtendedDataRouterOptions, a5 as ExtendedDefaultModelRouterOptions, a6 as ExtendedModelRouterOptions, a7 as Filter, a8 as FilterOperator, a9 as FindAccess, aa as FindArgs, ab as FindByIdArgs, ac as FindByIdOptions, ad as FindOneArgs, ae as FindOneOptions, af as FindOptions, ag as GlobalOptions, ah as GlobalPermissionValue, ai as GuardHook, aj as IdentifierHook, ak as Include, al as IoTsContextEntryLike, am as IoTsDecodeErrorLike, an as IoTsDecoderLike, ao as JoiSchemaLike, ap as JoiValidationErrorDetailLike, aq as KeyValueProjection, ar as LegacyInclude, as as ListQueryInput, at as ListResult, au as MIDDLEWARE, av as MaybePromise, aw as ModelBaseFilterHook, ax as ModelChangeHook, ay as ModelDeleteHook, az as ModelDocPermissionsHook, aA as ModelDocument, aB as ModelDocumentHook, aC as ModelHook, aD as ModelHookContext, aE as ModelIdentifierHook, aF as ModelListHook, aG as ModelOverrideFilterHook, aH as ModelRequest, aI as ModelRouterOptions, aJ as ModelValidateHook, aK as PERMISSIONS, aL as PERMISSION_KEYS, aM as ParentRef, aN as PathValue, aO as PermissionSchema, aP as Populate, aQ as PopulateAccess, aR as PrepareAccess, aS as Projection, aT as PublicCreateArgs, aU as PublicCreateOptions, aV as PublicErrorResult, aW as PublicListArgs, aX as PublicListOptions, aY as PublicListResult, aZ as PublicOutput, a_ as PublicReadArgs, a$ as PublicReadOptions, b0 as PublicServiceResult, b1 as PublicSingleResult, b2 as PublicUpdateArgs, b3 as PublicUpdateOptions, b4 as PublicUpsertArgs, b5 as PublicUpsertOptions, b6 as ReadQueryInput, b7 as Request, b8 as RequestSchemaAdapter, b9 as RequestSchemaFailure, ba as RequestSchemaIssue, bb as RequestSchemaLike, bc as RequestSchemaOptions, bd as RequestSchemaResult, be as RequestSchemaSuccess, bf as RequestSchemaValidator, bg as RequestSchemas, bh as RootDataListQueryEntry, bi as RootDataOperation, bj as RootDataReadByFilterQueryEntry, bk as RootDataReadByIdQueryEntry, bl as RootModelCountQueryEntry, bm as RootModelCreateQueryEntry, bn as RootModelDeleteQueryEntry, bo as RootModelDistinctQueryEntry, bp as RootModelListQueryEntry, bq as RootModelNewQueryEntry, br as RootModelOperation, bs as RootModelReadByFilterQueryEntry, bt as RootModelReadByIdQueryEntry, bu as RootModelSubBulkUpdateQueryEntry, bv as RootModelSubCreateQueryEntry, bw as RootModelSubDeleteQueryEntry, bx as RootModelSubListQueryEntry, by as RootModelSubReadQueryEntry, bz as RootModelSubUpdateQueryEntry, bA as RootModelUpdateQueryEntry, bB as RootModelUpsertQueryEntry, bC as RootOperationResult, bD as RootQueryEntry, bE as RootRouterOptions, bF as RootSubOperation, bG as RootTarget, bH as RouteGuardAccess, bI as SelectAccess, bJ as SelectedPopulatedPublicOutput, bK as SelectedPublicOutput, bL as ServiceResult, bM as SingleResult, bN as Sort, bO as SortOrder, bP as StandardSchemaFailure, bQ as StandardSchemaInferOutput, bR as StandardSchemaIssue, bS as StandardSchemaPathSegment, bT as StandardSchemaResult, bU as StandardSchemaSuccess, bV as StandardSchemaV1, bW as StatusCodes, bX as SubListBody, bY as SubPopulate, bZ as SubQueryEntry, b_ as SubReadBody, b$ as SubdocumentBulkRecord, c0 as SubdocumentBulkUpdateInput, c1 as SubdocumentCreateInput, c2 as SubdocumentCreateOptions, c3 as SubdocumentId, c4 as SubdocumentListOptions, c5 as SubdocumentName, c6 as SubdocumentParentArgs, c7 as SubdocumentParentOptions, c8 as SubdocumentReadOptions, c9 as SubdocumentRecord, ca as SuperstructFailureLike, cb as SuperstructValidateLike, cc as Task, cd as TaskType, ce as TransformAccess, cf as TypedFilter, cg as UpdateByIdArgs, ch as UpdateByIdOptions, ci as UpdateOneArgs, cj as UpdateOneOptions, ck as UpdateQueryInput, cl as UpsertArgs, cm as UpsertOptions, cn as UpsertQueryInput, co as ValibotIssueLike, cp as ValibotPathItemLike, cq as ValibotSafeParseLike, cr as ValibotSafeParseResult, cs as ValidateAccess, ct as ValidateRule, cu as Validation, cv as ValidationError, cw as VineValidationErrorLike, cx as VineValidationMessageLike, cy as VineValidatorLike, cz as YupSchemaLike, cA as YupValidationErrorLike, cB as defineRequestSchema, cC as fromAjv, cD as fromArkType, cE as fromIoTs, cF as fromJoi, cG as fromStandardSchema, cH as fromSuperstruct, cI as fromValibot, cJ as fromVine, cK as fromYup, cL as fromZod, cM as parseBody, cN as parseBodyWithSchema, cO as parseNestedBodyWithSchema, cP as parsePathParam, cQ as parseQuery } from './parsers-BidvJUu7.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import 'mongoose';
|
|
4
4
|
import 'express';
|
package/advanced.js
CHANGED
|
@@ -944,6 +944,7 @@ var AccessRuntime = class {
|
|
|
944
944
|
this.modelAtts = {};
|
|
945
945
|
this.modelInstances = {};
|
|
946
946
|
this.openApiRegistry = new OpenApiRegistry();
|
|
947
|
+
this.openApiPathPrefix = "";
|
|
947
948
|
this.allowGlobalModelLookup = options.allowGlobalModelLookup ?? false;
|
|
948
949
|
ensureMongooseJsonSchemaInitialized();
|
|
949
950
|
}
|
|
@@ -972,7 +973,24 @@ var AccessRuntime = class {
|
|
|
972
973
|
return global;
|
|
973
974
|
}
|
|
974
975
|
registerOpenApiRoute(route) {
|
|
975
|
-
this.openApiRegistry.register(
|
|
976
|
+
this.openApiRegistry.register(
|
|
977
|
+
this.openApiPathPrefix ? { ...route, path: (0, import_utils8.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Compose OpenAPI paths while synchronously constructing mounted routers.
|
|
982
|
+
* Prefixes apply before collision checks; prior descriptors and Express paths
|
|
983
|
+
* are unchanged. Nested calls compose and the prior scope is restored even on
|
|
984
|
+
* failure. The callback must finish registration synchronously (no async work).
|
|
985
|
+
*/
|
|
986
|
+
withOpenApiPathPrefix(prefix, build) {
|
|
987
|
+
const previous = this.openApiPathPrefix;
|
|
988
|
+
this.openApiPathPrefix = (0, import_utils8.normalizeUrlPath)(`${previous}/${prefix}`);
|
|
989
|
+
try {
|
|
990
|
+
return build();
|
|
991
|
+
} finally {
|
|
992
|
+
this.openApiPathPrefix = previous;
|
|
993
|
+
}
|
|
976
994
|
}
|
|
977
995
|
/**
|
|
978
996
|
* Enable strict OpenAPI collision detection for the remainder of this
|
|
@@ -1814,19 +1832,12 @@ function fromJoi(schema) {
|
|
|
1814
1832
|
}
|
|
1815
1833
|
function fromAjv(validator) {
|
|
1816
1834
|
return async (value) => {
|
|
1817
|
-
|
|
1818
|
-
if (isThenable(outcome)) {
|
|
1835
|
+
if (validator.$async === true) {
|
|
1819
1836
|
try {
|
|
1820
|
-
const data = await
|
|
1821
|
-
if (data === false) {
|
|
1822
|
-
return {
|
|
1823
|
-
success: false,
|
|
1824
|
-
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
1825
|
-
};
|
|
1826
|
-
}
|
|
1837
|
+
const data = await validator(value);
|
|
1827
1838
|
return {
|
|
1828
1839
|
success: true,
|
|
1829
|
-
data
|
|
1840
|
+
data
|
|
1830
1841
|
};
|
|
1831
1842
|
} catch (error) {
|
|
1832
1843
|
if (!isAjvValidationError(error)) {
|
|
@@ -1834,11 +1845,21 @@ function fromAjv(validator) {
|
|
|
1834
1845
|
}
|
|
1835
1846
|
return {
|
|
1836
1847
|
success: false,
|
|
1837
|
-
issues: normalizeAjvIssues(error.errors
|
|
1848
|
+
issues: normalizeAjvIssues(error.errors)
|
|
1838
1849
|
};
|
|
1839
1850
|
}
|
|
1840
1851
|
}
|
|
1841
|
-
const
|
|
1852
|
+
const outcome = validator(value);
|
|
1853
|
+
if (isThenable(outcome)) {
|
|
1854
|
+
void Promise.resolve(outcome).catch(() => {
|
|
1855
|
+
});
|
|
1856
|
+
throw new TypeError(
|
|
1857
|
+
"fromAjv: promise/thenable validators must declare $async: true and resolve with validated data"
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
if (typeof outcome !== "boolean") {
|
|
1861
|
+
throw new TypeError("fromAjv: synchronous validators must return a boolean verdict");
|
|
1862
|
+
}
|
|
1842
1863
|
if (outcome) {
|
|
1843
1864
|
return {
|
|
1844
1865
|
success: true,
|
|
@@ -1847,7 +1868,9 @@ function fromAjv(validator) {
|
|
|
1847
1868
|
}
|
|
1848
1869
|
return {
|
|
1849
1870
|
success: false,
|
|
1850
|
-
|
|
1871
|
+
// Normalize immediately, copying message/path before another call can
|
|
1872
|
+
// replace the errors array or mutate its entries.
|
|
1873
|
+
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
1851
1874
|
};
|
|
1852
1875
|
};
|
|
1853
1876
|
}
|
|
@@ -2077,14 +2100,14 @@ function isArkTypeErrors(value) {
|
|
|
2077
2100
|
return record.arkKind === "errors" || record[" arkKind"] === "errors";
|
|
2078
2101
|
}
|
|
2079
2102
|
function isThenable(value) {
|
|
2080
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
2103
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
2081
2104
|
}
|
|
2082
2105
|
function isAjvValidationError(error) {
|
|
2083
2106
|
if (typeof error !== "object" || error === null || !("errors" in error)) {
|
|
2084
2107
|
return false;
|
|
2085
2108
|
}
|
|
2086
|
-
const
|
|
2087
|
-
return
|
|
2109
|
+
const candidate = error;
|
|
2110
|
+
return candidate.ajv === true && candidate.validation === true && Array.isArray(candidate.errors);
|
|
2088
2111
|
}
|
|
2089
2112
|
function isVineValidationError(error) {
|
|
2090
2113
|
if (typeof error !== "object" || error === null || !("messages" in error)) {
|
package/advanced.mjs
CHANGED
|
@@ -56,7 +56,8 @@ import {
|
|
|
56
56
|
isNil as isNil2,
|
|
57
57
|
isPlainObject as isPlainObject9,
|
|
58
58
|
isString as isString3,
|
|
59
|
-
keys
|
|
59
|
+
keys,
|
|
60
|
+
normalizeUrlPath
|
|
60
61
|
} from "@web-ts-toolkit/utils";
|
|
61
62
|
|
|
62
63
|
// src/helpers/query.ts
|
|
@@ -873,6 +874,7 @@ var AccessRuntime = class {
|
|
|
873
874
|
this.modelAtts = {};
|
|
874
875
|
this.modelInstances = {};
|
|
875
876
|
this.openApiRegistry = new OpenApiRegistry();
|
|
877
|
+
this.openApiPathPrefix = "";
|
|
876
878
|
this.allowGlobalModelLookup = options.allowGlobalModelLookup ?? false;
|
|
877
879
|
ensureMongooseJsonSchemaInitialized();
|
|
878
880
|
}
|
|
@@ -901,7 +903,24 @@ var AccessRuntime = class {
|
|
|
901
903
|
return global;
|
|
902
904
|
}
|
|
903
905
|
registerOpenApiRoute(route) {
|
|
904
|
-
this.openApiRegistry.register(
|
|
906
|
+
this.openApiRegistry.register(
|
|
907
|
+
this.openApiPathPrefix ? { ...route, path: normalizeUrlPath(`${this.openApiPathPrefix}/${route.path}`) } : route
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Compose OpenAPI paths while synchronously constructing mounted routers.
|
|
912
|
+
* Prefixes apply before collision checks; prior descriptors and Express paths
|
|
913
|
+
* are unchanged. Nested calls compose and the prior scope is restored even on
|
|
914
|
+
* failure. The callback must finish registration synchronously (no async work).
|
|
915
|
+
*/
|
|
916
|
+
withOpenApiPathPrefix(prefix, build) {
|
|
917
|
+
const previous = this.openApiPathPrefix;
|
|
918
|
+
this.openApiPathPrefix = normalizeUrlPath(`${previous}/${prefix}`);
|
|
919
|
+
try {
|
|
920
|
+
return build();
|
|
921
|
+
} finally {
|
|
922
|
+
this.openApiPathPrefix = previous;
|
|
923
|
+
}
|
|
905
924
|
}
|
|
906
925
|
/**
|
|
907
926
|
* Enable strict OpenAPI collision detection for the remainder of this
|
|
@@ -1743,19 +1762,12 @@ function fromJoi(schema) {
|
|
|
1743
1762
|
}
|
|
1744
1763
|
function fromAjv(validator) {
|
|
1745
1764
|
return async (value) => {
|
|
1746
|
-
|
|
1747
|
-
if (isThenable(outcome)) {
|
|
1765
|
+
if (validator.$async === true) {
|
|
1748
1766
|
try {
|
|
1749
|
-
const data = await
|
|
1750
|
-
if (data === false) {
|
|
1751
|
-
return {
|
|
1752
|
-
success: false,
|
|
1753
|
-
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1767
|
+
const data = await validator(value);
|
|
1756
1768
|
return {
|
|
1757
1769
|
success: true,
|
|
1758
|
-
data
|
|
1770
|
+
data
|
|
1759
1771
|
};
|
|
1760
1772
|
} catch (error) {
|
|
1761
1773
|
if (!isAjvValidationError(error)) {
|
|
@@ -1763,11 +1775,21 @@ function fromAjv(validator) {
|
|
|
1763
1775
|
}
|
|
1764
1776
|
return {
|
|
1765
1777
|
success: false,
|
|
1766
|
-
issues: normalizeAjvIssues(error.errors
|
|
1778
|
+
issues: normalizeAjvIssues(error.errors)
|
|
1767
1779
|
};
|
|
1768
1780
|
}
|
|
1769
1781
|
}
|
|
1770
|
-
const
|
|
1782
|
+
const outcome = validator(value);
|
|
1783
|
+
if (isThenable(outcome)) {
|
|
1784
|
+
void Promise.resolve(outcome).catch(() => {
|
|
1785
|
+
});
|
|
1786
|
+
throw new TypeError(
|
|
1787
|
+
"fromAjv: promise/thenable validators must declare $async: true and resolve with validated data"
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
if (typeof outcome !== "boolean") {
|
|
1791
|
+
throw new TypeError("fromAjv: synchronous validators must return a boolean verdict");
|
|
1792
|
+
}
|
|
1771
1793
|
if (outcome) {
|
|
1772
1794
|
return {
|
|
1773
1795
|
success: true,
|
|
@@ -1776,7 +1798,9 @@ function fromAjv(validator) {
|
|
|
1776
1798
|
}
|
|
1777
1799
|
return {
|
|
1778
1800
|
success: false,
|
|
1779
|
-
|
|
1801
|
+
// Normalize immediately, copying message/path before another call can
|
|
1802
|
+
// replace the errors array or mutate its entries.
|
|
1803
|
+
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
1780
1804
|
};
|
|
1781
1805
|
};
|
|
1782
1806
|
}
|
|
@@ -2006,14 +2030,14 @@ function isArkTypeErrors(value) {
|
|
|
2006
2030
|
return record.arkKind === "errors" || record[" arkKind"] === "errors";
|
|
2007
2031
|
}
|
|
2008
2032
|
function isThenable(value) {
|
|
2009
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
2033
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
2010
2034
|
}
|
|
2011
2035
|
function isAjvValidationError(error) {
|
|
2012
2036
|
if (typeof error !== "object" || error === null || !("errors" in error)) {
|
|
2013
2037
|
return false;
|
|
2014
2038
|
}
|
|
2015
|
-
const
|
|
2016
|
-
return
|
|
2039
|
+
const candidate = error;
|
|
2040
|
+
return candidate.ajv === true && candidate.validation === true && Array.isArray(candidate.errors);
|
|
2017
2041
|
}
|
|
2018
2042
|
function isVineValidationError(error) {
|
|
2019
2043
|
if (typeof error !== "object" || error === null || !("messages" in error)) {
|