@web-ts-toolkit/access-router 0.55.0 → 0.57.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 +218 -56
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +100 -61
- package/advanced.mjs +67 -28
- package/index.d.mts +2 -2
- package/index.d.ts +2 -2
- package/index.js +624 -533
- package/index.mjs +361 -270
- package/llms.txt +3 -2
- package/package.json +3 -3
- package/{parsers-BrVRL2fg.d.mts → parsers-CngKnVjW.d.mts} +93 -14
- package/{parsers-BrVRL2fg.d.ts → parsers-CngKnVjW.d.ts} +93 -14
package/llms.txt
CHANGED
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
- [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)
|
|
36
36
|
- [Correlated includes and literal escapes](README.md#correlated-includes) · [Nested writes and trusted hooks](README.md#nested-updates-and-trusted-hooks)
|
|
37
37
|
- [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)
|
|
38
|
-
- [Private populated dependencies](README.md#private-populated-dependencies) · [Runnable
|
|
39
|
-
- [
|
|
38
|
+
- [Private populated dependencies](README.md#private-populated-dependencies) · [Runnable example](README.md#runnable-private-dependency-example): server virtual `dependsOnPopulate: [{ path, select? }]`, automatically retained scope-local static ObjectId ref/1D array, eagerly registered same-runtime/same-connection target. Explicit `ModelVirtualDescriptor<Source, Value, { ref: Target }>` types only optional getter argument four (`populated = {}`); argument one/local dependsOn and persisted/write/public types stay unchanged. Root/advanced types: ModelVirtualPopulateDependency, ModelVirtualPopulateInputs. No client/query authority or new entrypoint.
|
|
39
|
+
- [Runtime populate operation policies](README.md#runtime-populate-operation-policies): exactly `requireOperationAccessForPopulateQueries` and `requireOperationAccessForPopulateDependencies`, both default true; only literal false bypasses its operation gate (malformed JS/undefined is true). Q=false skips ordinary effective target read/list/custom admission only; source entry/field authorization, target field/document grants, rows/transforms, original select/_id, match and mandatory IDs remain. D=false skips private target base operationAccess.read only; server declarations grant input fields, virtual output authorization and request baseFilter.read/overrideFilter.read/original IDs remain. Four independent modes: true/true strict both; false/true public only; true/false private only; false/false both, subject to those policies. Direct/root target access stays denied. Every target needs an actual runtime-registered model and effective object permissionSchema (defaults allowed, {} closes business fields); a configuration-only Document needs no HTTP routes. Missing policy is controlled bad_request before target I/O/write. Typed GlobalOptions/setters/aliases/getGlobalOptions frozen snapshots; server/runtime-only, captured before awaits through nested output, later calls see updates, runtimes isolated.
|
|
40
|
+
- [Reference reconstruction](README.md#target-authority-selection-and-reconstruction) · [Breaking migration](README.md#breaking-release-populate-policy-migration): no additional target-base-read reference-visibility gate. Adding a virtual does not change ordinary refs. Source unselected/field-denied -> omit; no admitted public population -> exact original IDs/arrays/nulls/missing IDs/order/duplicates/presence, even on strict private denial or private row/query/getter failure. Admitted population -> original authorized mask, scalar null/filtered-array misses (not IDs). Compatible positive fetch unions retain separate immutable public/private masks and request read-row scope, independent of public match/access/related grants. Each getter sees only its declared fourth-argument mask; no implicit DTO/handle/metadata/hook/snapshot/task transport, only its authorized deliberate return. No recursive private target virtuals or successful-write retries. Removed old flags have no aliases/raw mapping; see the migration note. README covers error guards/static validation/native isolation/hydration caveats and measured batching.
|
|
40
41
|
|
|
41
42
|
## Additional examples
|
|
42
43
|
|
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.57.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.57.0",
|
|
61
|
+
"@web-ts-toolkit/utils": "0.57.0",
|
|
62
62
|
"just-diff": "^6.0.2",
|
|
63
63
|
"mongoose-schema-jsonschema": "^4.0.1",
|
|
64
64
|
"sift": "^17.1.3",
|
|
@@ -420,10 +420,22 @@ type ModelVirtualDoc<TModel = unknown> = IsUnknownVirtual<TModel> extends true ?
|
|
|
420
420
|
* and traversal through target refs are rejected. `_id` is included unless excluded.
|
|
421
421
|
* Static string refs must bind the actual same-connection ObjectId model; register
|
|
422
422
|
* its model instance AND options first. Validation is eager/atomic, including setters.
|
|
423
|
-
* This
|
|
424
|
-
*
|
|
425
|
-
* the
|
|
423
|
+
* This bypasses source/target input-field permissions, not the virtual's own output
|
|
424
|
+
* authorization. GlobalOptions.requireOperationAccessForPopulateDependencies defaults
|
|
425
|
+
* to true, requiring the captured caller's target base operationAccess.read for private
|
|
426
|
+
* loading. Only literal false bypasses that gate; private-only loading then adds no
|
|
427
|
+
* operation check solely for the fetch. Target overrideFilter.read/baseFilter.read and
|
|
428
|
+
* retained source IDs always use request authority/tenant scope, independently of ordinary
|
|
426
429
|
* populate match/access and authorization.related. Private target virtuals never run.
|
|
430
|
+
* Private work does not change ordinary reference shape. Selected/permitted source refs
|
|
431
|
+
* keep exact original IDs, including array order/duplicates/missing IDs/nulls, unless an
|
|
432
|
+
* admitted ordinary population owns the slot (then its original public mask/miss applies).
|
|
433
|
+
* Strict private denial, row/query/getter failure and deferred virtual denial cannot hide
|
|
434
|
+
* permitted IDs. Only source unselection/field denial omits them; there is no extra target
|
|
435
|
+
* base-read visibility gate. GlobalOptions.requireOperationAccessForPopulateQueries
|
|
436
|
+
* independently controls ordinary target operation admission. Both flags and source/target
|
|
437
|
+
* policy are captured before awaits; registration and an effective object permissionSchema
|
|
438
|
+
* remain mandatory. Private inputs never implicitly enter hooks, handles or metadata.
|
|
427
439
|
*/
|
|
428
440
|
interface ModelVirtualPopulateDependency<TModel = unknown> {
|
|
429
441
|
/** Top-level ref relative to this virtual's scope; no dotted source paths. Retained automatically. */
|
|
@@ -468,7 +480,7 @@ type ModelVirtualPopulateInputs<TModel = unknown, TPopulated extends VirtualPopu
|
|
|
468
480
|
* It is separate from `doc`, whose persisted/public-related typing is unchanged.
|
|
469
481
|
* The runtime supplies it (empty without a declaration); the empty default generic
|
|
470
482
|
* guesses no target shape. Optionality preserves legacy three-argument direct calls
|
|
471
|
-
* and callbacks. Absent source refs or unavailable
|
|
483
|
+
* and callbacks. Absent source refs or unavailable private inputs skip invocation;
|
|
472
484
|
* present null/empty refs and missing target rows yield null/[]/null array slots.
|
|
473
485
|
* Private values are not implicitly transported to output or authorization metadata;
|
|
474
486
|
* an authorized getter can deliberately return a computed value using them.
|
|
@@ -494,7 +506,9 @@ type ModelVirtualGetter<TModel = unknown, TValue = unknown, TPopulated extends V
|
|
|
494
506
|
* generic never changes the persisted model, write types or public output.
|
|
495
507
|
* Use `populated = {}` or guard undefined to retain three-argument direct calls.
|
|
496
508
|
* See the shipped README's "Private populated dependencies" for registration,
|
|
497
|
-
*
|
|
509
|
+
* source-field omission/population/ID shapes, independent captured operation policies,
|
|
510
|
+
* mandatory row gates and absence/error rules. Private computation does not grant output
|
|
511
|
+
* or remove otherwise permitted parent IDs.
|
|
498
512
|
*
|
|
499
513
|
* @example
|
|
500
514
|
* ```ts
|
|
@@ -1143,6 +1157,10 @@ interface AuthorizationConfiguration {
|
|
|
1143
1157
|
readonly catalog: AuthorizationFieldCatalog;
|
|
1144
1158
|
/** Opaque terminal private-input capture. Never related authority or public document metadata. */
|
|
1145
1159
|
readonly virtualPopulateSnapshot?: VirtualPopulateSnapshotToken;
|
|
1160
|
+
/** Runtime-owned private input admission mode, not public output/related authority. */
|
|
1161
|
+
readonly requireOperationAccessForPopulateDependencies?: boolean;
|
|
1162
|
+
/** Independent ordinary-populate operation admission from the same global snapshot. */
|
|
1163
|
+
readonly requireOperationAccessForPopulateQueries?: boolean;
|
|
1146
1164
|
readonly documentPermissionField?: string;
|
|
1147
1165
|
/** Captured permission schema, virtual descriptors, always-select and metadata posture. */
|
|
1148
1166
|
readonly [key: string]: unknown;
|
|
@@ -1156,8 +1174,7 @@ interface AuthorizationRelatedTarget {
|
|
|
1156
1174
|
readonly queryIntent?: ModelServiceIntent;
|
|
1157
1175
|
/** Existing include operation gate, resolved synchronously with its target. */
|
|
1158
1176
|
readonly operationGuard?: unknown;
|
|
1159
|
-
|
|
1160
|
-
readonly kind?: 'output' | 'count' | 'passthrough';
|
|
1177
|
+
readonly kind?: 'output' | 'count';
|
|
1161
1178
|
readonly relatedTargets?: readonly AuthorizationRelatedTarget[];
|
|
1162
1179
|
/** Captured supported raw-ref transport shape; exotic ordinary populates keep their adapter path. */
|
|
1163
1180
|
readonly staticObjectIdReference?: boolean;
|
|
@@ -2096,7 +2113,7 @@ declare class Core {
|
|
|
2096
2113
|
/** Persisted request-only populate admission; fetch planning is not field authority. */
|
|
2097
2114
|
private requestPopulateSelect;
|
|
2098
2115
|
/** Request-only population. Use named response scopePath; positional subPaths are removed. */
|
|
2099
|
-
genPopulate(modelName: string, access?: SelectAccess | BaseFilterAccess, _populate?: Populate | Populate[] | string | null, options?: PopulatePlanningOptions): Promise<
|
|
2116
|
+
genPopulate(modelName: string, access?: SelectAccess | BaseFilterAccess, _populate?: Populate | Populate[] | string | null, options?: PopulatePlanningOptions): Promise<Populate[]>;
|
|
2100
2117
|
validate(modelName: string, allowedData: unknown, access: ValidateAccess, context: ModelHookContext): Promise<boolean | unknown[]>;
|
|
2101
2118
|
prepare<T>(modelName: string, allowedData: T, access: PrepareAccess, context: ModelHookContext): Promise<T>;
|
|
2102
2119
|
transform<T>(modelName: string, doc: T, access: TransformAccess, context: ModelHookContext): Promise<T>;
|
|
@@ -3116,7 +3133,7 @@ interface AccessRouterLogger {
|
|
|
3116
3133
|
isLevelEnabled?: (level: string) => boolean;
|
|
3117
3134
|
}
|
|
3118
3135
|
/**
|
|
3119
|
-
* Runtime-wide request initialization and resource settings, configured with
|
|
3136
|
+
* Runtime-wide request initialization, populate operation policies and resource settings, configured with
|
|
3120
3137
|
* api.setGlobalOptions / api.setGlobalOption. These are not model options or wire
|
|
3121
3138
|
* options. Object assignment is shallow; nested requestComplexity replaces as a unit.
|
|
3122
3139
|
*/
|
|
@@ -3132,8 +3149,57 @@ interface GlobalOptions {
|
|
|
3132
3149
|
* context. Generated routers do not authenticate client claims for this hook.
|
|
3133
3150
|
*/
|
|
3134
3151
|
globalPermissions?: (this: AccessRouterRequest, req: AccessRouterRequest) => MaybePromise<GlobalPermissionValue>;
|
|
3135
|
-
/**
|
|
3136
|
-
|
|
3152
|
+
/**
|
|
3153
|
+
* Require the caller's effective target operation (`read`, supported `list` or trusted
|
|
3154
|
+
* custom access) for ordinary query population (default `true`). Only literal `false`
|
|
3155
|
+
* skips that admission gate; source entry/field authorization, target field/document
|
|
3156
|
+
* grants, row/base/transform policies, original selection, match and mandatory reference
|
|
3157
|
+
* IDs still constrain output. Denied population retains selected/permitted original
|
|
3158
|
+
* source IDs; admitted population misses return null/filtered arrays, not restored IDs.
|
|
3159
|
+
* Targets always require an actual Mongoose model and ACL configuration with an
|
|
3160
|
+
* effective object permissionSchema in this runtime; `{}` closes business fields.
|
|
3161
|
+
* Runtime-owned and captured before awaits, independently of private dependency
|
|
3162
|
+
* admission. A configuration-only Document needs no HTTP routes. Its denied direct/root
|
|
3163
|
+
* read/list operations stay denied. No per-model/query/client override.
|
|
3164
|
+
*/
|
|
3165
|
+
requireOperationAccessForPopulateQueries?: boolean;
|
|
3166
|
+
/**
|
|
3167
|
+
* Require the original caller's target base `operationAccess.read` before loading
|
|
3168
|
+
* declared private `dependsOnPopulate` inputs (default `true`). Only literal boolean
|
|
3169
|
+
* `false` bypasses that operation gate; undefined/malformed JS values normalize to
|
|
3170
|
+
* `true`. Source/target input-field permissions are already bypassed by the server
|
|
3171
|
+
* declaration; the virtual's own output authorization still applies. Target request-mode
|
|
3172
|
+
* `baseFilter.read`, `overrideFilter.read` and retained reference IDs always constrain rows.
|
|
3173
|
+
* Captured with source/target policy and query intent before the first await, including
|
|
3174
|
+
* nested output scopes. In-flight calls retain their mode; later calls see updates.
|
|
3175
|
+
* Runtime-isolated server configuration only: no caller/query or per-model override.
|
|
3176
|
+
*
|
|
3177
|
+
* Neither mode changes ordinary reference shape: selected/permitted parent fields keep
|
|
3178
|
+
* exact original IDs (including array order, duplicates, missing IDs and nulls) when no
|
|
3179
|
+
* admitted ordinary population owns them. Private denial, row/query/getter failure or
|
|
3180
|
+
* deferred virtual denial cannot hide those IDs. Omit only for parent unselection/field
|
|
3181
|
+
* denial; admitted public DTO/null/filtered-array results retain their original masks.
|
|
3182
|
+
* There is no additional target-base-read reference-visibility gate. False-mode loading
|
|
3183
|
+
* adds no operation check solely for its private fetch. Private fourth-argument inputs
|
|
3184
|
+
* never implicitly enter DTOs, hooks, handles, snapshots or permission metadata.
|
|
3185
|
+
* An actual registered model, effective object permissionSchema and static declarations
|
|
3186
|
+
* remain mandatory, independently of requireOperationAccessForPopulateQueries.
|
|
3187
|
+
*
|
|
3188
|
+
* @example
|
|
3189
|
+
* ```ts
|
|
3190
|
+
* import { createAccessRuntime, type GlobalOptions } from '@web-ts-toolkit/access-router';
|
|
3191
|
+
* const api = createAccessRuntime();
|
|
3192
|
+
* const policy = {
|
|
3193
|
+
* requireOperationAccessForPopulateQueries: true,
|
|
3194
|
+
* requireOperationAccessForPopulateDependencies: false,
|
|
3195
|
+
* } satisfies GlobalOptions;
|
|
3196
|
+
* api.setGlobalOptions(policy); // private computation; denied public population retains permitted IDs
|
|
3197
|
+
* const snapshot: GlobalOptions = api.getGlobalOptions();
|
|
3198
|
+
* api.setGlobalOption('requireOperationAccessForPopulateDependencies', true);
|
|
3199
|
+
* void snapshot; // stored snapshot; read again to observe the setter
|
|
3200
|
+
* ```
|
|
3201
|
+
*/
|
|
3202
|
+
requireOperationAccessForPopulateDependencies?: boolean;
|
|
3137
3203
|
logger?: AccessRouterLogger;
|
|
3138
3204
|
/** Request budgets and separate hook/persistence pools; concurrency limits are captured on first owner use. */
|
|
3139
3205
|
requestComplexity?: RequestComplexityOptions;
|
|
@@ -3289,6 +3355,12 @@ interface ModelHydrationOptions {
|
|
|
3289
3355
|
}
|
|
3290
3356
|
/** Model starting defaults; data/root routers do not inherit these. Nested values assign shallowly. */
|
|
3291
3357
|
interface DefaultModelRouterOptions<TModel = unknown> {
|
|
3358
|
+
/**
|
|
3359
|
+
* Default business-field policy when a model's permissionSchema is undefined.
|
|
3360
|
+
* Populate targets require an effective object schema; `{}` explicitly closes
|
|
3361
|
+
* business fields. A model's defined schema replaces this entire default.
|
|
3362
|
+
*/
|
|
3363
|
+
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel>>;
|
|
3292
3364
|
/** Server-owned fetch choices, captured per call. Not a per-call or method-default option. */
|
|
3293
3365
|
hydration?: ModelHydrationOptions;
|
|
3294
3366
|
/** Maximum returned list rows (default 1000), independent of exact match/count totals. */
|
|
@@ -3414,6 +3486,8 @@ interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record
|
|
|
3414
3486
|
* siblings; a whole-object/array grant permits replacement (no generic deep merge).
|
|
3415
3487
|
* Send nested JSON; literal dotted client keys are not update operators.
|
|
3416
3488
|
* Basic/advanced keys are route guards in operationAccess, not field grants here.
|
|
3489
|
+
* Requested populate targets require an effective object schema in their owning
|
|
3490
|
+
* runtime (model defaults may supply it); `{}` explicitly closes business fields.
|
|
3417
3491
|
*/
|
|
3418
3492
|
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel> | Extract<keyof TVirtuals, string>>;
|
|
3419
3493
|
/**
|
|
@@ -3441,8 +3515,13 @@ interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record
|
|
|
3441
3515
|
* that getter's fourth argument. Annotate ModelVirtualDescriptor<Source, Value,
|
|
3442
3516
|
* { ref: Target }> separately, then assign here; persisted/write/public types
|
|
3443
3517
|
* stay unchanged. Register target models/options first in this runtime. Source
|
|
3444
|
-
* refs are retained automatically
|
|
3445
|
-
*
|
|
3518
|
+
* refs are retained automatically. GlobalOptions.requireOperationAccessForPopulateDependencies
|
|
3519
|
+
* defaults to true; false bypasses only private target-operation admission, never
|
|
3520
|
+
* request read-row filters/retained IDs. Neither mode changes ordinary reference
|
|
3521
|
+
* shape: admitted population keeps its original authorized mask/miss; otherwise
|
|
3522
|
+
* selected/permitted parent fields keep exact original IDs, even on private failure.
|
|
3523
|
+
* Only unselected/field-denied source refs are omitted. Query operation admission is
|
|
3524
|
+
* independently controlled by GlobalOptions.requireOperationAccessForPopulateQueries.
|
|
3446
3525
|
*
|
|
3447
3526
|
* @example Shortest typed happy-path (persisted model lacks `fullAddress`):
|
|
3448
3527
|
* ```ts
|
|
@@ -3567,7 +3646,7 @@ interface DataRouterOptions<TData = unknown> {
|
|
|
3567
3646
|
decorateAll?: DataListHook<TData> | Record<string, DataListHook<TData>>;
|
|
3568
3647
|
requestSchemas?: DataRequestSchemas;
|
|
3569
3648
|
}
|
|
3570
|
-
interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, ExtendedDefaultModelRouterOptions<TModel> {
|
|
3649
|
+
interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, Omit<ExtendedDefaultModelRouterOptions<TModel>, 'permissionSchema'> {
|
|
3571
3650
|
'alwaysSelectFields.default'?: string[];
|
|
3572
3651
|
'alwaysSelectFields.list'?: string[];
|
|
3573
3652
|
'alwaysSelectFields.create'?: string[];
|
|
@@ -420,10 +420,22 @@ type ModelVirtualDoc<TModel = unknown> = IsUnknownVirtual<TModel> extends true ?
|
|
|
420
420
|
* and traversal through target refs are rejected. `_id` is included unless excluded.
|
|
421
421
|
* Static string refs must bind the actual same-connection ObjectId model; register
|
|
422
422
|
* its model instance AND options first. Validation is eager/atomic, including setters.
|
|
423
|
-
* This
|
|
424
|
-
*
|
|
425
|
-
* the
|
|
423
|
+
* This bypasses source/target input-field permissions, not the virtual's own output
|
|
424
|
+
* authorization. GlobalOptions.requireOperationAccessForPopulateDependencies defaults
|
|
425
|
+
* to true, requiring the captured caller's target base operationAccess.read for private
|
|
426
|
+
* loading. Only literal false bypasses that gate; private-only loading then adds no
|
|
427
|
+
* operation check solely for the fetch. Target overrideFilter.read/baseFilter.read and
|
|
428
|
+
* retained source IDs always use request authority/tenant scope, independently of ordinary
|
|
426
429
|
* populate match/access and authorization.related. Private target virtuals never run.
|
|
430
|
+
* Private work does not change ordinary reference shape. Selected/permitted source refs
|
|
431
|
+
* keep exact original IDs, including array order/duplicates/missing IDs/nulls, unless an
|
|
432
|
+
* admitted ordinary population owns the slot (then its original public mask/miss applies).
|
|
433
|
+
* Strict private denial, row/query/getter failure and deferred virtual denial cannot hide
|
|
434
|
+
* permitted IDs. Only source unselection/field denial omits them; there is no extra target
|
|
435
|
+
* base-read visibility gate. GlobalOptions.requireOperationAccessForPopulateQueries
|
|
436
|
+
* independently controls ordinary target operation admission. Both flags and source/target
|
|
437
|
+
* policy are captured before awaits; registration and an effective object permissionSchema
|
|
438
|
+
* remain mandatory. Private inputs never implicitly enter hooks, handles or metadata.
|
|
427
439
|
*/
|
|
428
440
|
interface ModelVirtualPopulateDependency<TModel = unknown> {
|
|
429
441
|
/** Top-level ref relative to this virtual's scope; no dotted source paths. Retained automatically. */
|
|
@@ -468,7 +480,7 @@ type ModelVirtualPopulateInputs<TModel = unknown, TPopulated extends VirtualPopu
|
|
|
468
480
|
* It is separate from `doc`, whose persisted/public-related typing is unchanged.
|
|
469
481
|
* The runtime supplies it (empty without a declaration); the empty default generic
|
|
470
482
|
* guesses no target shape. Optionality preserves legacy three-argument direct calls
|
|
471
|
-
* and callbacks. Absent source refs or unavailable
|
|
483
|
+
* and callbacks. Absent source refs or unavailable private inputs skip invocation;
|
|
472
484
|
* present null/empty refs and missing target rows yield null/[]/null array slots.
|
|
473
485
|
* Private values are not implicitly transported to output or authorization metadata;
|
|
474
486
|
* an authorized getter can deliberately return a computed value using them.
|
|
@@ -494,7 +506,9 @@ type ModelVirtualGetter<TModel = unknown, TValue = unknown, TPopulated extends V
|
|
|
494
506
|
* generic never changes the persisted model, write types or public output.
|
|
495
507
|
* Use `populated = {}` or guard undefined to retain three-argument direct calls.
|
|
496
508
|
* See the shipped README's "Private populated dependencies" for registration,
|
|
497
|
-
*
|
|
509
|
+
* source-field omission/population/ID shapes, independent captured operation policies,
|
|
510
|
+
* mandatory row gates and absence/error rules. Private computation does not grant output
|
|
511
|
+
* or remove otherwise permitted parent IDs.
|
|
498
512
|
*
|
|
499
513
|
* @example
|
|
500
514
|
* ```ts
|
|
@@ -1143,6 +1157,10 @@ interface AuthorizationConfiguration {
|
|
|
1143
1157
|
readonly catalog: AuthorizationFieldCatalog;
|
|
1144
1158
|
/** Opaque terminal private-input capture. Never related authority or public document metadata. */
|
|
1145
1159
|
readonly virtualPopulateSnapshot?: VirtualPopulateSnapshotToken;
|
|
1160
|
+
/** Runtime-owned private input admission mode, not public output/related authority. */
|
|
1161
|
+
readonly requireOperationAccessForPopulateDependencies?: boolean;
|
|
1162
|
+
/** Independent ordinary-populate operation admission from the same global snapshot. */
|
|
1163
|
+
readonly requireOperationAccessForPopulateQueries?: boolean;
|
|
1146
1164
|
readonly documentPermissionField?: string;
|
|
1147
1165
|
/** Captured permission schema, virtual descriptors, always-select and metadata posture. */
|
|
1148
1166
|
readonly [key: string]: unknown;
|
|
@@ -1156,8 +1174,7 @@ interface AuthorizationRelatedTarget {
|
|
|
1156
1174
|
readonly queryIntent?: ModelServiceIntent;
|
|
1157
1175
|
/** Existing include operation gate, resolved synchronously with its target. */
|
|
1158
1176
|
readonly operationGuard?: unknown;
|
|
1159
|
-
|
|
1160
|
-
readonly kind?: 'output' | 'count' | 'passthrough';
|
|
1177
|
+
readonly kind?: 'output' | 'count';
|
|
1161
1178
|
readonly relatedTargets?: readonly AuthorizationRelatedTarget[];
|
|
1162
1179
|
/** Captured supported raw-ref transport shape; exotic ordinary populates keep their adapter path. */
|
|
1163
1180
|
readonly staticObjectIdReference?: boolean;
|
|
@@ -2096,7 +2113,7 @@ declare class Core {
|
|
|
2096
2113
|
/** Persisted request-only populate admission; fetch planning is not field authority. */
|
|
2097
2114
|
private requestPopulateSelect;
|
|
2098
2115
|
/** Request-only population. Use named response scopePath; positional subPaths are removed. */
|
|
2099
|
-
genPopulate(modelName: string, access?: SelectAccess | BaseFilterAccess, _populate?: Populate | Populate[] | string | null, options?: PopulatePlanningOptions): Promise<
|
|
2116
|
+
genPopulate(modelName: string, access?: SelectAccess | BaseFilterAccess, _populate?: Populate | Populate[] | string | null, options?: PopulatePlanningOptions): Promise<Populate[]>;
|
|
2100
2117
|
validate(modelName: string, allowedData: unknown, access: ValidateAccess, context: ModelHookContext): Promise<boolean | unknown[]>;
|
|
2101
2118
|
prepare<T>(modelName: string, allowedData: T, access: PrepareAccess, context: ModelHookContext): Promise<T>;
|
|
2102
2119
|
transform<T>(modelName: string, doc: T, access: TransformAccess, context: ModelHookContext): Promise<T>;
|
|
@@ -3116,7 +3133,7 @@ interface AccessRouterLogger {
|
|
|
3116
3133
|
isLevelEnabled?: (level: string) => boolean;
|
|
3117
3134
|
}
|
|
3118
3135
|
/**
|
|
3119
|
-
* Runtime-wide request initialization and resource settings, configured with
|
|
3136
|
+
* Runtime-wide request initialization, populate operation policies and resource settings, configured with
|
|
3120
3137
|
* api.setGlobalOptions / api.setGlobalOption. These are not model options or wire
|
|
3121
3138
|
* options. Object assignment is shallow; nested requestComplexity replaces as a unit.
|
|
3122
3139
|
*/
|
|
@@ -3132,8 +3149,57 @@ interface GlobalOptions {
|
|
|
3132
3149
|
* context. Generated routers do not authenticate client claims for this hook.
|
|
3133
3150
|
*/
|
|
3134
3151
|
globalPermissions?: (this: AccessRouterRequest, req: AccessRouterRequest) => MaybePromise<GlobalPermissionValue>;
|
|
3135
|
-
/**
|
|
3136
|
-
|
|
3152
|
+
/**
|
|
3153
|
+
* Require the caller's effective target operation (`read`, supported `list` or trusted
|
|
3154
|
+
* custom access) for ordinary query population (default `true`). Only literal `false`
|
|
3155
|
+
* skips that admission gate; source entry/field authorization, target field/document
|
|
3156
|
+
* grants, row/base/transform policies, original selection, match and mandatory reference
|
|
3157
|
+
* IDs still constrain output. Denied population retains selected/permitted original
|
|
3158
|
+
* source IDs; admitted population misses return null/filtered arrays, not restored IDs.
|
|
3159
|
+
* Targets always require an actual Mongoose model and ACL configuration with an
|
|
3160
|
+
* effective object permissionSchema in this runtime; `{}` closes business fields.
|
|
3161
|
+
* Runtime-owned and captured before awaits, independently of private dependency
|
|
3162
|
+
* admission. A configuration-only Document needs no HTTP routes. Its denied direct/root
|
|
3163
|
+
* read/list operations stay denied. No per-model/query/client override.
|
|
3164
|
+
*/
|
|
3165
|
+
requireOperationAccessForPopulateQueries?: boolean;
|
|
3166
|
+
/**
|
|
3167
|
+
* Require the original caller's target base `operationAccess.read` before loading
|
|
3168
|
+
* declared private `dependsOnPopulate` inputs (default `true`). Only literal boolean
|
|
3169
|
+
* `false` bypasses that operation gate; undefined/malformed JS values normalize to
|
|
3170
|
+
* `true`. Source/target input-field permissions are already bypassed by the server
|
|
3171
|
+
* declaration; the virtual's own output authorization still applies. Target request-mode
|
|
3172
|
+
* `baseFilter.read`, `overrideFilter.read` and retained reference IDs always constrain rows.
|
|
3173
|
+
* Captured with source/target policy and query intent before the first await, including
|
|
3174
|
+
* nested output scopes. In-flight calls retain their mode; later calls see updates.
|
|
3175
|
+
* Runtime-isolated server configuration only: no caller/query or per-model override.
|
|
3176
|
+
*
|
|
3177
|
+
* Neither mode changes ordinary reference shape: selected/permitted parent fields keep
|
|
3178
|
+
* exact original IDs (including array order, duplicates, missing IDs and nulls) when no
|
|
3179
|
+
* admitted ordinary population owns them. Private denial, row/query/getter failure or
|
|
3180
|
+
* deferred virtual denial cannot hide those IDs. Omit only for parent unselection/field
|
|
3181
|
+
* denial; admitted public DTO/null/filtered-array results retain their original masks.
|
|
3182
|
+
* There is no additional target-base-read reference-visibility gate. False-mode loading
|
|
3183
|
+
* adds no operation check solely for its private fetch. Private fourth-argument inputs
|
|
3184
|
+
* never implicitly enter DTOs, hooks, handles, snapshots or permission metadata.
|
|
3185
|
+
* An actual registered model, effective object permissionSchema and static declarations
|
|
3186
|
+
* remain mandatory, independently of requireOperationAccessForPopulateQueries.
|
|
3187
|
+
*
|
|
3188
|
+
* @example
|
|
3189
|
+
* ```ts
|
|
3190
|
+
* import { createAccessRuntime, type GlobalOptions } from '@web-ts-toolkit/access-router';
|
|
3191
|
+
* const api = createAccessRuntime();
|
|
3192
|
+
* const policy = {
|
|
3193
|
+
* requireOperationAccessForPopulateQueries: true,
|
|
3194
|
+
* requireOperationAccessForPopulateDependencies: false,
|
|
3195
|
+
* } satisfies GlobalOptions;
|
|
3196
|
+
* api.setGlobalOptions(policy); // private computation; denied public population retains permitted IDs
|
|
3197
|
+
* const snapshot: GlobalOptions = api.getGlobalOptions();
|
|
3198
|
+
* api.setGlobalOption('requireOperationAccessForPopulateDependencies', true);
|
|
3199
|
+
* void snapshot; // stored snapshot; read again to observe the setter
|
|
3200
|
+
* ```
|
|
3201
|
+
*/
|
|
3202
|
+
requireOperationAccessForPopulateDependencies?: boolean;
|
|
3137
3203
|
logger?: AccessRouterLogger;
|
|
3138
3204
|
/** Request budgets and separate hook/persistence pools; concurrency limits are captured on first owner use. */
|
|
3139
3205
|
requestComplexity?: RequestComplexityOptions;
|
|
@@ -3289,6 +3355,12 @@ interface ModelHydrationOptions {
|
|
|
3289
3355
|
}
|
|
3290
3356
|
/** Model starting defaults; data/root routers do not inherit these. Nested values assign shallowly. */
|
|
3291
3357
|
interface DefaultModelRouterOptions<TModel = unknown> {
|
|
3358
|
+
/**
|
|
3359
|
+
* Default business-field policy when a model's permissionSchema is undefined.
|
|
3360
|
+
* Populate targets require an effective object schema; `{}` explicitly closes
|
|
3361
|
+
* business fields. A model's defined schema replaces this entire default.
|
|
3362
|
+
*/
|
|
3363
|
+
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel>>;
|
|
3292
3364
|
/** Server-owned fetch choices, captured per call. Not a per-call or method-default option. */
|
|
3293
3365
|
hydration?: ModelHydrationOptions;
|
|
3294
3366
|
/** Maximum returned list rows (default 1000), independent of exact match/count totals. */
|
|
@@ -3414,6 +3486,8 @@ interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record
|
|
|
3414
3486
|
* siblings; a whole-object/array grant permits replacement (no generic deep merge).
|
|
3415
3487
|
* Send nested JSON; literal dotted client keys are not update operators.
|
|
3416
3488
|
* Basic/advanced keys are route guards in operationAccess, not field grants here.
|
|
3489
|
+
* Requested populate targets require an effective object schema in their owning
|
|
3490
|
+
* runtime (model defaults may supply it); `{}` explicitly closes business fields.
|
|
3417
3491
|
*/
|
|
3418
3492
|
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel> | Extract<keyof TVirtuals, string>>;
|
|
3419
3493
|
/**
|
|
@@ -3441,8 +3515,13 @@ interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record
|
|
|
3441
3515
|
* that getter's fourth argument. Annotate ModelVirtualDescriptor<Source, Value,
|
|
3442
3516
|
* { ref: Target }> separately, then assign here; persisted/write/public types
|
|
3443
3517
|
* stay unchanged. Register target models/options first in this runtime. Source
|
|
3444
|
-
* refs are retained automatically
|
|
3445
|
-
*
|
|
3518
|
+
* refs are retained automatically. GlobalOptions.requireOperationAccessForPopulateDependencies
|
|
3519
|
+
* defaults to true; false bypasses only private target-operation admission, never
|
|
3520
|
+
* request read-row filters/retained IDs. Neither mode changes ordinary reference
|
|
3521
|
+
* shape: admitted population keeps its original authorized mask/miss; otherwise
|
|
3522
|
+
* selected/permitted parent fields keep exact original IDs, even on private failure.
|
|
3523
|
+
* Only unselected/field-denied source refs are omitted. Query operation admission is
|
|
3524
|
+
* independently controlled by GlobalOptions.requireOperationAccessForPopulateQueries.
|
|
3446
3525
|
*
|
|
3447
3526
|
* @example Shortest typed happy-path (persisted model lacks `fullAddress`):
|
|
3448
3527
|
* ```ts
|
|
@@ -3567,7 +3646,7 @@ interface DataRouterOptions<TData = unknown> {
|
|
|
3567
3646
|
decorateAll?: DataListHook<TData> | Record<string, DataListHook<TData>>;
|
|
3568
3647
|
requestSchemas?: DataRequestSchemas;
|
|
3569
3648
|
}
|
|
3570
|
-
interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, ExtendedDefaultModelRouterOptions<TModel> {
|
|
3649
|
+
interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, Omit<ExtendedDefaultModelRouterOptions<TModel>, 'permissionSchema'> {
|
|
3571
3650
|
'alwaysSelectFields.default'?: string[];
|
|
3572
3651
|
'alwaysSelectFields.list'?: string[];
|
|
3573
3652
|
'alwaysSelectFields.create'?: string[];
|