@web-ts-toolkit/access-router 0.48.0 → 0.49.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 +507 -0
- package/advanced.d.mts +49 -13
- package/advanced.d.ts +49 -13
- package/advanced.js +394 -12
- package/advanced.mjs +396 -13
- package/index.d.mts +134 -45
- package/index.d.ts +134 -45
- package/index.js +8707 -4826
- package/index.mjs +8705 -4814
- package/llms.txt +136 -1
- package/package.json +3 -3
- package/{parsers-D6iK8khR.d.mts → parsers-DhUuvj9B.d.mts} +810 -54
- package/{parsers-D6iK8khR.d.ts → parsers-DhUuvj9B.d.ts} +810 -54
|
@@ -24,9 +24,128 @@ declare class Permission {
|
|
|
24
24
|
type Permissions = Permission;
|
|
25
25
|
type AccessRouterPermissions = Permission;
|
|
26
26
|
|
|
27
|
-
declare class
|
|
27
|
+
declare class RequestConcurrencyScheduler {
|
|
28
|
+
readonly limit: number;
|
|
29
|
+
private activeWork;
|
|
30
|
+
private readonly waiters;
|
|
31
|
+
constructor(limit: number);
|
|
32
|
+
map<TInput, TOutput>(items: TInput[], iteratee: (item: TInput, index: number, scheduled: true) => Promise<TOutput>): Promise<TOutput[]>;
|
|
33
|
+
run<TOutput>(iteratee: (scheduled: true) => Promise<TOutput>): Promise<TOutput>;
|
|
34
|
+
work<TOutput>(operation: () => TOutput | PromiseLike<TOutput>): Promise<TOutput>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Shared hook-orchestration gate for VIRT-03 finalization (VIRT-00A D7).
|
|
38
|
+
*
|
|
39
|
+
* Distinct from {@link RequestConcurrencyScheduler.work} (leaf persistence
|
|
40
|
+
* permits): this gate admits virtual getter bodies only, never persistence
|
|
41
|
+
* ops. A single gate instance is shared across all rows of a top-level list
|
|
42
|
+
* so peak active getter work stays at or below the configured limit instead
|
|
43
|
+
* of multiplying by row count. Callers hold the gate only for the getter body
|
|
44
|
+
* itself — never while awaiting descendant finalization — so limit-1
|
|
45
|
+
* configurations complete without deadlock. FIFO admission keeps completion
|
|
46
|
+
* order independent of input order; the surrounding row map preserves stable
|
|
47
|
+
* index-keyed output association regardless of completion order.
|
|
48
|
+
*/
|
|
49
|
+
declare class SharedHookGate {
|
|
50
|
+
readonly limit: number;
|
|
51
|
+
private active;
|
|
52
|
+
private readonly waiters;
|
|
53
|
+
constructor(limit: number);
|
|
54
|
+
run<TOutput>(operation: () => TOutput | PromiseLike<TOutput>): Promise<TOutput>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Internal effective data policy, including trusted custom accesses; virtual configuration keys stay narrow. */
|
|
58
|
+
type VirtualProjectionAccess = 'list' | 'create' | 'read' | 'update' | (string & {});
|
|
59
|
+
type VirtualCandidateAuthState = 'granted' | 'deferred';
|
|
60
|
+
interface VirtualProjectionSnapshot {
|
|
61
|
+
permissionSchema?: Record<string, unknown> | null;
|
|
62
|
+
virtuals?: Record<string, unknown> | null;
|
|
63
|
+
/** Raw `alwaysSelectFields` option: array or per-access record. */
|
|
64
|
+
alwaysSelectFields?: string[] | Record<string, string[]> | null;
|
|
65
|
+
modelPermissionPrefix?: string;
|
|
66
|
+
requireExplicitSelect?: boolean;
|
|
67
|
+
documentPermissionField?: string;
|
|
68
|
+
}
|
|
69
|
+
interface PlanVirtualCandidate {
|
|
70
|
+
name: string;
|
|
71
|
+
descriptor: {
|
|
72
|
+
get: (...args: never[]) => unknown;
|
|
73
|
+
dependsOn: string[];
|
|
74
|
+
};
|
|
75
|
+
authState: VirtualCandidateAuthState;
|
|
76
|
+
dependsOn: string[];
|
|
77
|
+
virtualAccess: VirtualProjectionAccess;
|
|
78
|
+
outputAccess: VirtualProjectionAccess;
|
|
79
|
+
}
|
|
80
|
+
interface VirtualProjectionPlan {
|
|
81
|
+
receivingModelName: string;
|
|
82
|
+
scopePath: string[];
|
|
83
|
+
virtualAccess: VirtualProjectionAccess;
|
|
84
|
+
outputAccess: VirtualProjectionAccess;
|
|
85
|
+
docPermissionsAccess: VirtualProjectionAccess;
|
|
86
|
+
selectionMode: 'all' | 'include' | 'exclude';
|
|
87
|
+
/** Normalized effective select (empty = omitted/effectively empty). */
|
|
88
|
+
effectiveSelectNorm: string[];
|
|
89
|
+
/** Persisted output eligibility + virtual names eligible for output. */
|
|
90
|
+
outputSelection: string[];
|
|
91
|
+
outputPersistedFields: string[];
|
|
92
|
+
outputVirtualNames: string[];
|
|
93
|
+
/** Safe Mongoose projection: no virtuals, no `.sub` paths, no `-_id`. */
|
|
94
|
+
persistedFetchSelection: string[];
|
|
95
|
+
candidates: PlanVirtualCandidate[];
|
|
96
|
+
virtualOnlyDeps: string[];
|
|
97
|
+
internalOnlyFields: string[];
|
|
98
|
+
depProvenance: Record<string, string[]>;
|
|
99
|
+
outputIdExcluded: boolean;
|
|
100
|
+
fetchIdRetained: boolean;
|
|
101
|
+
internalIdentityFields: string[];
|
|
102
|
+
internalJoinFields: string[];
|
|
103
|
+
childPlans: Record<string, VirtualProjectionPlan>;
|
|
104
|
+
relatedPlans: VirtualProjectionPlan[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type VirtualFinalizeAccess = VirtualProjectionAccess;
|
|
108
|
+
interface FinalizeSnapshot extends VirtualProjectionSnapshot {
|
|
109
|
+
documentPermissionField?: string;
|
|
110
|
+
exposedDocPermissionKeys?: string[] | undefined;
|
|
111
|
+
stripPermissionsField?: boolean;
|
|
112
|
+
disableFieldPermissions?: boolean;
|
|
113
|
+
}
|
|
114
|
+
interface FinalizeRelatedTarget {
|
|
115
|
+
/** Output path in the parent scope (top-level name for v1; dotted supported). */
|
|
116
|
+
path: string;
|
|
117
|
+
plan: VirtualProjectionPlan;
|
|
118
|
+
snapshot: FinalizeSnapshot;
|
|
119
|
+
virtualAccess: VirtualFinalizeAccess;
|
|
120
|
+
outputAccess: VirtualFinalizeAccess;
|
|
121
|
+
docPermissionsAccess: VirtualFinalizeAccess;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* VIRT-04 direct-operation virtual wiring (VIRT-00A D1/D6/D7/D8).
|
|
126
|
+
*
|
|
127
|
+
* - Access matrix: list `list/list/list`; read/readFilter `read/read/read`
|
|
128
|
+
* (read using list fallback becomes `list/list/list` with
|
|
129
|
+
* `operation: 'read'` preserved); create/create-branch `create/read/create`;
|
|
130
|
+
* update/update-branch `update/read/update`; new template
|
|
131
|
+
* `create/create/create` (see `new()` note on ignored `args.select`).
|
|
132
|
+
* - Effective mutation output selection is carried on the internal plan
|
|
133
|
+
* into the shared finalizer before evaluation (explicit select,
|
|
134
|
+
* `returningAll: false` implicits, decorator/task selection preserved
|
|
135
|
+
* via existing public picks after decorate/tasks). Internal create/update
|
|
136
|
+
* args omit `select`; `overrides.effectiveSelect` is the deliberate
|
|
137
|
+
* transport (see `interfaces/service-create.ts`, `service-update.ts`).
|
|
138
|
+
* - Output-only admission runs before validation/persistence; computed
|
|
139
|
+
* values are assigned only to output copies (finalizer), never to
|
|
140
|
+
* persisted docs/snapshots.
|
|
141
|
+
* - `maxHookConcurrency` bounds active getter + row-finalization work via
|
|
142
|
+
* ONE bounded row map + shared `SharedHookGate` (stable order, limit-1
|
|
143
|
+
* completes, no persistence permits held).
|
|
144
|
+
*/
|
|
145
|
+
type VirtualOperationAccess = 'list' | 'create' | 'read' | 'update';
|
|
146
|
+
declare class Service<TModel = unknown, TVirtuals extends object = Record<never, never>> extends Base<TModel, TVirtuals> {
|
|
28
147
|
protected model: any;
|
|
29
|
-
protected options: ModelRouterOptions<TModel>;
|
|
148
|
+
protected options: ModelRouterOptions<TModel, TVirtuals>;
|
|
30
149
|
defaults: Defaults<TModel>;
|
|
31
150
|
protected baseFields: string[];
|
|
32
151
|
protected baseFieldsExt: string[];
|
|
@@ -38,7 +157,7 @@ declare class Service<TModel = unknown> extends Base<TModel> {
|
|
|
38
157
|
lean: boolean;
|
|
39
158
|
}): any;
|
|
40
159
|
protected createModelAdapter(modelName: string): any;
|
|
41
|
-
protected getModelRouterOptions(modelName: string): ModelRouterOptions<TModel>;
|
|
160
|
+
protected getModelRouterOptions(modelName: string): ModelRouterOptions<TModel, TVirtuals>;
|
|
42
161
|
/**
|
|
43
162
|
* Router-level permission output postures (see `ModelRouterOptions`).
|
|
44
163
|
* Absolute: they win over client input and per-operation `defaults`.
|
|
@@ -61,6 +180,164 @@ declare class Service<TModel = unknown> extends Base<TModel> {
|
|
|
61
180
|
* through untouched.
|
|
62
181
|
*/
|
|
63
182
|
protected resolveEffectiveSelect(select: Projection | undefined): Projection | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* Capture a coherent virtual snapshot for an operation (VIRT-00A D1/D2,
|
|
185
|
+
* VIRT-01 copy-on-write). Planner and finalizer use this same object for
|
|
186
|
+
* the operation so in-flight descriptor replacement cannot mix versions.
|
|
187
|
+
* Public for VIRT-06 subdocument routes (same coherence per sub op).
|
|
188
|
+
*/
|
|
189
|
+
captureVirtualSnapshot(): FinalizeSnapshot;
|
|
190
|
+
getVirtualGlobalPermissions(): {
|
|
191
|
+
has: (key: string) => boolean;
|
|
192
|
+
hasKey: (key: string) => boolean;
|
|
193
|
+
};
|
|
194
|
+
getVirtualHookConcurrencyLimit(): number;
|
|
195
|
+
buildVirtualPlan(args: {
|
|
196
|
+
snapshot: FinalizeSnapshot;
|
|
197
|
+
virtualAccess: VirtualOperationAccess;
|
|
198
|
+
outputAccess: VirtualOperationAccess;
|
|
199
|
+
docPermissionsAccess: VirtualOperationAccess;
|
|
200
|
+
requestedSelect?: Projection;
|
|
201
|
+
effectiveSelect?: Projection;
|
|
202
|
+
internalFetch?: {
|
|
203
|
+
baseFields?: string[];
|
|
204
|
+
joinFields?: string[];
|
|
205
|
+
identityFields?: string[];
|
|
206
|
+
extraPersistedFields?: string[];
|
|
207
|
+
trustedOverrideSelect?: Projection;
|
|
208
|
+
};
|
|
209
|
+
}): VirtualProjectionPlan;
|
|
210
|
+
/**
|
|
211
|
+
* VIRT-06 sub-scope planner.
|
|
212
|
+
*
|
|
213
|
+
* Builds a `planVirtualProjection` with `scopePath=[sub,'sub']` for
|
|
214
|
+
* dedicated subdocument routes (`virtuals.<field>.sub.<name>.<access>`).
|
|
215
|
+
* `dependsOn` resolves relative to that scope (VIRT-00A D5); literal
|
|
216
|
+
* `.sub` segments never become DB/response names (planner strips them).
|
|
217
|
+
* Uses the same coherent `snapshot` captured once per sub op.
|
|
218
|
+
*/
|
|
219
|
+
buildSubVirtualPlan(sub: string, snapshot: FinalizeSnapshot, virtualAccess: VirtualOperationAccess, outputAccess: VirtualOperationAccess, docPermissionsAccess: VirtualOperationAccess, requestedSelect?: Projection, effectiveSelect?: Projection): VirtualProjectionPlan;
|
|
220
|
+
/**
|
|
221
|
+
* VIRT-06 internal accessors for dedicated subdocument routes.
|
|
222
|
+
* `model-subdocument-service` uses these instead of reaching into
|
|
223
|
+
* protected `req`/`modelName`/`model`, preserving raw docs for
|
|
224
|
+
* persistence (never substituting trimmed DTOs) and keeping request
|
|
225
|
+
* ownership inside `Service`.
|
|
226
|
+
*/
|
|
227
|
+
getServiceRequest(): ModelRequest;
|
|
228
|
+
getServiceModelName(): string;
|
|
229
|
+
getServiceMongooseModel(): unknown;
|
|
230
|
+
/**
|
|
231
|
+
* VIRT-06 owning-parent internal grants (VIRT-00A D2).
|
|
232
|
+
*
|
|
233
|
+
* Dedicated subdocument routes reuse the owning parent's internally
|
|
234
|
+
* computed `read` grants with scoped `permissionSchema.<field>.sub` rules;
|
|
235
|
+
* v1 adds NO independent embedded `docPermissions` hook family. `operation`
|
|
236
|
+
* stays the initiating sub op (`subList`/`subRead`/`subCreate`/`subUpdate`/
|
|
237
|
+
* `subBulkUpdate`) for logs, while `docPermissionsAccess` is `read`.
|
|
238
|
+
* Never throws: hook failure yields `{}` (fail-closed downstream).
|
|
239
|
+
*/
|
|
240
|
+
getSubParentGrants(parentDoc: unknown, operation: string): Promise<Record<string, unknown>>;
|
|
241
|
+
/**
|
|
242
|
+
* VIRT-06 bounded sub-row finalization (VIRT-00A D7, VIRT-03 strategy).
|
|
243
|
+
*
|
|
244
|
+
* Finalizes already-filtered (visible-only) sub rows through the shared
|
|
245
|
+
* boundary with scope ` [sub,'sub']`, owning-parent `read` grants, and
|
|
246
|
+
* scoped rules. Children (deeper nested + related via `related`) finalize
|
|
247
|
+
* before sub getters; only finalized children appear in getter views.
|
|
248
|
+
* Uses ONE bounded row map + shared `SharedHookGate` (stable order,
|
|
249
|
+
* peak ≤ `maxHookConcurrency`, limit-1 completes, no persistence permits
|
|
250
|
+
* held). Callers must filter rows (existing row/operation filters) BEFORE
|
|
251
|
+
* calling; hidden rows never reach getters. Never mutates inputs.
|
|
252
|
+
*/
|
|
253
|
+
finalizeSubRowsWithVirtuals(args: {
|
|
254
|
+
sub: string;
|
|
255
|
+
rows: unknown[];
|
|
256
|
+
plan: VirtualProjectionPlan;
|
|
257
|
+
snapshot: FinalizeSnapshot;
|
|
258
|
+
parentGrants: Record<string, unknown>;
|
|
259
|
+
virtualAccess: VirtualOperationAccess;
|
|
260
|
+
outputAccess: VirtualOperationAccess;
|
|
261
|
+
docPermissionsAccess: VirtualOperationAccess;
|
|
262
|
+
operation: string;
|
|
263
|
+
related?: FinalizeRelatedTarget[];
|
|
264
|
+
}): Promise<Record<string, unknown>[]>;
|
|
265
|
+
/**
|
|
266
|
+
* VIRT-06 single sub-row finalization (same boundary/bounds as list).
|
|
267
|
+
* Returns `null` when input is nullish (caller maps to `null` for
|
|
268
|
+
* denied-post-persist single visibility, never a retryable error).
|
|
269
|
+
*/
|
|
270
|
+
finalizeSingleSubRowWithVirtuals(args: {
|
|
271
|
+
sub: string;
|
|
272
|
+
row: unknown;
|
|
273
|
+
plan: VirtualProjectionPlan;
|
|
274
|
+
snapshot: FinalizeSnapshot;
|
|
275
|
+
parentGrants: Record<string, unknown>;
|
|
276
|
+
virtualAccess: VirtualOperationAccess;
|
|
277
|
+
outputAccess: VirtualOperationAccess;
|
|
278
|
+
docPermissionsAccess: VirtualOperationAccess;
|
|
279
|
+
operation: string;
|
|
280
|
+
related?: FinalizeRelatedTarget[];
|
|
281
|
+
}): Promise<Record<string, unknown> | null>;
|
|
282
|
+
/**
|
|
283
|
+
* VIRT-06 populate finalization for sub parents (VIRT-05 path).
|
|
284
|
+
*
|
|
285
|
+
* After Mongoose `populate` resolves on the raw parent (e.g.
|
|
286
|
+
* `contacts.friend` via `genSubPopulate`), finalize each actual target
|
|
287
|
+
* with its own plan/context before sub getters. Handles null/scalar/array
|
|
288
|
+
* + dotted through embedded arrays; trims even without virtuals. Bounded
|
|
289
|
+
* via shared gate; limit-1 completes. No-op when no entries.
|
|
290
|
+
*/
|
|
291
|
+
finalizePopulateForSubParent(parentDoc: unknown, populateEntries: Populate[], parentOperation: string, hookLimit?: number, sharedGate?: SharedHookGate): Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* Output-only write admission (VIRT-04 requirement 4). Excludes registered
|
|
294
|
+
* virtual names for any access (including bare `true` rules and inactive
|
|
295
|
+
* descriptors) and sanitizes registered embedded virtual keys inside
|
|
296
|
+
* whole-object/array grants. Trusted prepare/transform authority is
|
|
297
|
+
* preserved (this runs on client data before validation only); computed
|
|
298
|
+
* values are assigned only to output copies by the finalizer.
|
|
299
|
+
*/
|
|
300
|
+
private admitWriteDataForVirtuals;
|
|
301
|
+
private filterAllowedFieldsForVirtuals;
|
|
302
|
+
private stripVirtualsFromFilterForSnapshot;
|
|
303
|
+
/**
|
|
304
|
+
* Preserve the `permissions` Mongoose virtual (from `permissionsPlugin`) for
|
|
305
|
+
* hydrated outputs. `isolateDocForOutput` uses `toObject({ virtuals: false })`
|
|
306
|
+
* so Mongoose getter virtuals never leak into package-virtual planning;
|
|
307
|
+
* `lean: true` therefore drops them (explicit parity rule). For `lean: false`
|
|
308
|
+
* (hydrated Mongoose documents) the historical contract exposes
|
|
309
|
+
* `doc.permissions` as an alias of the document-permission field. The shared
|
|
310
|
+
* finalizer keeps only the document-permission field, so restore the alias
|
|
311
|
+
* here for backward compatibility when the input was hydrated and the
|
|
312
|
+
* virtual existed. Lean outputs stay without the virtual. Models without
|
|
313
|
+
* the plugin never had the virtual and stay unchanged.
|
|
314
|
+
*/
|
|
315
|
+
private preservePermissionsVirtual;
|
|
316
|
+
/**
|
|
317
|
+
* VIRT-05 populate + association helpers.
|
|
318
|
+
*
|
|
319
|
+
* - Populate targets finalize with their own plan/context before parent
|
|
320
|
+
* getters (target trimming even without virtuals). Handles null, scalar
|
|
321
|
+
* IDs when population is skipped, arrays, and dotted paths through
|
|
322
|
+
* embedded arrays (e.g. `contacts.friend`). One evaluation per logical
|
|
323
|
+
* target output, no in-place mutation of shared/cached instances (targets
|
|
324
|
+
* are replaced, never mutated; parents are fresh per-query docs, consistent
|
|
325
|
+
* with `includeDocs` in-place attachment), isolated copies per parent,
|
|
326
|
+
* bounded via the shared `SharedHookGate` + `mapWithConcurrencyLimit`
|
|
327
|
+
* (no unbounded fan-out, limit-1 completes, no persistence permits held).
|
|
328
|
+
* - Association transport stays service-only (symbols, outside serializers
|
|
329
|
+
* and finalized output input): `find` fetches `foreignField` fetch-only,
|
|
330
|
+
* captures immutable values before trim, and returns them aligned to rows.
|
|
331
|
+
*/
|
|
332
|
+
private getAssociationFieldFromOptions;
|
|
333
|
+
private buildDbSelectForAssociation;
|
|
334
|
+
private captureAssociationValues;
|
|
335
|
+
private captureTargetSnapshotForPopulate;
|
|
336
|
+
private resolvePopulateMeta;
|
|
337
|
+
private isScalarPopulateValue;
|
|
338
|
+
private finalizeSinglePopulateTarget;
|
|
339
|
+
private finalizePopulatePath;
|
|
340
|
+
private finalizePopulateForSingleDoc;
|
|
64
341
|
private beginOp;
|
|
65
342
|
private completeOp;
|
|
66
343
|
constructor(req: ModelRequest, modelName: string);
|
|
@@ -164,44 +441,52 @@ declare class Service<TModel = unknown> extends Base<TModel> {
|
|
|
164
441
|
getParentDoc(id: SubdocumentId, sub: SubdocumentName, args?: SubdocumentParentArgs, options?: SubdocumentParentOptions): Promise<any>;
|
|
165
442
|
}
|
|
166
443
|
|
|
167
|
-
declare class PublicService<TModel = unknown
|
|
444
|
+
declare class PublicService<TModel = unknown, TVirtuals extends object = Record<never, never>> extends Service<TModel, TVirtuals> {
|
|
168
445
|
_list<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(filter: Filter<TModel>, args?: Omit<PublicListArgs, 'select' | 'populate'> & {
|
|
169
446
|
select?: TSelect;
|
|
170
447
|
populate?: TPopulate;
|
|
171
|
-
}, options?: PublicListOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
448
|
+
}, options?: PublicListOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
172
449
|
_create<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(data: unknown, args?: Omit<PublicCreateArgs, 'select' | 'populate'> & {
|
|
173
450
|
select?: TSelect;
|
|
174
451
|
populate?: TPopulate;
|
|
175
|
-
}, options?: PublicCreateOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
452
|
+
}, options?: PublicCreateOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
176
453
|
_new(args?: {
|
|
177
454
|
select?: string[];
|
|
178
455
|
}, options?: {
|
|
179
456
|
skim?: boolean;
|
|
180
457
|
includePermissions?: boolean;
|
|
181
458
|
includeFieldPermissions?: boolean;
|
|
182
|
-
}): Promise<SingleResult<PublicOutput<TModel>>>;
|
|
459
|
+
}): Promise<SingleResult<PublicOutput<TModel, TVirtuals>>>;
|
|
183
460
|
_read<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(id: string, args?: Omit<PublicReadArgs, 'select' | 'populate'> & {
|
|
184
461
|
select?: TSelect;
|
|
185
462
|
populate?: TPopulate;
|
|
186
|
-
}, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
463
|
+
}, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
187
464
|
_readFilter<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(filter: Filter<TModel>, args?: Omit<PublicReadArgs, 'select' | 'populate'> & {
|
|
188
465
|
select?: TSelect;
|
|
189
466
|
populate?: TPopulate;
|
|
190
467
|
sort?: Sort;
|
|
191
|
-
}, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
468
|
+
}, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
192
469
|
_update<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(id: string, data: Record<string, unknown>, args?: Omit<PublicUpdateArgs, 'select' | 'populate'> & {
|
|
193
470
|
select?: TSelect;
|
|
194
471
|
populate?: TPopulate;
|
|
195
|
-
}, options?: PublicUpdateOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
472
|
+
}, options?: PublicUpdateOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
196
473
|
/**
|
|
197
474
|
* With an _id, check update-policy existence then update: terminal denial returns
|
|
198
475
|
* Forbidden, an allowed miss remains Unauthorized, and neither creates a row.
|
|
199
476
|
* Without an _id, use the create path.
|
|
477
|
+
*
|
|
478
|
+
* VIRT-04 D1/D6: both branches keep `operation: 'upsert'` while accesses
|
|
479
|
+
* follow the taken branch (update/read/update vs create/read/create).
|
|
480
|
+
* Effective selection is carried via internal `overrides.effectiveSelect`
|
|
481
|
+
* before evaluation; presentation picks after decorate/tasks are preserved.
|
|
482
|
+
* Internal `updateById`/`create` are called directly (not via `_update`/
|
|
483
|
+
* `_create`) so the initiating operation is not overwritten with the
|
|
484
|
+
* branch name.
|
|
200
485
|
*/
|
|
201
486
|
_upsert<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(data: Record<string, unknown>, args?: Omit<PublicUpsertArgs, 'select' | 'populate'> & {
|
|
202
487
|
select?: TSelect;
|
|
203
488
|
populate?: TPopulate;
|
|
204
|
-
}, options?: PublicUpsertOptions): Promise<ServiceResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
|
|
489
|
+
}, options?: PublicUpsertOptions): Promise<ServiceResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
|
|
205
490
|
_delete(id: string): Promise<SingleResult<unknown> | ErrorResult>;
|
|
206
491
|
_distinct(field: string, options?: DistinctArgs<TModel>): Promise<ListResult<unknown> | ErrorResult>;
|
|
207
492
|
_count(filter: Filter<TModel>, access?: BaseFilterAccess): Promise<SingleResult<number> | ErrorResult>;
|
|
@@ -460,7 +745,7 @@ type AdvancedListBody = {
|
|
|
460
745
|
includeFieldPermissions?: boolean;
|
|
461
746
|
includeCount?: boolean;
|
|
462
747
|
includeExtraHeaders?: boolean;
|
|
463
|
-
populateAccess?:
|
|
748
|
+
populateAccess?: PopulateAccess;
|
|
464
749
|
};
|
|
465
750
|
};
|
|
466
751
|
type CountBody = {
|
|
@@ -478,7 +763,7 @@ type AdvancedReadFilterBody = {
|
|
|
478
763
|
includePermissions?: boolean;
|
|
479
764
|
includeFieldPermissions?: boolean;
|
|
480
765
|
tryList?: boolean;
|
|
481
|
-
populateAccess?:
|
|
766
|
+
populateAccess?: PopulateAccess;
|
|
482
767
|
};
|
|
483
768
|
};
|
|
484
769
|
type AdvancedReadBody = {
|
|
@@ -491,7 +776,7 @@ type AdvancedReadBody = {
|
|
|
491
776
|
includePermissions?: boolean;
|
|
492
777
|
includeFieldPermissions?: boolean;
|
|
493
778
|
tryList?: boolean;
|
|
494
|
-
populateAccess?:
|
|
779
|
+
populateAccess?: PopulateAccess;
|
|
495
780
|
};
|
|
496
781
|
};
|
|
497
782
|
type AdvancedCreateBody = {
|
|
@@ -502,7 +787,7 @@ type AdvancedCreateBody = {
|
|
|
502
787
|
options?: {
|
|
503
788
|
includePermissions?: boolean;
|
|
504
789
|
includeFieldPermissions?: boolean;
|
|
505
|
-
populateAccess?:
|
|
790
|
+
populateAccess?: PopulateAccess;
|
|
506
791
|
};
|
|
507
792
|
};
|
|
508
793
|
type AdvancedUpdateBody = {
|
|
@@ -514,7 +799,7 @@ type AdvancedUpdateBody = {
|
|
|
514
799
|
returningAll?: boolean;
|
|
515
800
|
includePermissions?: boolean;
|
|
516
801
|
includeFieldPermissions?: boolean;
|
|
517
|
-
populateAccess?:
|
|
802
|
+
populateAccess?: PopulateAccess;
|
|
518
803
|
};
|
|
519
804
|
};
|
|
520
805
|
type AdvancedUpsertBody = {
|
|
@@ -526,7 +811,7 @@ type AdvancedUpsertBody = {
|
|
|
526
811
|
returningAll?: boolean;
|
|
527
812
|
includePermissions?: boolean;
|
|
528
813
|
includeFieldPermissions?: boolean;
|
|
529
|
-
populateAccess?:
|
|
814
|
+
populateAccess?: PopulateAccess;
|
|
530
815
|
};
|
|
531
816
|
};
|
|
532
817
|
type DistinctBody = {
|
|
@@ -558,6 +843,23 @@ interface RequestComplexityOptions {
|
|
|
558
843
|
* Not a process-wide connection limit or protection against competing edits.
|
|
559
844
|
*/
|
|
560
845
|
maxBulkConcurrency?: number;
|
|
846
|
+
/**
|
|
847
|
+
* Defaults to 10. Per-request/per-runtime ceiling on concurrently awaited
|
|
848
|
+
* virtual-getter + row-finalization orchestration work (VIRT-00A D7), NOT a
|
|
849
|
+
* per-row multiplier and NOT leaf persistence admission. Top-level lists
|
|
850
|
+
* finalize rows through ONE bounded map with a shared hook gate held only
|
|
851
|
+
* for getter bodies (stable index-keyed order regardless of completion
|
|
852
|
+
* order), so peak active getter work stays at or below this limit — never
|
|
853
|
+
* `rows × getters`. Nested include/populate/embedded work uses bounded
|
|
854
|
+
* child maps with a finite bound derived from the same limit; recursion
|
|
855
|
+
* depth is additionally bounded by `maxCorrelatedDepth` and the correlated
|
|
856
|
+
* budgets. Recursive orchestration never holds leaf persistence permits
|
|
857
|
+
* (`RequestConcurrencyScheduler.work`, which admits only leaf
|
|
858
|
+
* adapter/document persistence ops) while awaiting descendants or hooks,
|
|
859
|
+
* so limit-1 configurations complete. Trusted direct DB/network I/O issued
|
|
860
|
+
* by virtual getters themselves runs outside the leaf persistence ceiling
|
|
861
|
+
* (no dataloader/batching in v1). Not a process-wide connection limit.
|
|
862
|
+
*/
|
|
561
863
|
maxHookConcurrency?: number;
|
|
562
864
|
/** Cumulative correlated target executions per request/runtime (default 100); permit release does not refund this budget. */
|
|
563
865
|
maxCorrelatedQueries?: number;
|
|
@@ -732,21 +1034,49 @@ declare class AccessRuntime {
|
|
|
732
1034
|
getDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions<unknown>[K];
|
|
733
1035
|
private createModelOptions;
|
|
734
1036
|
private getOrCreateModelOptions;
|
|
735
|
-
setModelOptions<TModel = unknown
|
|
736
|
-
setModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown
|
|
737
|
-
getModelOptions<TModel = unknown
|
|
738
|
-
getModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown
|
|
739
|
-
getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown
|
|
1037
|
+
setModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, options: ModelRouterOptions<TModel, TVirtuals>): void;
|
|
1038
|
+
setModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K, value: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): void;
|
|
1039
|
+
getModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): ModelRouterOptions<TModel, TVirtuals>;
|
|
1040
|
+
getModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
1041
|
+
getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
1042
|
+
/**
|
|
1043
|
+
* List registered virtual leaf names for a scope (VIRT-01).
|
|
1044
|
+
* Scope containers (`{ sub }`) are not leaves. Inapplicable names are
|
|
1045
|
+
* still listed here; applicability is decided per-access by
|
|
1046
|
+
* {@link resolveVirtualDescriptor}. Request-controlled include collisions
|
|
1047
|
+
* are validated by VIRT-05, not guessed here.
|
|
1048
|
+
*/
|
|
1049
|
+
getVirtualNames(modelName: string, scopePath?: string[]): string[];
|
|
1050
|
+
/**
|
|
1051
|
+
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
1052
|
+
* Inapplicable registered names stay virtual (never persisted), even when
|
|
1053
|
+
* no getter applies for the current access. Containers and persisted
|
|
1054
|
+
* fields return false.
|
|
1055
|
+
*/
|
|
1056
|
+
isVirtualField(modelName: string, fieldName: string, scopePath?: string[]): boolean;
|
|
1057
|
+
/**
|
|
1058
|
+
* Resolve an applicable virtual descriptor with exact → `.default` → bare
|
|
1059
|
+
* semantics (VIRT-01, VIRT-00A D1/D5). Returns `undefined` when no getter
|
|
1060
|
+
* applies for `access`. Crucially, an access-record object is NEVER
|
|
1061
|
+
* interpreted as a getter descriptor when the access is absent: an
|
|
1062
|
+
* inapplicable registered name stays virtual (excluded from persisted
|
|
1063
|
+
* projections, write admission, and DB sort/filter/distinct).
|
|
1064
|
+
* Shared record-level `dependsOn` is inherited when a leaf lacks its own.
|
|
1065
|
+
*/
|
|
1066
|
+
resolveVirtualDescriptor(modelName: string, virtualName: string, access: string, scopePath?: string[]): {
|
|
1067
|
+
get: (...args: never[]) => unknown;
|
|
1068
|
+
dependsOn: string[];
|
|
1069
|
+
} | undefined;
|
|
740
1070
|
getModelNames(): string[];
|
|
741
1071
|
hasModel(modelName: string): boolean;
|
|
742
1072
|
getModelJsonSchema(modelName: string): Record<string, unknown>;
|
|
743
1073
|
private createDataOptions;
|
|
744
1074
|
private getOrCreateDataOptions;
|
|
745
1075
|
setDataOptions<TData = unknown>(dataName: string, options: DataRouterOptions<TData>): void;
|
|
746
|
-
setDataOption<K extends keyof
|
|
1076
|
+
setDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: ExtendedDataRouterOptions<TData>[K]): void;
|
|
747
1077
|
getDataOptions<TData = unknown>(dataName: string): DataRouterOptions<TData>;
|
|
748
1078
|
getDataSnapshot<TData = unknown>(dataName: string): readonly TData[];
|
|
749
|
-
getDataOption<K extends keyof
|
|
1079
|
+
getDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: ExtendedDataRouterOptions<TData>[K]): ExtendedDataRouterOptions<TData>[K];
|
|
750
1080
|
getExactDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions<TData>[K];
|
|
751
1081
|
getDataNames(): string[];
|
|
752
1082
|
ensureModelMeta(modelName: string): void;
|
|
@@ -756,22 +1086,12 @@ declare class AccessRuntime {
|
|
|
756
1086
|
}
|
|
757
1087
|
declare const defaultRuntime: AccessRuntime;
|
|
758
1088
|
|
|
759
|
-
declare class RequestConcurrencyScheduler {
|
|
760
|
-
readonly limit: number;
|
|
761
|
-
private activeWork;
|
|
762
|
-
private readonly waiters;
|
|
763
|
-
constructor(limit: number);
|
|
764
|
-
map<TInput, TOutput>(items: TInput[], iteratee: (item: TInput, index: number, scheduled: true) => Promise<TOutput>): Promise<TOutput[]>;
|
|
765
|
-
run<TOutput>(iteratee: (scheduled: true) => Promise<TOutput>): Promise<TOutput>;
|
|
766
|
-
work<TOutput>(operation: () => TOutput | PromiseLike<TOutput>): Promise<TOutput>;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
1089
|
type CrossResourceModelOperation = 'list' | 'read' | 'count';
|
|
770
1090
|
interface CorrelatedExecState {
|
|
771
1091
|
totalQueries: number;
|
|
772
1092
|
scheduler: RequestConcurrencyScheduler;
|
|
773
1093
|
}
|
|
774
|
-
declare class Base<TModel = unknown
|
|
1094
|
+
declare class Base<TModel = unknown, TVirtuals extends object = Record<never, never>> {
|
|
775
1095
|
protected req: ModelRequest;
|
|
776
1096
|
protected modelName: string;
|
|
777
1097
|
protected readonly requestRuntime: AccessRuntime;
|
|
@@ -806,7 +1126,7 @@ declare class Base<TModel = unknown> {
|
|
|
806
1126
|
getRequestComplexity(): Required<RequestComplexityOptions>;
|
|
807
1127
|
protected getClientRequestErrorResult(error: unknown): ErrorResult | null;
|
|
808
1128
|
protected throwClientRequestError(code: ErrorResult['code'], detail: string): never;
|
|
809
|
-
protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation): Promise<PublicService<unknown
|
|
1129
|
+
protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation): Promise<PublicService<unknown, Record<never, never>>>;
|
|
810
1130
|
protected processInclude(include: Include | Include[]): {
|
|
811
1131
|
includes: LegacyInclude[];
|
|
812
1132
|
correlatedIncludes: CorrelatedInclude[];
|
|
@@ -815,6 +1135,17 @@ declare class Base<TModel = unknown> {
|
|
|
815
1135
|
correlatedReferenceFields: string[];
|
|
816
1136
|
};
|
|
817
1137
|
private validateIncludeOutputPath;
|
|
1138
|
+
/**
|
|
1139
|
+
* VIRT-05 preflight: request include output paths vs registered virtual
|
|
1140
|
+
* paths with ancestor/descendant checks per receiving scope, before any
|
|
1141
|
+
* target dispatch. Registered virtual names remain virtual even when no
|
|
1142
|
+
* getter applies; an include that would overwrite a virtual (equal,
|
|
1143
|
+
* ancestor, or descendant, including bracket equivalents) fails closed
|
|
1144
|
+
* with `BadRequest`. Ordinary non-virtual collisions stay permitted and
|
|
1145
|
+
* existing permission-metadata protections above are preserved.
|
|
1146
|
+
*/
|
|
1147
|
+
private collectVirtualDbPathsForPreflight;
|
|
1148
|
+
private validateIncludeVirtualCollision;
|
|
818
1149
|
private assertIncludeOutputPaths;
|
|
819
1150
|
private getForeignKeyValues;
|
|
820
1151
|
private getForeignKey;
|
|
@@ -887,6 +1218,20 @@ declare class Core {
|
|
|
887
1218
|
beforeDelete<T>(modelName: string, doc: T, context: ModelHookContext): Promise<void>;
|
|
888
1219
|
afterDelete<T>(modelName: string, doc: T, context: ModelHookContext): Promise<void>;
|
|
889
1220
|
genDocPermissions(modelName: string, doc: unknown, access: DocPermissionsAccess, context: ModelHookContext): Promise<{}>;
|
|
1221
|
+
/**
|
|
1222
|
+
* VIRT-03 internal grant resolution (VIRT-00A D1/D2).
|
|
1223
|
+
*
|
|
1224
|
+
* Returns full internal document grants for virtual authorization without
|
|
1225
|
+
* consulting response-metadata switches (`includePermissions`,
|
|
1226
|
+
* `includeFieldPermissions`, `skim`, `stripPermissionsField`,
|
|
1227
|
+
* `exposedDocPermissionKeys`, `disableFieldPermissions`): those switches
|
|
1228
|
+
* affect serialization at the caller boundary only, never authorization
|
|
1229
|
+
* inputs. When `supplied` is a plain object it is reused as-is (no second
|
|
1230
|
+
* hook invocation for the same output-stage access); otherwise the correct
|
|
1231
|
+
* `docPermissionsAccess` hook runs once. The result populates scope-aware
|
|
1232
|
+
* `context.docPermissions` (full grants, distinct from serialized metadata).
|
|
1233
|
+
*/
|
|
1234
|
+
resolveFinalizerDocPermissions(modelName: string, doc: unknown, access: DocPermissionsAccess, context: ModelHookContext, supplied?: unknown): Promise<Record<string, unknown>>;
|
|
890
1235
|
addEmptyPermissions<T>(modelName: string, doc: T): T;
|
|
891
1236
|
addDocPermissions<T>(modelName: string, doc: T, access: DocPermissionsAccess, context: ModelHookContext): Promise<T>;
|
|
892
1237
|
addFieldPermissions<T extends {
|
|
@@ -899,10 +1244,12 @@ declare class Core {
|
|
|
899
1244
|
setPermissions(): Promise<void>;
|
|
900
1245
|
canActivate(routeGuard: Validation): Promise<boolean>;
|
|
901
1246
|
isAllowed(modelName: string, access: RouteGuardAccess | string): Promise<boolean>;
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1247
|
+
/** Authorize route entry using explicit server-owned basic/advanced metadata. */
|
|
1248
|
+
isAllowedRoute(modelName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise<boolean>;
|
|
1249
|
+
getService<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): Service<TModel, TVirtuals>;
|
|
1250
|
+
getPublicService<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): PublicService<TModel, TVirtuals>;
|
|
1251
|
+
service<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): PublicService<TModel, TVirtuals>;
|
|
1252
|
+
svc<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): PublicService<TModel, TVirtuals>;
|
|
906
1253
|
private getGlobalPermissions;
|
|
907
1254
|
}
|
|
908
1255
|
|
|
@@ -922,6 +1269,8 @@ declare class DataCore {
|
|
|
922
1269
|
setPermissions(): Promise<void>;
|
|
923
1270
|
canActivate(routeGuard: Validation): Promise<boolean>;
|
|
924
1271
|
isAllowed(dataName: string, access: RouteGuardAccess | string): Promise<boolean>;
|
|
1272
|
+
/** Authorize route entry using explicit server-owned basic/advanced metadata. */
|
|
1273
|
+
isAllowedRoute(dataName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise<boolean>;
|
|
925
1274
|
getService<TData = unknown>(dataName: string): DataService<TData>;
|
|
926
1275
|
service<TData = unknown>(dataName: string): DataService<TData>;
|
|
927
1276
|
svc<TData = unknown>(dataName: string): DataService<TData>;
|
|
@@ -982,6 +1331,24 @@ interface PublicCreateArgs {
|
|
|
982
1331
|
tasks?: Task | Task[];
|
|
983
1332
|
}
|
|
984
1333
|
interface CreateArgs extends Omit<PublicCreateArgs, 'select' | 'tasks'> {
|
|
1334
|
+
overrides?: {
|
|
1335
|
+
populate?: Populate[] | string;
|
|
1336
|
+
/**
|
|
1337
|
+
* VIRT-04 internal plan transport (VIRT-00A D6).
|
|
1338
|
+
* Effective mutation output selection carried into internal finalization
|
|
1339
|
+
* before virtual evaluation. Internal create args omit `select` publicly;
|
|
1340
|
+
* this trusted transport supplies the effective selection (explicit
|
|
1341
|
+
* `select` or `undefined` for all) without changing public presentation
|
|
1342
|
+
* (public `_create` still picks after decorate/tasks).
|
|
1343
|
+
*/
|
|
1344
|
+
effectiveSelect?: Projection;
|
|
1345
|
+
/**
|
|
1346
|
+
* VIRT-04 initiating operation (VIRT-00A D1). Upsert branches keep
|
|
1347
|
+
* `operation: 'upsert'` while virtual/output/doc accesses follow the
|
|
1348
|
+
* taken branch. Defaults to `'create'` when absent.
|
|
1349
|
+
*/
|
|
1350
|
+
operation?: string;
|
|
1351
|
+
};
|
|
985
1352
|
}
|
|
986
1353
|
interface PublicCreateOptions {
|
|
987
1354
|
skim?: boolean;
|
|
@@ -1064,13 +1431,34 @@ interface UpdateOneArgs<T = unknown> extends Omit<PublicUpdateArgs, 'select' | '
|
|
|
1064
1431
|
/** Trusted replacement: nullish generates row policy; false returns Forbidden without filter generation or persistence. */
|
|
1065
1432
|
filter?: Filter<T>;
|
|
1066
1433
|
populate?: Populate[] | string;
|
|
1434
|
+
/**
|
|
1435
|
+
* VIRT-04 internal plan transport (VIRT-00A D6).
|
|
1436
|
+
* Effective mutation output selection carried into internal finalization
|
|
1437
|
+
* before virtual evaluation (explicit `select`, `returningAll: false`
|
|
1438
|
+
* implicit `Object.keys(data)+_id`, or `undefined` for all). Public
|
|
1439
|
+
* `_update` still enforces presentation pick after decorate/tasks.
|
|
1440
|
+
*/
|
|
1441
|
+
effectiveSelect?: Projection;
|
|
1442
|
+
/**
|
|
1443
|
+
* VIRT-04 initiating operation (VIRT-00A D1). Upsert branches keep
|
|
1444
|
+
* `operation: 'upsert'` while accesses follow the taken branch.
|
|
1445
|
+
* Defaults to `'update'` when absent.
|
|
1446
|
+
*/
|
|
1447
|
+
operation?: string;
|
|
1067
1448
|
};
|
|
1068
1449
|
}
|
|
1069
|
-
interface UpdateByIdArgs<T = unknown> extends Omit<
|
|
1450
|
+
interface UpdateByIdArgs<T = unknown> extends Omit<PublicUpdateArgs, 'select' | 'tasks'> {
|
|
1070
1451
|
overrides?: {
|
|
1071
1452
|
populate?: Populate[] | string;
|
|
1072
1453
|
/** Nullish resolves the identifier; false denies without ID/filter generation or persistence. Objects still receive row policy. */
|
|
1073
1454
|
idFilter?: Filter<T>;
|
|
1455
|
+
/**
|
|
1456
|
+
* VIRT-04 internal plan transport (VIRT-00A D6): effective output
|
|
1457
|
+
* selection for virtual planning before decorate/tasks.
|
|
1458
|
+
*/
|
|
1459
|
+
effectiveSelect?: Projection;
|
|
1460
|
+
/** VIRT-04 initiating operation (VIRT-00A D1); defaults to `'update'`. */
|
|
1461
|
+
operation?: string;
|
|
1074
1462
|
};
|
|
1075
1463
|
}
|
|
1076
1464
|
interface UpsertArgs<T = unknown> extends UpdateOneArgs<T> {
|
|
@@ -1137,7 +1525,13 @@ type WithDefaultId<T> = T extends object ? T & {
|
|
|
1137
1525
|
} : {
|
|
1138
1526
|
_id?: unknown;
|
|
1139
1527
|
};
|
|
1140
|
-
|
|
1528
|
+
/**
|
|
1529
|
+
* Public output with optional computed virtual shape (VIRT-01, VIRT-00A D3).
|
|
1530
|
+
* Virtual names are NEVER added to persisted `Filter`, sort/distinct inputs,
|
|
1531
|
+
* or client write types — only selected output shape gains optional virtuals.
|
|
1532
|
+
*/
|
|
1533
|
+
type WithVirtuals<TModel, TVirtuals extends object = Record<never, never>> = TModel & Partial<TVirtuals>;
|
|
1534
|
+
type PublicOutput<T, TVirtuals extends object = Record<never, never>> = WithVirtuals<T, TVirtuals> extends object ? WithVirtuals<T, TVirtuals> & Record<string, unknown> : Record<string, unknown>;
|
|
1141
1535
|
type ModelDocument<TModel = unknown> = mongoose.Document & TModel;
|
|
1142
1536
|
type TrimLeft<S extends string> = S extends ` ${infer Rest}` ? TrimLeft<Rest> : S;
|
|
1143
1537
|
type TrimRight<S extends string> = S extends `${infer Rest} ` ? TrimRight<Rest> : S;
|
|
@@ -1155,7 +1549,12 @@ type DeepPickSingle<T, TPath extends string> = TPath extends `${infer Head}.${in
|
|
|
1155
1549
|
[K in Head]: WithDefaultId<T>[K] extends readonly (infer U)[] ? Array<Simplify<DeepPickSingle<U, Tail>>> : WithDefaultId<T>[K] extends object ? Simplify<DeepPickSingle<WithDefaultId<T>[K], Tail>> : WithDefaultId<T>[K];
|
|
1156
1550
|
} : {} : {} : TPath extends keyof WithDefaultId<T> ? Pick<WithDefaultId<T>, TPath> : {};
|
|
1157
1551
|
type DeepPick<T, TPaths extends string> = Simplify<UnionToIntersection<DeepPickSingle<T, TPaths>>>;
|
|
1158
|
-
|
|
1552
|
+
/**
|
|
1553
|
+
* Literal-select output with optional virtuals (VIRT-01, VIRT-00A D3).
|
|
1554
|
+
* Virtual outputs stay optional (`Partial`) because authorization, absent
|
|
1555
|
+
* dependencies, `undefined`, and fail-closed errors can omit them.
|
|
1556
|
+
*/
|
|
1557
|
+
type SelectedPublicOutput<T, TProjection, TVirtuals extends object = Record<never, never>> = string extends PositiveProjectionPaths<TProjection> ? PublicOutput<T, TVirtuals> : [PositiveProjectionPaths<TProjection>] extends [never] ? PublicOutput<T, TVirtuals> : DeepPick<WithVirtuals<T, TVirtuals>, Extract<PositiveProjectionPaths<TProjection>, string>>;
|
|
1159
1558
|
type PopulateInput = Populate | string;
|
|
1160
1559
|
type PopulateInputs<TPopulate> = TPopulate extends readonly (infer U)[] ? U : TPopulate;
|
|
1161
1560
|
type PopulatePath<TPopulateEntry> = TPopulateEntry extends string ? TPopulateEntry : TPopulateEntry extends {
|
|
@@ -1172,7 +1571,7 @@ type PopulateEntryShape<T, TPath extends string, TValue> = TPath extends `${infe
|
|
|
1172
1571
|
}, TPath> : {};
|
|
1173
1572
|
type PopulateOutputShape<T, TPopulateEntry> = PopulatePath<TPopulateEntry> extends infer TPath extends string ? PopulateEntryShape<T, TPath, PopulateLeafValue<PathValue<WithDefaultId<T>, TPath>, TPopulateEntry>> : {};
|
|
1174
1573
|
type PopulateOutput<T, TPopulate> = string extends PopulatePath<PopulateInputs<TPopulate>> ? {} : [PopulatePath<PopulateInputs<TPopulate>>] extends [never] ? {} : Simplify<UnionToIntersection<PopulateOutputShape<T, Extract<PopulateInputs<TPopulate>, PopulateInput>>>>;
|
|
1175
|
-
type SelectedPopulatedPublicOutput<T, TProjection, TPopulate
|
|
1574
|
+
type SelectedPopulatedPublicOutput<T, TProjection, TPopulate, TVirtuals extends object = Record<never, never>> = Simplify<SelectedPublicOutput<T, TProjection, TVirtuals> & PopulateOutput<T, TPopulate>>;
|
|
1176
1575
|
type Unwrap<T> = NonNullable<Descend<T>>;
|
|
1177
1576
|
type DeepFieldPath<T, Depth extends number = 4> = [Depth] extends [never] ? never : Unwrap<T> extends NonTraversable ? never : {
|
|
1178
1577
|
[K in Extract<keyof Unwrap<T>, string>]: K | (NonNullable<Unwrap<T>[K]> extends NonTraversable ? never : NonNullable<Unwrap<T>[K]> extends readonly (infer U)[] ? Unwrap<U> extends NonTraversable ? never : `${K}.${DeepFieldPath<U, PrevDepth[Depth]>}` : NonNullable<Unwrap<T>[K]> extends object ? `${K}.${DeepFieldPath<NonNullable<Unwrap<T>[K]>, PrevDepth[Depth]>}` : never);
|
|
@@ -1303,7 +1702,16 @@ interface SubPopulate {
|
|
|
1303
1702
|
type GuardHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, permissions: AccessRouterPermissions) => boolean | Promise<boolean>;
|
|
1304
1703
|
type Validation = boolean | string | string[] | GuardHook;
|
|
1305
1704
|
type SelectAccess = 'list' | 'create' | 'read' | 'update' | string;
|
|
1306
|
-
|
|
1705
|
+
/** Base operations with both basic and advanced generated model routes. */
|
|
1706
|
+
type PairedRouteAccess = 'list' | 'read' | 'create' | 'update' | 'upsert' | 'count' | 'distinct';
|
|
1707
|
+
/** Existing base route guards, including unpaired operations and subdocument access. */
|
|
1708
|
+
type RouteBaseAccess = PairedRouteAccess | 'new' | 'delete' | 'subs';
|
|
1709
|
+
/** Server-owned transport variant for a paired generated route. */
|
|
1710
|
+
type RouteVariant = 'basic' | 'advanced';
|
|
1711
|
+
/** The fourteen route-only keys, or the two keys for a specified paired base operation. */
|
|
1712
|
+
type RouteVariantAccess<TAccess extends PairedRouteAccess = PairedRouteAccess> = `${RouteVariant}${Capitalize<TAccess>}`;
|
|
1713
|
+
/** Known route guards with the existing trusted custom access-string compatibility. */
|
|
1714
|
+
type RouteGuardAccess = RouteBaseAccess | RouteVariantAccess | (string & {});
|
|
1307
1715
|
type DocPermissionsAccess = 'list' | 'create' | 'read' | 'update' | string;
|
|
1308
1716
|
type BaseFilterAccess = 'list' | 'read' | 'update' | 'delete' | string;
|
|
1309
1717
|
type DecorateAccess = 'list' | 'create' | 'read' | 'update' | string;
|
|
@@ -1686,6 +2094,161 @@ type ModelHook<TValue = unknown> = HookChain<TValue, ModelHookContext, ModelRequ
|
|
|
1686
2094
|
type ModelListHook<TValue = unknown> = HookChain<TValue[], ModelHookContext, ModelRequest>;
|
|
1687
2095
|
type DataHook<TValue = unknown> = HookChain<TValue, DataHookContext, DataRequest>;
|
|
1688
2096
|
type DataListHook<TValue = unknown> = HookChain<TValue[], DataHookContext, DataRequest>;
|
|
2097
|
+
/**
|
|
2098
|
+
* Supported per-access virtual getter keys (VIRT-00A D1/D5).
|
|
2099
|
+
* `default` is a fallback access; `delete`/`distinct`/`count` are NOT
|
|
2100
|
+
* virtual accesses and must be rejected at configuration time.
|
|
2101
|
+
*/
|
|
2102
|
+
type ModelVirtualAccess = 'list' | 'create' | 'read' | 'update';
|
|
2103
|
+
type ModelVirtualRecordAccess = 'default' | ModelVirtualAccess;
|
|
2104
|
+
/**
|
|
2105
|
+
* Scope-aware virtual getter context (VIRT-00A D2, frozen).
|
|
2106
|
+
* Extends {@link ModelHookContext} with the receiving model, definition/
|
|
2107
|
+
* permission `scopePath` (e.g. `[]` at root, `['contacts','sub']` for
|
|
2108
|
+
* `contacts.sub.displayName`), and the three effective accesses. The
|
|
2109
|
+
* initiating operation stays in `context.operation` and is never
|
|
2110
|
+
* overwritten with fallback/effective visibility.
|
|
2111
|
+
*
|
|
2112
|
+
* Embedded outputs reuse the owning parent's internally computed grants with
|
|
2113
|
+
* the scope's `permissionSchema.<field>.sub` rules; dedicated subdocument
|
|
2114
|
+
* routes resolve grants from the raw parent lookup. Related (populate/
|
|
2115
|
+
* include) targets finalize with the target model's own definitions,
|
|
2116
|
+
* permissions, selection, and effective access before parent getters run.
|
|
2117
|
+
*/
|
|
2118
|
+
interface ModelVirtualContext extends ModelHookContext {
|
|
2119
|
+
receivingModelName: string;
|
|
2120
|
+
scopePath: string[];
|
|
2121
|
+
virtualAccess: ModelVirtualAccess;
|
|
2122
|
+
outputAccess: ModelVirtualAccess;
|
|
2123
|
+
docPermissionsAccess: ModelVirtualAccess;
|
|
2124
|
+
}
|
|
2125
|
+
type IsUnknownVirtual<T> = unknown extends T ? ([keyof T] extends [never] ? true : false) : false;
|
|
2126
|
+
/**
|
|
2127
|
+
* Typed isolated persisted view for a virtual getter scope (VIRT-00A D3/D4).
|
|
2128
|
+
* Known models use a partial read-only persisted view (never
|
|
2129
|
+
* `Record<string, unknown>`); unknown/untyped models keep an explicit
|
|
2130
|
+
* loose fallback.
|
|
2131
|
+
*/
|
|
2132
|
+
type ModelVirtualDoc<TModel = unknown> = IsUnknownVirtual<TModel> extends true ? Readonly<Partial<Record<string, unknown>>> : [Extract<keyof TModel, string>] extends [never] ? Readonly<Partial<Record<string, unknown>>> : Readonly<Partial<TModel>>;
|
|
2133
|
+
/**
|
|
2134
|
+
* Virtual getter signature (VIRT-00A D3/D4, frozen).
|
|
2135
|
+
* Receives an isolated plain-object snapshot of persisted dependencies +
|
|
2136
|
+
* already-finalized related/embedded outputs for its scope. Sibling virtual
|
|
2137
|
+
* values are not visible. Mutating the input cannot change response data or
|
|
2138
|
+
* lifecycle snapshots. Only the returned field value is committed;
|
|
2139
|
+
* `undefined` omits the field; throws omit fail-closed. `this` is the
|
|
2140
|
+
* model request.
|
|
2141
|
+
*/
|
|
2142
|
+
type ModelVirtualGetter<TModel = unknown, TValue = unknown> = (this: ModelRequest, doc: ModelVirtualDoc<TModel>, permissions: AccessRouterPermissions, context: ModelVirtualContext) => MaybePromise<TValue | undefined>;
|
|
2143
|
+
/**
|
|
2144
|
+
* Leaf virtual descriptor (VIRT-01): `{ get, dependsOn }`.
|
|
2145
|
+
* `dependsOn` lists top-level persisted field names relative to the
|
|
2146
|
+
* definition's scope (e.g. `['displayName']` inside `contacts.sub`, never
|
|
2147
|
+
* `['contacts.sub.displayName']`). Dotted paths and virtual-to-virtual
|
|
2148
|
+
* dependencies are rejected at configuration time.
|
|
2149
|
+
*
|
|
2150
|
+
* The getter runs after `toObject()`/lean normalization and after internal
|
|
2151
|
+
* document-permissions computation, with global permissions available and
|
|
2152
|
+
* before `decorate`/`decorateAll`/tasks. Only the returned field value is
|
|
2153
|
+
* committed via document-aware helpers: `undefined` (or a throw) omits the
|
|
2154
|
+
* field fail-closed and computed values never persist. Getters see an
|
|
2155
|
+
* isolated plain-object view for both lean and hydrated results; mutating the
|
|
2156
|
+
* input cannot change response data or lifecycle snapshots.
|
|
2157
|
+
*
|
|
2158
|
+
* @example
|
|
2159
|
+
* ```ts
|
|
2160
|
+
* import type { ModelVirtualDescriptor } from '@web-ts-toolkit/access-router';
|
|
2161
|
+
* interface User { name: string; address: string; }
|
|
2162
|
+
* const descriptor: ModelVirtualDescriptor<User, string> = {
|
|
2163
|
+
* dependsOn: ['address'],
|
|
2164
|
+
* get: async (doc) => (doc.address === undefined ? undefined : `addr:${doc.address}`),
|
|
2165
|
+
* };
|
|
2166
|
+
* ```
|
|
2167
|
+
*/
|
|
2168
|
+
interface ModelVirtualDescriptor<TModel = unknown, TValue = unknown> {
|
|
2169
|
+
get: ModelVirtualGetter<TModel, TValue>;
|
|
2170
|
+
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Per-access virtual record (VIRT-01): `default` + `list`/`create`/`read`/
|
|
2174
|
+
* `update` (NOT `delete`/`distinct`/`count`). Each present access holds a
|
|
2175
|
+
* leaf descriptor or bare getter shorthand. An optional shared `dependsOn`
|
|
2176
|
+
* supports the documented `{ dependsOn, read: getter }` shape; a leaf's own
|
|
2177
|
+
* `dependsOn` wins over the shared list during resolution.
|
|
2178
|
+
*/
|
|
2179
|
+
type ModelVirtualAccessRecord<TModel = unknown, TValue = unknown> = {
|
|
2180
|
+
default?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2181
|
+
list?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2182
|
+
create?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2183
|
+
read?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2184
|
+
update?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2185
|
+
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
2186
|
+
};
|
|
2187
|
+
/** Single virtual entry: bare getter, leaf descriptor, or per-access record. */
|
|
2188
|
+
type ModelVirtualLeaf<TModel = unknown, TValue = unknown> = ModelVirtualGetter<TModel, TValue> | ModelVirtualDescriptor<TModel, TValue> | ModelVirtualAccessRecord<TModel, TValue>;
|
|
2189
|
+
type UnwrapVirtualArray<T> = T extends readonly (infer U)[] ? U : T;
|
|
2190
|
+
/**
|
|
2191
|
+
* Persisted sub-scope model for an embedded container field (VIRT-01).
|
|
2192
|
+
* Unwraps arrays/nullable wrappers to the element/object type; unknown
|
|
2193
|
+
* fields fall back to a loose record.
|
|
2194
|
+
*/
|
|
2195
|
+
type VirtualSubModel<TModel, TField extends string> = TField extends keyof TModel ? UnwrapVirtualArray<NonNullable<TModel[TField]>> : Record<string, unknown>;
|
|
2196
|
+
type LooseVirtualLeaf = ModelVirtualGetter<Record<string, unknown>, unknown> | ModelVirtualDescriptor<Record<string, unknown>, unknown> | ModelVirtualAccessRecord<Record<string, unknown>, unknown>;
|
|
2197
|
+
type LooseVirtualSubEntry = LooseVirtualLeaf | {
|
|
2198
|
+
sub?: Record<string, LooseVirtualLeaf | {
|
|
2199
|
+
sub?: Record<string, LooseVirtualLeaf>;
|
|
2200
|
+
}>;
|
|
2201
|
+
};
|
|
2202
|
+
/**
|
|
2203
|
+
* Virtual definitions for a model scope (VIRT-01).
|
|
2204
|
+
* Root leaves come from `TVirtuals` (strict: unknown persisted getter
|
|
2205
|
+
* fields are type errors). Embedded containers mirror
|
|
2206
|
+
* `permissionSchema.<field>.sub`: any persisted field may host a `sub`
|
|
2207
|
+
* record whose leaves use the container's persisted sub-model view.
|
|
2208
|
+
* Untyped models keep an explicit loose record fallback.
|
|
2209
|
+
*
|
|
2210
|
+
* Selected virtual outputs are optional (`Partial`): authorization, absent
|
|
2211
|
+
* dependencies, `undefined`, and fail-closed errors can omit them. Virtual
|
|
2212
|
+
* names are never added to persisted `Filter`, sort/distinct inputs, or
|
|
2213
|
+
* client write types.
|
|
2214
|
+
*
|
|
2215
|
+
* @example End-to-end typed virtual (persisted `User` has no `fullAddress`):
|
|
2216
|
+
* ```ts
|
|
2217
|
+
* import acl from '@web-ts-toolkit/access-router';
|
|
2218
|
+
* import type { SelectedPublicOutput } from '@web-ts-toolkit/access-router/advanced';
|
|
2219
|
+
* interface User { name: string; address: string; }
|
|
2220
|
+
* interface UserVirtuals { fullAddress: string; }
|
|
2221
|
+
* const router = acl.createRouter<User, UserVirtuals>('User', {
|
|
2222
|
+
* permissionSchema: {
|
|
2223
|
+
* name: { read: true },
|
|
2224
|
+
* address: { read: 'canViewAddress' },
|
|
2225
|
+
* fullAddress: { read: 'canViewAddress' },
|
|
2226
|
+
* },
|
|
2227
|
+
* virtuals: {
|
|
2228
|
+
* fullAddress: {
|
|
2229
|
+
* dependsOn: ['address'],
|
|
2230
|
+
* read: async function (doc) {
|
|
2231
|
+
* if (doc.address === undefined) return undefined;
|
|
2232
|
+
* return `addr:${doc.address}`;
|
|
2233
|
+
* },
|
|
2234
|
+
* },
|
|
2235
|
+
* },
|
|
2236
|
+
* });
|
|
2237
|
+
* type Out = SelectedPublicOutput<User, ['name', 'fullAddress'], UserVirtuals>;
|
|
2238
|
+
* const out: Out = { name: 'Ada', fullAddress: 'addr:x' };
|
|
2239
|
+
* const omitted: Out = { name: 'Ada' }; // virtual is optional when denied/missing
|
|
2240
|
+
* void [router, out, omitted];
|
|
2241
|
+
* ```
|
|
2242
|
+
*/
|
|
2243
|
+
type ModelVirtuals<TModel = unknown, TVirtuals extends object = Record<never, never>> = IsUnknownVirtual<TModel> extends true ? Record<string, LooseVirtualSubEntry> : {
|
|
2244
|
+
[K in Extract<keyof TVirtuals, string>]?: ModelVirtualLeaf<TModel, TVirtuals[K]>;
|
|
2245
|
+
} & {
|
|
2246
|
+
[K in Extract<keyof TModel, string>]?: {
|
|
2247
|
+
sub?: Record<string, ModelVirtualLeaf<VirtualSubModel<TModel, K>, unknown> | {
|
|
2248
|
+
sub?: Record<string, LooseVirtualLeaf>;
|
|
2249
|
+
}>;
|
|
2250
|
+
};
|
|
2251
|
+
};
|
|
1689
2252
|
|
|
1690
2253
|
interface DefaultFindOneArgs<TModel = unknown> extends Omit<FindOneArgs<TModel>, 'overrides'> {
|
|
1691
2254
|
}
|
|
@@ -1698,7 +2261,33 @@ type NestedRequestSchema = {
|
|
|
1698
2261
|
default?: RequestSchema;
|
|
1699
2262
|
data?: RequestSchema;
|
|
1700
2263
|
};
|
|
1701
|
-
|
|
2264
|
+
/**
|
|
2265
|
+
* One field's subdocument route guards: exact variant → field base → field scalar
|
|
2266
|
+
* or closed object. Only an absent field rule falls back to top-level variant/base
|
|
2267
|
+
* guards. A defined object with no applicable key denies; nested `.default` is not
|
|
2268
|
+
* a fallback. Dynamic legacy operation names remain supported.
|
|
2269
|
+
*/
|
|
2270
|
+
interface SubOperationAccess {
|
|
2271
|
+
/** Base list guard for this field; fallback for both subdocument list variants. */
|
|
2272
|
+
list?: Validation;
|
|
2273
|
+
/** Base read guard for this field; fallback for both subdocument read variants. */
|
|
2274
|
+
read?: Validation;
|
|
2275
|
+
create?: Validation;
|
|
2276
|
+
update?: Validation;
|
|
2277
|
+
delete?: Validation;
|
|
2278
|
+
/** GET /:id/<field>; undefined inherits this field's `list` rule. */
|
|
2279
|
+
basicList?: Validation;
|
|
2280
|
+
/** POST /:id/<field>/__query; undefined inherits this field's `list` rule. */
|
|
2281
|
+
advancedList?: Validation;
|
|
2282
|
+
/** GET /:id/<field>/:subId (and HEAD); undefined inherits this field's `read` rule. */
|
|
2283
|
+
basicRead?: Validation;
|
|
2284
|
+
/** POST /:id/<field>/:subId/__query; undefined inherits this field's `read` rule. */
|
|
2285
|
+
advancedRead?: Validation;
|
|
2286
|
+
[access: string]: Validation | undefined;
|
|
2287
|
+
}
|
|
2288
|
+
/** Per-field subdocument route objects or scalar guards; field names remain dynamic. */
|
|
2289
|
+
type SubRouteGuardOptions = Record<string, Validation | SubOperationAccess>;
|
|
2290
|
+
type SubFieldPermissionOptions = Record<string, Validation | Record<string, Validation>>;
|
|
1702
2291
|
interface RequestSchemas {
|
|
1703
2292
|
create?: RequestSchema;
|
|
1704
2293
|
update?: RequestSchema;
|
|
@@ -1767,13 +2356,15 @@ interface GlobalOptions {
|
|
|
1767
2356
|
}
|
|
1768
2357
|
interface RootRouterOptions {
|
|
1769
2358
|
basePath: string;
|
|
2359
|
+
/** Guard for the batch endpoint. Entries use target base operations, never basic/advanced variant names. */
|
|
1770
2360
|
operationAccess?: Validation;
|
|
1771
2361
|
maxBatchEntries?: number;
|
|
1772
2362
|
maxOrderGroups?: number;
|
|
1773
2363
|
/** Whole root operations per order group; separate from requestComplexity.maxBulkConcurrency persistence admission. */
|
|
1774
2364
|
maxConcurrentOperations?: number;
|
|
1775
2365
|
}
|
|
1776
|
-
|
|
2366
|
+
/** Base-operation field grants used by PermissionSchema. Basic/advanced route keys do not grant fields. */
|
|
2367
|
+
interface FieldOperationAccess {
|
|
1777
2368
|
new?: Validation;
|
|
1778
2369
|
list?: Validation;
|
|
1779
2370
|
create?: Validation;
|
|
@@ -1783,9 +2374,75 @@ interface OperationAccess {
|
|
|
1783
2374
|
delete?: Validation;
|
|
1784
2375
|
distinct?: Validation;
|
|
1785
2376
|
count?: Validation;
|
|
2377
|
+
subs?: Validation | SubFieldPermissionOptions;
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* Live generated-route guards, separate from PermissionSchema field grants.
|
|
2381
|
+
* An exact basic/advanced rule replaces its base guard; only undefined inherits
|
|
2382
|
+
* the base operation's exact → `.default` → scalar-shorthand resolution. Selected
|
|
2383
|
+
* false/empty-array/false-returning guards are terminal, not ANDed with the base.
|
|
2384
|
+
* Model exact lookup includes runtime-default variants; data has no model defaults.
|
|
2385
|
+
* Denial is HTTP 401; routes remain registered in getEndpoints() and OpenAPI.
|
|
2386
|
+
* Field/row/hook, root-entry, related-target and mutation-response policies keep
|
|
2387
|
+
* their existing base operations. Paths below use the default route segments.
|
|
2388
|
+
*/
|
|
2389
|
+
interface OperationAccess extends Omit<FieldOperationAccess, 'subs'> {
|
|
2390
|
+
/** GET /new guard; unpaired, with no basic/advanced override. */
|
|
2391
|
+
new?: Validation;
|
|
2392
|
+
/** Base list guard and fallback for both list routes; also governs model read-to-list retries. */
|
|
2393
|
+
list?: Validation;
|
|
2394
|
+
/** Base create guard and fallback for both model create routes. */
|
|
2395
|
+
create?: Validation;
|
|
2396
|
+
/** Base read guard and fallback for both read variants; advancedRead covers both POST read routes. */
|
|
2397
|
+
read?: Validation;
|
|
2398
|
+
/** Base update guard and fallback for both model update routes. */
|
|
2399
|
+
update?: Validation;
|
|
2400
|
+
/** Base upsert guard and fallback for both upserts; create/update route variants do not gate its branches. */
|
|
2401
|
+
upsert?: Validation;
|
|
2402
|
+
/** DELETE /:id guard; unpaired, with no basic/advanced override. */
|
|
2403
|
+
delete?: Validation;
|
|
2404
|
+
/** Base distinct guard and fallback for GET and POST /distinct/:field. */
|
|
2405
|
+
distinct?: Validation;
|
|
2406
|
+
/** Base count guard and fallback for GET and POST /count. */
|
|
2407
|
+
count?: Validation;
|
|
2408
|
+
/** Fallback for a base operation without an exact rule; explicit base/variant guards take precedence. */
|
|
2409
|
+
default?: Validation;
|
|
2410
|
+
/** GET / (and HEAD) override for model/data list; undefined inherits `list`. */
|
|
2411
|
+
basicList?: Validation;
|
|
2412
|
+
/** POST /__query override for model/data list; undefined inherits `list`. */
|
|
2413
|
+
advancedList?: Validation;
|
|
2414
|
+
/** GET /:id (and HEAD) override for model/data read; undefined inherits `read`. */
|
|
2415
|
+
basicRead?: Validation;
|
|
2416
|
+
/** POST /__query/:id and /__query/__filter override for model/data read; undefined inherits `read`. */
|
|
2417
|
+
advancedRead?: Validation;
|
|
2418
|
+
/** POST / model create override; undefined inherits `create`. */
|
|
2419
|
+
basicCreate?: Validation;
|
|
2420
|
+
/** POST /__mutation model create override; undefined inherits `create`. */
|
|
2421
|
+
advancedCreate?: Validation;
|
|
2422
|
+
/** PATCH /:id model update override; undefined inherits `update`. */
|
|
2423
|
+
basicUpdate?: Validation;
|
|
2424
|
+
/** PATCH /__mutation/:id model update override; undefined inherits `update`. */
|
|
2425
|
+
advancedUpdate?: Validation;
|
|
2426
|
+
/** PUT / model upsert override; undefined inherits `upsert`. */
|
|
2427
|
+
basicUpsert?: Validation;
|
|
2428
|
+
/** PUT /__mutation model upsert override; undefined inherits `upsert`. */
|
|
2429
|
+
advancedUpsert?: Validation;
|
|
2430
|
+
/** GET /count model count override; undefined inherits `count`. */
|
|
2431
|
+
basicCount?: Validation;
|
|
2432
|
+
/** POST /count model filtered-count override; undefined inherits `count`. */
|
|
2433
|
+
advancedCount?: Validation;
|
|
2434
|
+
/** GET /distinct/:field model distinct override; undefined inherits `distinct`. */
|
|
2435
|
+
basicDistinct?: Validation;
|
|
2436
|
+
/** POST /distinct/:field model filtered-distinct override; undefined inherits `distinct`. */
|
|
2437
|
+
advancedDistinct?: Validation;
|
|
2438
|
+
/**
|
|
2439
|
+
* Per-field scalar or closed-object guards, with list/read variants. Legacy scalar
|
|
2440
|
+
* `subs` is accepted but is not an umbrella guard for absent fields; they use top-level rules.
|
|
2441
|
+
*/
|
|
1786
2442
|
subs?: Validation | SubRouteGuardOptions;
|
|
1787
2443
|
}
|
|
1788
|
-
type PermissionRule = Validation |
|
|
2444
|
+
type PermissionRule = Validation | FieldOperationAccess;
|
|
2445
|
+
/** Field grants by base operation. Put basic/advanced route guards in operationAccess, not field-rule objects. */
|
|
1789
2446
|
type PermissionSchema<TField extends string = string> = Partial<Record<TField, PermissionRule>>;
|
|
1790
2447
|
interface DocPermissions {
|
|
1791
2448
|
list?: ModelDocPermissionsHook;
|
|
@@ -1803,6 +2460,14 @@ interface DefaultModelRouterOptions<TModel = unknown> {
|
|
|
1803
2460
|
parentPath?: string;
|
|
1804
2461
|
queryRouteSegment?: string;
|
|
1805
2462
|
mutationRouteSegment?: string;
|
|
2463
|
+
/**
|
|
2464
|
+
* Live model route guards: scalar Validation shorthand or base/variant rules.
|
|
2465
|
+
* `{ list: true, read: true, basicRead: false }` denies only GET /:id (and HEAD)
|
|
2466
|
+
* among top-level routes, retaining both advanced read POSTs subject to ordinary
|
|
2467
|
+
* field/row policy. Subdocument reads follow their field-rule precedence.
|
|
2468
|
+
* Exact runtime-default variants precede base fallback; option objects replace
|
|
2469
|
+
* top-level keys shallowly, rather than recursively merging operationAccess.
|
|
2470
|
+
*/
|
|
1806
2471
|
operationAccess?: Validation | OperationAccess;
|
|
1807
2472
|
modelPermissionPrefix?: string;
|
|
1808
2473
|
/**
|
|
@@ -1861,17 +2526,80 @@ interface ExtendedDefaultModelRouterOptions<TModel = unknown> extends DefaultMod
|
|
|
1861
2526
|
'operationAccess.create'?: Validation;
|
|
1862
2527
|
'operationAccess.distinct'?: Validation;
|
|
1863
2528
|
'operationAccess.count'?: Validation;
|
|
1864
|
-
'operationAccess.
|
|
1865
|
-
|
|
1866
|
-
|
|
2529
|
+
'operationAccess.basicList'?: Validation;
|
|
2530
|
+
'operationAccess.advancedList'?: Validation;
|
|
2531
|
+
'operationAccess.basicRead'?: Validation;
|
|
2532
|
+
'operationAccess.advancedRead'?: Validation;
|
|
2533
|
+
'operationAccess.basicCreate'?: Validation;
|
|
2534
|
+
'operationAccess.advancedCreate'?: Validation;
|
|
2535
|
+
'operationAccess.basicUpdate'?: Validation;
|
|
2536
|
+
'operationAccess.advancedUpdate'?: Validation;
|
|
2537
|
+
'operationAccess.basicUpsert'?: Validation;
|
|
2538
|
+
'operationAccess.advancedUpsert'?: Validation;
|
|
2539
|
+
'operationAccess.basicCount'?: Validation;
|
|
2540
|
+
'operationAccess.advancedCount'?: Validation;
|
|
2541
|
+
'operationAccess.basicDistinct'?: Validation;
|
|
2542
|
+
'operationAccess.advancedDistinct'?: Validation;
|
|
2543
|
+
'operationAccess.subs'?: Validation | SubRouteGuardOptions;
|
|
2544
|
+
}
|
|
2545
|
+
interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends DefaultModelRouterOptions<TModel> {
|
|
1867
2546
|
modelName?: string;
|
|
1868
2547
|
basePath?: string;
|
|
1869
2548
|
/**
|
|
1870
2549
|
* Update grants define assignment boundaries: authorized leaves preserve omitted
|
|
1871
2550
|
* siblings; a whole-object/array grant permits replacement (no generic deep merge).
|
|
1872
2551
|
* Send nested JSON; literal dotted client keys are not update operators.
|
|
2552
|
+
* Basic/advanced keys are route guards in operationAccess, not field grants here.
|
|
2553
|
+
*/
|
|
2554
|
+
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel> | Extract<keyof TVirtuals, string>>;
|
|
2555
|
+
/**
|
|
2556
|
+
* Package-level computed fields ("virtuals", VIRT-01).
|
|
2557
|
+
* Typed getter/descriptor/output generics per VIRT-00A D3: typed getters
|
|
2558
|
+
* use a partial read-only persisted view for their scope, never
|
|
2559
|
+
* `Record<string, unknown>` for a known model; untyped consumers keep an
|
|
2560
|
+
* explicit loose fallback. Per-access records use `default` +
|
|
2561
|
+
* `list`/`create`/`read`/`update` (NOT `delete`/`distinct`/`count`) and
|
|
2562
|
+
* recursive embedded `sub` scopes mirroring
|
|
2563
|
+
* `permissionSchema.<field>.sub`. Inapplicable registered names stay
|
|
2564
|
+
* virtual (never persisted). Mutable post-construction (not a build-time
|
|
2565
|
+
* key); replacement swaps the frozen snapshot so in-flight planning keeps
|
|
2566
|
+
* a coherent version.
|
|
2567
|
+
*
|
|
2568
|
+
* Getters run after `toObject()`/lean normalization (both lean and
|
|
2569
|
+
* hydrated results compute), after internal document-permissions
|
|
2570
|
+
* computation, and before `decorate`/`decorateAll`/tasks and final
|
|
2571
|
+
* trimming. `dependsOn` fields are fetched internally even when the caller
|
|
2572
|
+
* may not receive them, then stripped unless independently selected and
|
|
2573
|
+
* authorized. Virtuals are output-only: registered names (including
|
|
2574
|
+
* embedded `sub` leaves) are excluded from persisted projections, client
|
|
2575
|
+
* write admission, and database sort/filter/distinct handling.
|
|
2576
|
+
*
|
|
2577
|
+
* @example Shortest typed happy-path (persisted model lacks `fullAddress`):
|
|
2578
|
+
* ```ts
|
|
2579
|
+
* import acl from '@web-ts-toolkit/access-router';
|
|
2580
|
+
* interface User { name: string; address: string; }
|
|
2581
|
+
* interface UserVirtuals { fullAddress: string; }
|
|
2582
|
+
* const router = acl.createRouter<User, UserVirtuals>('User', {
|
|
2583
|
+
* permissionSchema: {
|
|
2584
|
+
* name: { read: true },
|
|
2585
|
+
* address: { read: 'canViewAddress' },
|
|
2586
|
+
* fullAddress: { read: 'canViewAddress' },
|
|
2587
|
+
* },
|
|
2588
|
+
* virtuals: {
|
|
2589
|
+
* fullAddress: {
|
|
2590
|
+
* dependsOn: ['address'],
|
|
2591
|
+
* read: async function (doc) {
|
|
2592
|
+
* if (doc.address === undefined) return undefined;
|
|
2593
|
+
* return `addr:${doc.address}`;
|
|
2594
|
+
* },
|
|
2595
|
+
* },
|
|
2596
|
+
* },
|
|
2597
|
+
* });
|
|
2598
|
+
* // Selected output: { name: string; fullAddress?: string } — virtual is optional.
|
|
2599
|
+
* void router;
|
|
2600
|
+
* ```
|
|
1873
2601
|
*/
|
|
1874
|
-
|
|
2602
|
+
virtuals?: ModelVirtuals<TModel, TVirtuals>;
|
|
1875
2603
|
alwaysSelectFields?: string[];
|
|
1876
2604
|
docPermissions?: DocPermissions | ModelDocPermissionsHook;
|
|
1877
2605
|
/**
|
|
@@ -1923,9 +2651,16 @@ interface DataRouterOptions<TData = unknown> {
|
|
|
1923
2651
|
resolveIdFilter?: DataIdentifierHook<TData>;
|
|
1924
2652
|
parentPath?: string;
|
|
1925
2653
|
queryRouteSegment?: string;
|
|
2654
|
+
/**
|
|
2655
|
+
* Live data-route guards: list/read plus basicList/advancedList/basicRead/advancedRead.
|
|
2656
|
+
* Only undefined variants inherit their base; false denies with HTTP 401 while
|
|
2657
|
+
* routes remain registered. Data never inherits model defaults. The broader
|
|
2658
|
+
* legacy operation-rule object and scalar Validation shorthand remain accepted.
|
|
2659
|
+
*/
|
|
1926
2660
|
operationAccess?: Validation | OperationAccess;
|
|
1927
2661
|
dataName?: string;
|
|
1928
2662
|
basePath?: string;
|
|
2663
|
+
/** Base-operation field grants, independent of operationAccess's basic/advanced route guards. */
|
|
1929
2664
|
permissionSchema?: PermissionSchema<AccessRouterFieldKey<TData>>;
|
|
1930
2665
|
baseFilter?: DataBaseFilterHook | Record<string, DataBaseFilterHook>;
|
|
1931
2666
|
overrideFilter?: DataOverrideFilterHook | Record<string, DataOverrideFilterHook>;
|
|
@@ -1933,7 +2668,7 @@ interface DataRouterOptions<TData = unknown> {
|
|
|
1933
2668
|
decorateAll?: DataListHook<TData> | Record<string, DataListHook<TData>>;
|
|
1934
2669
|
requestSchemas?: DataRequestSchemas;
|
|
1935
2670
|
}
|
|
1936
|
-
interface ExtendedModelRouterOptions<TModel = unknown
|
|
2671
|
+
interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, ExtendedDefaultModelRouterOptions<TModel> {
|
|
1937
2672
|
'alwaysSelectFields.default'?: string[];
|
|
1938
2673
|
'alwaysSelectFields.list'?: string[];
|
|
1939
2674
|
'alwaysSelectFields.create'?: string[];
|
|
@@ -2015,8 +2750,29 @@ interface ExtendedModelRouterOptions<TModel = unknown> extends ModelRouterOption
|
|
|
2015
2750
|
'defaults.publicReadOptions'?: PublicReadOptions;
|
|
2016
2751
|
'defaults.publicUpdateArgs'?: PublicUpdateArgs;
|
|
2017
2752
|
'defaults.publicUpdateOptions'?: PublicUpdateOptions;
|
|
2753
|
+
/**
|
|
2754
|
+
* Virtual dotted keys (VIRT-01): `virtuals`, `virtuals.<name>`,
|
|
2755
|
+
* `virtuals.<name>.<access>`, `virtuals.<field>.sub...` (recursive).
|
|
2756
|
+
* The template fallback preserves typed getter doc views for known
|
|
2757
|
+
* models so unknown persisted getter fields fail (including dotted
|
|
2758
|
+
* setters); embedded `sub` records stay structurally loose here and are
|
|
2759
|
+
* fully validated at runtime against the receiving Mongoose schema +
|
|
2760
|
+
* child scopes.
|
|
2761
|
+
*/
|
|
2762
|
+
[key: `virtuals.${string}`]: ModelVirtuals<TModel, TVirtuals>[Extract<keyof TVirtuals, string>] | ModelVirtualDescriptor<TModel, unknown> | ModelVirtualGetter<TModel, unknown> | {
|
|
2763
|
+
sub?: Record<string, unknown>;
|
|
2764
|
+
} | Record<string, ModelVirtualDescriptor<Record<string, unknown>, unknown> | ModelVirtualGetter<Record<string, unknown>, unknown> | {
|
|
2765
|
+
sub?: unknown;
|
|
2766
|
+
}>;
|
|
2018
2767
|
}
|
|
2019
2768
|
interface ExtendedDataRouterOptions<TData = unknown> extends DataRouterOptions<TData> {
|
|
2769
|
+
'operationAccess.default'?: Validation;
|
|
2770
|
+
'operationAccess.list'?: Validation;
|
|
2771
|
+
'operationAccess.read'?: Validation;
|
|
2772
|
+
'operationAccess.basicList'?: Validation;
|
|
2773
|
+
'operationAccess.advancedList'?: Validation;
|
|
2774
|
+
'operationAccess.basicRead'?: Validation;
|
|
2775
|
+
'operationAccess.advancedRead'?: Validation;
|
|
2020
2776
|
'requestSchemas.advancedList'?: RequestSchema;
|
|
2021
2777
|
'requestSchemas.advancedReadFilter'?: RequestSchema;
|
|
2022
2778
|
'requestSchemas.advancedRead'?: RequestSchema;
|
|
@@ -2123,4 +2879,4 @@ declare function fromIoTs<TValue = unknown>(decoder: IoTsDecoderLike<TValue>): R
|
|
|
2123
2879
|
declare function fromSuperstruct<TStruct, TOutput = unknown>(struct: TStruct, validate: SuperstructValidateLike): RequestSchemaValidator<TOutput>;
|
|
2124
2880
|
declare function fromVine<TValue = unknown>(validator: VineValidatorLike<TValue>): RequestSchemaValidator<TValue>;
|
|
2125
2881
|
|
|
2126
|
-
export { type DistinctArgs as $, type AccessRouterBaseRequest as A, type BaseFilterAccess as B, type Change as C, type CreateArgs as D, type CreateOptions as E, type CreateQueryInput as F, CustomHeaders as G, DATA_MIDDLEWARE as H, type DataBaseFilterHook as I, type DataFilter as J, type DataFindArgs as K, type DataFindOneArgs as L, type DataFindOneOptions as M, type DataFindOptions as N, type DataHook as O, type DataHookContext as P, type DataIdentifierHook as Q, type DataListHook as R, type DataOverrideFilterHook as S, type DataRequest as T, type DataRequestSchemas as U, type DataRouterOptions as V, type DecorateAccess as W, type DecorateAllAccess as X, type DeepFieldPath as Y, type DefaultModelRouterOptions as Z, type Defaults as _, type AccessRouterFieldKey as a, type
|
|
2882
|
+
export { type DistinctArgs as $, type AccessRouterBaseRequest as A, type BaseFilterAccess as B, type Change as C, type CreateArgs as D, type CreateOptions as E, type CreateQueryInput as F, CustomHeaders as G, DATA_MIDDLEWARE as H, type DataBaseFilterHook as I, type DataFilter as J, type DataFindArgs as K, type DataFindOneArgs as L, type DataFindOneOptions as M, type DataFindOptions as N, type DataHook as O, type DataHookContext as P, type DataIdentifierHook as Q, type DataListHook as R, type DataOverrideFilterHook as S, type DataRequest as T, type DataRequestSchemas as U, type DataRouterOptions as V, type DecorateAccess as W, type DecorateAllAccess as X, type DeepFieldPath as Y, type DefaultModelRouterOptions as Z, type Defaults as _, type AccessRouterFieldKey as a, type Populate as a$, type DistinctBody as a0, type DocPermissionsAccess as a1, type ErrorResult as a2, type ExistsOptions as a3, type ExtendedDataRouterOptions as a4, type ExtendedDefaultModelRouterOptions as a5, type ExtendedModelRouterOptions as a6, type FieldOperationAccess as a7, type Filter as a8, FilterOperator as a9, type ModelDocPermissionsHook as aA, type ModelDocument as aB, type ModelDocumentHook as aC, type ModelHook as aD, type ModelHookContext as aE, type ModelIdentifierHook as aF, type ModelListHook as aG, type ModelOverrideFilterHook as aH, type ModelRequest as aI, type ModelRouterOptions as aJ, type ModelValidateHook as aK, type ModelVirtualAccess as aL, type ModelVirtualAccessRecord as aM, type ModelVirtualContext as aN, type ModelVirtualDescriptor as aO, type ModelVirtualDoc as aP, type ModelVirtualGetter as aQ, type ModelVirtualLeaf as aR, type ModelVirtualRecordAccess as aS, type ModelVirtuals as aT, type OperationAccess as aU, PERMISSIONS as aV, PERMISSION_KEYS as aW, type PairedRouteAccess as aX, type ParentRef as aY, type PathValue as aZ, type PermissionSchema as a_, type FindAccess as aa, type FindArgs as ab, type FindByIdArgs as ac, type FindByIdOptions as ad, type FindOneArgs as ae, type FindOneOptions as af, type FindOptions as ag, type GlobalOptions as ah, type GlobalPermissionValue as ai, type GuardHook as aj, type IdentifierHook as ak, type Include as al, type IoTsContextEntryLike as am, type IoTsDecodeErrorLike as an, type IoTsDecoderLike as ao, type JoiSchemaLike as ap, type JoiValidationErrorDetailLike as aq, type KeyValueProjection as ar, type LegacyInclude as as, type ListQueryInput as at, type ListResult as au, MIDDLEWARE as av, type MaybePromise as aw, type ModelBaseFilterHook as ax, type ModelChangeHook as ay, type ModelDeleteHook as az, type AccessRouterLogger as b, type SingleResult as b$, type PopulateAccess as b0, type PrepareAccess as b1, type Projection as b2, type PublicCreateArgs as b3, type PublicCreateOptions as b4, type PublicErrorResult as b5, type PublicListArgs as b6, type PublicListOptions as b7, type PublicListResult as b8, type PublicOutput as b9, type RootModelDistinctQueryEntry as bA, type RootModelListQueryEntry as bB, type RootModelNewQueryEntry as bC, type RootModelOperation as bD, type RootModelReadByFilterQueryEntry as bE, type RootModelReadByIdQueryEntry as bF, type RootModelSubBulkUpdateQueryEntry as bG, type RootModelSubCreateQueryEntry as bH, type RootModelSubDeleteQueryEntry as bI, type RootModelSubListQueryEntry as bJ, type RootModelSubReadQueryEntry as bK, type RootModelSubUpdateQueryEntry as bL, type RootModelUpdateQueryEntry as bM, type RootModelUpsertQueryEntry as bN, type RootOperationResult as bO, type RootQueryEntry as bP, type RootRouterOptions as bQ, type RootSubOperation as bR, type RootTarget as bS, type RouteBaseAccess as bT, type RouteGuardAccess as bU, type RouteVariant as bV, type RouteVariantAccess as bW, type SelectAccess as bX, type SelectedPopulatedPublicOutput as bY, type SelectedPublicOutput as bZ, type ServiceResult as b_, type PublicReadArgs as ba, type PublicReadOptions as bb, type PublicServiceResult as bc, type PublicSingleResult as bd, type PublicUpdateArgs as be, type PublicUpdateOptions as bf, type PublicUpsertArgs as bg, type PublicUpsertOptions as bh, type ReadQueryInput as bi, type Request as bj, type RequestSchemaAdapter as bk, type RequestSchemaFailure as bl, type RequestSchemaIssue as bm, type RequestSchemaLike as bn, type RequestSchemaOptions as bo, type RequestSchemaResult as bp, type RequestSchemaSuccess as bq, type RequestSchemaValidator as br, type RequestSchemas as bs, type RootDataListQueryEntry as bt, type RootDataOperation as bu, type RootDataReadByFilterQueryEntry as bv, type RootDataReadByIdQueryEntry as bw, type RootModelCountQueryEntry as bx, type RootModelCreateQueryEntry as by, type RootModelDeleteQueryEntry as bz, type AccessRouterRequest as c, fromValibot as c$, type Sort as c0, type SortOrder as c1, type StandardSchemaFailure as c2, type StandardSchemaInferOutput as c3, type StandardSchemaIssue as c4, type StandardSchemaPathSegment as c5, type StandardSchemaResult as c6, type StandardSchemaSuccess as c7, type StandardSchemaV1 as c8, StatusCodes as c9, type UpdateOneOptions as cA, type UpdateQueryInput as cB, type UpsertArgs as cC, type UpsertOptions as cD, type UpsertQueryInput as cE, type ValibotIssueLike as cF, type ValibotPathItemLike as cG, type ValibotSafeParseLike as cH, type ValibotSafeParseResult as cI, type ValidateAccess as cJ, type ValidateRule as cK, type Validation as cL, type ValidationError as cM, type VineValidationErrorLike as cN, type VineValidationMessageLike as cO, type VineValidatorLike as cP, type VirtualSubModel as cQ, type WithVirtuals as cR, type YupSchemaLike as cS, type YupValidationErrorLike as cT, defineRequestSchema as cU, fromAjv as cV, fromArkType as cW, fromIoTs as cX, fromJoi as cY, fromStandardSchema as cZ, fromSuperstruct as c_, type SubListBody as ca, type SubOperationAccess as cb, type SubPopulate as cc, type SubQueryEntry as cd, type SubReadBody as ce, type SubRouteGuardOptions as cf, type SubdocumentBulkRecord as cg, type SubdocumentBulkUpdateInput as ch, type SubdocumentCreateInput as ci, type SubdocumentCreateOptions as cj, type SubdocumentId as ck, type SubdocumentListOptions as cl, type SubdocumentName as cm, type SubdocumentParentArgs as cn, type SubdocumentParentOptions as co, type SubdocumentReadOptions as cp, type SubdocumentRecord as cq, type SuperstructFailureLike as cr, type SuperstructValidateLike as cs, type Task as ct, type TaskType as cu, type TransformAccess as cv, type TypedFilter as cw, type UpdateByIdArgs as cx, type UpdateByIdOptions as cy, type UpdateOneArgs as cz, type AccessRouterRequestExtensions as d, fromVine as d0, fromYup as d1, fromZod as d2, parseBody as d3, parseBodyWithSchema as d4, parseNestedBodyWithSchema as d5, parsePathParam as d6, parseQuery as d7, AccessRuntime as d8, DataService as d9, Service as da, PublicService as db, type OpenApiRouterOptions as dc, type OpenApiRegistryOptions as dd, type OpenApiRouteDescriptor as de, type OpenApiDocumentOptions as df, type OpenApiParameter as dg, type OpenApiSchema as dh, type OpenApiResponses as di, type AccessRouterPermissionMap as dj, type AccessRouterPermissions as dk, type OpenApiMethod as dl, type Permissions as dm, defaultRuntime as dn, type AdvancedCreateBody as e, type AdvancedListBody as f, type AdvancedReadBody as g, type AdvancedReadFilterBody as h, type AdvancedUpdateBody as i, type AdvancedUpsertBody as j, type AfterPersistAccess as k, type AjvErrorObjectLike as l, type AjvValidationErrorLike as m, type AjvValidatorLike as n, type ArkTypeErrorsLike as o, type ArkTypeLike as p, type ArkTypeProblemLike as q, Codes as r, type CorrelatedCountInclude as s, type CorrelatedFilter as t, type CorrelatedInclude as u, type CorrelatedIncludeArgs as v, type CorrelatedListInclude as w, type CorrelatedReadByFilterInclude as x, type CorrelatedReadByIdInclude as y, type CountBody as z };
|