@web-ts-toolkit/access-router 0.47.1 → 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.
@@ -24,9 +24,128 @@ declare class Permission {
24
24
  type Permissions = Permission;
25
25
  type AccessRouterPermissions = Permission;
26
26
 
27
- declare class Service<TModel = unknown> extends Base<TModel> {
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,9 +157,187 @@ 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>;
161
+ /**
162
+ * Router-level permission output postures (see `ModelRouterOptions`).
163
+ * Absolute: they win over client input and per-operation `defaults`.
164
+ */
165
+ getPermissionsPosture(): {
166
+ stripPermissionsField: boolean;
167
+ disableFieldPermissions: boolean;
168
+ };
169
+ /**
170
+ * Router posture `stripPermissionsField`: drop the whole document
171
+ * permissions field (including the empty placeholder) from output instead
172
+ * of emitting permission metadata. Returns true when applied.
173
+ */
174
+ private stripPermissionsOutput;
42
175
  private asServiceHookContext;
43
176
  private getEffectiveAllowedSortFields;
177
+ /**
178
+ * With `requireExplicitSelect`, an omitted client select behaves as an
179
+ * `_id`-only projection, yielding field-less rows. Explicit selects pass
180
+ * through untouched.
181
+ */
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;
44
341
  private beginOp;
45
342
  private completeOp;
46
343
  constructor(req: ModelRequest, modelName: string);
@@ -54,6 +351,7 @@ declare class Service<TModel = unknown> extends Base<TModel> {
54
351
  }, options?: {
55
352
  skim?: boolean;
56
353
  includePermissions?: boolean;
354
+ includeFieldPermissions?: boolean;
57
355
  }): Promise<SingleResult<TModel>>;
58
356
  /**
59
357
  * Update at authorized policy paths: admitted leaves preserve omitted siblings;
@@ -143,43 +441,52 @@ declare class Service<TModel = unknown> extends Base<TModel> {
143
441
  getParentDoc(id: SubdocumentId, sub: SubdocumentName, args?: SubdocumentParentArgs, options?: SubdocumentParentOptions): Promise<any>;
144
442
  }
145
443
 
146
- declare class PublicService<TModel = unknown> extends Service<TModel> {
444
+ declare class PublicService<TModel = unknown, TVirtuals extends object = Record<never, never>> extends Service<TModel, TVirtuals> {
147
445
  _list<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(filter: Filter<TModel>, args?: Omit<PublicListArgs, 'select' | 'populate'> & {
148
446
  select?: TSelect;
149
447
  populate?: TPopulate;
150
- }, options?: PublicListOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
448
+ }, options?: PublicListOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
151
449
  _create<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(data: unknown, args?: Omit<PublicCreateArgs, 'select' | 'populate'> & {
152
450
  select?: TSelect;
153
451
  populate?: TPopulate;
154
- }, options?: PublicCreateOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
452
+ }, options?: PublicCreateOptions): Promise<ListResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
155
453
  _new(args?: {
156
454
  select?: string[];
157
455
  }, options?: {
158
456
  skim?: boolean;
159
457
  includePermissions?: boolean;
160
- }): Promise<SingleResult<PublicOutput<TModel>>>;
458
+ includeFieldPermissions?: boolean;
459
+ }): Promise<SingleResult<PublicOutput<TModel, TVirtuals>>>;
161
460
  _read<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(id: string, args?: Omit<PublicReadArgs, 'select' | 'populate'> & {
162
461
  select?: TSelect;
163
462
  populate?: TPopulate;
164
- }, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
463
+ }, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
165
464
  _readFilter<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(filter: Filter<TModel>, args?: Omit<PublicReadArgs, 'select' | 'populate'> & {
166
465
  select?: TSelect;
167
466
  populate?: TPopulate;
168
467
  sort?: Sort;
169
- }, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
468
+ }, options?: PublicReadOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
170
469
  _update<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(id: string, data: Record<string, unknown>, args?: Omit<PublicUpdateArgs, 'select' | 'populate'> & {
171
470
  select?: TSelect;
172
471
  populate?: TPopulate;
173
- }, options?: PublicUpdateOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
472
+ }, options?: PublicUpdateOptions): Promise<SingleResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
174
473
  /**
175
474
  * With an _id, check update-policy existence then update: terminal denial returns
176
475
  * Forbidden, an allowed miss remains Unauthorized, and neither creates a row.
177
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.
178
485
  */
179
486
  _upsert<TSelect extends Projection | undefined = undefined, TPopulate extends Populate[] | string | undefined = undefined>(data: Record<string, unknown>, args?: Omit<PublicUpsertArgs, 'select' | 'populate'> & {
180
487
  select?: TSelect;
181
488
  populate?: TPopulate;
182
- }, options?: PublicUpsertOptions): Promise<ServiceResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate>> | ErrorResult>;
489
+ }, options?: PublicUpsertOptions): Promise<ServiceResult<SelectedPopulatedPublicOutput<TModel, TSelect, TPopulate, TVirtuals>> | ErrorResult>;
183
490
  _delete(id: string): Promise<SingleResult<unknown> | ErrorResult>;
184
491
  _distinct(field: string, options?: DistinctArgs<TModel>): Promise<ListResult<unknown> | ErrorResult>;
185
492
  _count(filter: Filter<TModel>, access?: BaseFilterAccess): Promise<SingleResult<number> | ErrorResult>;
@@ -398,22 +705,28 @@ type ListQueryInput = {
398
705
  page_size?: string;
399
706
  skim?: 'true' | 'false';
400
707
  include_permissions?: 'true' | 'false';
708
+ include_field_permissions?: 'true' | 'false';
401
709
  include_count?: 'true' | 'false';
402
710
  include_extra_headers?: 'true' | 'false';
403
711
  };
404
712
  type CreateQueryInput = {
405
713
  include_permissions?: 'true' | 'false';
714
+ include_field_permissions?: 'true' | 'false';
406
715
  };
407
716
  type ReadQueryInput = {
408
717
  include_permissions?: 'true' | 'false';
718
+ include_field_permissions?: 'true' | 'false';
409
719
  try_list?: 'true' | 'false';
410
720
  };
411
721
  type UpdateQueryInput = {
412
722
  returning_all?: 'true' | 'false';
723
+ include_permissions?: 'true' | 'false';
724
+ include_field_permissions?: 'true' | 'false';
413
725
  };
414
726
  type UpsertQueryInput = {
415
727
  returning_all?: 'true' | 'false';
416
728
  include_permissions?: 'true' | 'false';
729
+ include_field_permissions?: 'true' | 'false';
417
730
  };
418
731
  type AdvancedListBody = {
419
732
  filter?: Filter | unknown[];
@@ -429,9 +742,10 @@ type AdvancedListBody = {
429
742
  options?: {
430
743
  skim?: boolean;
431
744
  includePermissions?: boolean;
745
+ includeFieldPermissions?: boolean;
432
746
  includeCount?: boolean;
433
747
  includeExtraHeaders?: boolean;
434
- populateAccess?: unknown;
748
+ populateAccess?: PopulateAccess;
435
749
  };
436
750
  };
437
751
  type CountBody = {
@@ -447,8 +761,9 @@ type AdvancedReadFilterBody = {
447
761
  options?: {
448
762
  skim?: boolean;
449
763
  includePermissions?: boolean;
764
+ includeFieldPermissions?: boolean;
450
765
  tryList?: boolean;
451
- populateAccess?: unknown;
766
+ populateAccess?: PopulateAccess;
452
767
  };
453
768
  };
454
769
  type AdvancedReadBody = {
@@ -459,8 +774,9 @@ type AdvancedReadBody = {
459
774
  options?: {
460
775
  skim?: boolean;
461
776
  includePermissions?: boolean;
777
+ includeFieldPermissions?: boolean;
462
778
  tryList?: boolean;
463
- populateAccess?: unknown;
779
+ populateAccess?: PopulateAccess;
464
780
  };
465
781
  };
466
782
  type AdvancedCreateBody = {
@@ -470,7 +786,8 @@ type AdvancedCreateBody = {
470
786
  tasks?: Task | Task[];
471
787
  options?: {
472
788
  includePermissions?: boolean;
473
- populateAccess?: unknown;
789
+ includeFieldPermissions?: boolean;
790
+ populateAccess?: PopulateAccess;
474
791
  };
475
792
  };
476
793
  type AdvancedUpdateBody = {
@@ -481,7 +798,8 @@ type AdvancedUpdateBody = {
481
798
  options?: {
482
799
  returningAll?: boolean;
483
800
  includePermissions?: boolean;
484
- populateAccess?: unknown;
801
+ includeFieldPermissions?: boolean;
802
+ populateAccess?: PopulateAccess;
485
803
  };
486
804
  };
487
805
  type AdvancedUpsertBody = {
@@ -492,7 +810,8 @@ type AdvancedUpsertBody = {
492
810
  options?: {
493
811
  returningAll?: boolean;
494
812
  includePermissions?: boolean;
495
- populateAccess?: unknown;
813
+ includeFieldPermissions?: boolean;
814
+ populateAccess?: PopulateAccess;
496
815
  };
497
816
  };
498
817
  type DistinctBody = {
@@ -524,6 +843,23 @@ interface RequestComplexityOptions {
524
843
  * Not a process-wide connection limit or protection against competing edits.
525
844
  */
526
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
+ */
527
863
  maxHookConcurrency?: number;
528
864
  /** Cumulative correlated target executions per request/runtime (default 100); permit release does not refund this budget. */
529
865
  maxCorrelatedQueries?: number;
@@ -698,21 +1034,49 @@ declare class AccessRuntime {
698
1034
  getDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions<unknown>[K];
699
1035
  private createModelOptions;
700
1036
  private getOrCreateModelOptions;
701
- setModelOptions<TModel = unknown>(modelName: string, options: ModelRouterOptions<TModel>): void;
702
- setModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K, value: ExtendedModelRouterOptions<TModel>[K]): void;
703
- getModelOptions<TModel = unknown>(modelName: string): ModelRouterOptions<TModel>;
704
- getModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions<TModel>[K]): ExtendedModelRouterOptions<TModel>[K];
705
- getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K | string): ExtendedModelRouterOptions<TModel>[K];
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;
706
1070
  getModelNames(): string[];
707
1071
  hasModel(modelName: string): boolean;
708
1072
  getModelJsonSchema(modelName: string): Record<string, unknown>;
709
1073
  private createDataOptions;
710
1074
  private getOrCreateDataOptions;
711
1075
  setDataOptions<TData = unknown>(dataName: string, options: DataRouterOptions<TData>): void;
712
- setDataOption<K extends keyof DataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: DataRouterOptions<TData>[K]): void;
1076
+ setDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: ExtendedDataRouterOptions<TData>[K]): void;
713
1077
  getDataOptions<TData = unknown>(dataName: string): DataRouterOptions<TData>;
714
1078
  getDataSnapshot<TData = unknown>(dataName: string): readonly TData[];
715
- getDataOption<K extends keyof DataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: DataRouterOptions<TData>[K]): DataRouterOptions<TData>[K];
1079
+ getDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: ExtendedDataRouterOptions<TData>[K]): ExtendedDataRouterOptions<TData>[K];
716
1080
  getExactDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions<TData>[K];
717
1081
  getDataNames(): string[];
718
1082
  ensureModelMeta(modelName: string): void;
@@ -722,22 +1086,12 @@ declare class AccessRuntime {
722
1086
  }
723
1087
  declare const defaultRuntime: AccessRuntime;
724
1088
 
725
- declare class RequestConcurrencyScheduler {
726
- readonly limit: number;
727
- private activeWork;
728
- private readonly waiters;
729
- constructor(limit: number);
730
- map<TInput, TOutput>(items: TInput[], iteratee: (item: TInput, index: number, scheduled: true) => Promise<TOutput>): Promise<TOutput[]>;
731
- run<TOutput>(iteratee: (scheduled: true) => Promise<TOutput>): Promise<TOutput>;
732
- work<TOutput>(operation: () => TOutput | PromiseLike<TOutput>): Promise<TOutput>;
733
- }
734
-
735
1089
  type CrossResourceModelOperation = 'list' | 'read' | 'count';
736
1090
  interface CorrelatedExecState {
737
1091
  totalQueries: number;
738
1092
  scheduler: RequestConcurrencyScheduler;
739
1093
  }
740
- declare class Base<TModel = unknown> {
1094
+ declare class Base<TModel = unknown, TVirtuals extends object = Record<never, never>> {
741
1095
  protected req: ModelRequest;
742
1096
  protected modelName: string;
743
1097
  protected readonly requestRuntime: AccessRuntime;
@@ -772,7 +1126,7 @@ declare class Base<TModel = unknown> {
772
1126
  getRequestComplexity(): Required<RequestComplexityOptions>;
773
1127
  protected getClientRequestErrorResult(error: unknown): ErrorResult | null;
774
1128
  protected throwClientRequestError(code: ErrorResult['code'], detail: string): never;
775
- protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation): Promise<PublicService<unknown>>;
1129
+ protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation): Promise<PublicService<unknown, Record<never, never>>>;
776
1130
  protected processInclude(include: Include | Include[]): {
777
1131
  includes: LegacyInclude[];
778
1132
  correlatedIncludes: CorrelatedInclude[];
@@ -781,6 +1135,17 @@ declare class Base<TModel = unknown> {
781
1135
  correlatedReferenceFields: string[];
782
1136
  };
783
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;
784
1149
  private assertIncludeOutputPaths;
785
1150
  private getForeignKeyValues;
786
1151
  private getForeignKey;
@@ -853,6 +1218,20 @@ declare class Core {
853
1218
  beforeDelete<T>(modelName: string, doc: T, context: ModelHookContext): Promise<void>;
854
1219
  afterDelete<T>(modelName: string, doc: T, context: ModelHookContext): Promise<void>;
855
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>>;
856
1235
  addEmptyPermissions<T>(modelName: string, doc: T): T;
857
1236
  addDocPermissions<T>(modelName: string, doc: T, access: DocPermissionsAccess, context: ModelHookContext): Promise<T>;
858
1237
  addFieldPermissions<T extends {
@@ -865,10 +1244,12 @@ declare class Core {
865
1244
  setPermissions(): Promise<void>;
866
1245
  canActivate(routeGuard: Validation): Promise<boolean>;
867
1246
  isAllowed(modelName: string, access: RouteGuardAccess | string): Promise<boolean>;
868
- getService<TModel = unknown>(modelName: string): Service<TModel>;
869
- getPublicService<TModel = unknown>(modelName: string): PublicService<TModel>;
870
- service<TModel = unknown>(modelName: string): PublicService<TModel>;
871
- svc<TModel = unknown>(modelName: string): PublicService<TModel>;
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>;
872
1253
  private getGlobalPermissions;
873
1254
  }
874
1255
 
@@ -888,6 +1269,8 @@ declare class DataCore {
888
1269
  setPermissions(): Promise<void>;
889
1270
  canActivate(routeGuard: Validation): Promise<boolean>;
890
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>;
891
1274
  getService<TData = unknown>(dataName: string): DataService<TData>;
892
1275
  service<TData = unknown>(dataName: string): DataService<TData>;
893
1276
  svc<TData = unknown>(dataName: string): DataService<TData>;
@@ -948,10 +1331,37 @@ interface PublicCreateArgs {
948
1331
  tasks?: Task | Task[];
949
1332
  }
950
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
+ };
951
1352
  }
952
1353
  interface PublicCreateOptions {
953
1354
  skim?: boolean;
954
1355
  includePermissions?: boolean;
1356
+ /**
1357
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
1358
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
1359
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
1360
+ * Enabling field maps also computes doc permissions as grant input (even
1361
+ * under skim or when `includePermissions` is false), so the maps match the
1362
+ * `includePermissions: true` output.
1363
+ */
1364
+ includeFieldPermissions?: boolean;
955
1365
  populateAccess?: PopulateAccess;
956
1366
  }
957
1367
  interface CreateOptions extends PublicCreateOptions {
@@ -971,6 +1381,15 @@ interface PublicListArgs {
971
1381
  interface PublicListOptions {
972
1382
  skim?: boolean;
973
1383
  includePermissions?: boolean;
1384
+ /**
1385
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
1386
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
1387
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
1388
+ * Enabling field maps also computes doc permissions as grant input (even
1389
+ * under skim or when `includePermissions` is false), so the maps match the
1390
+ * `includePermissions: true` output.
1391
+ */
1392
+ includeFieldPermissions?: boolean;
974
1393
  includeCount?: boolean;
975
1394
  populateAccess?: PopulateAccess;
976
1395
  lean?: boolean;
@@ -987,6 +1406,15 @@ interface PublicReadOptions {
987
1406
  populateAccess?: PopulateAccess;
988
1407
  lean?: boolean;
989
1408
  includePermissions?: boolean;
1409
+ /**
1410
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
1411
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
1412
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
1413
+ * Enabling field maps also computes doc permissions as grant input (even
1414
+ * under skim or when `includePermissions` is false), so the maps match the
1415
+ * `includePermissions: true` output.
1416
+ */
1417
+ includeFieldPermissions?: boolean;
990
1418
  /** Retry an authorized read miss with list access (default true); requires list operation access. Forbidden/BadRequest never retry. */
991
1419
  tryList?: boolean;
992
1420
  }
@@ -1003,13 +1431,34 @@ interface UpdateOneArgs<T = unknown> extends Omit<PublicUpdateArgs, 'select' | '
1003
1431
  /** Trusted replacement: nullish generates row policy; false returns Forbidden without filter generation or persistence. */
1004
1432
  filter?: Filter<T>;
1005
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;
1006
1448
  };
1007
1449
  }
1008
- interface UpdateByIdArgs<T = unknown> extends Omit<UpdateOneArgs<T>, 'overrides'> {
1450
+ interface UpdateByIdArgs<T = unknown> extends Omit<PublicUpdateArgs, 'select' | 'tasks'> {
1009
1451
  overrides?: {
1010
1452
  populate?: Populate[] | string;
1011
1453
  /** Nullish resolves the identifier; false denies without ID/filter generation or persistence. Objects still receive row policy. */
1012
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;
1013
1462
  };
1014
1463
  }
1015
1464
  interface UpsertArgs<T = unknown> extends UpdateOneArgs<T> {
@@ -1018,6 +1467,15 @@ interface PublicUpdateOptions {
1018
1467
  skim?: boolean;
1019
1468
  returningAll?: boolean;
1020
1469
  includePermissions?: boolean;
1470
+ /**
1471
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
1472
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
1473
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
1474
+ * Enabling field maps also computes doc permissions as grant input (even
1475
+ * under skim or when `includePermissions` is false), so the maps match the
1476
+ * `includePermissions: true` output.
1477
+ */
1478
+ includeFieldPermissions?: boolean;
1021
1479
  populateAccess?: PopulateAccess;
1022
1480
  }
1023
1481
  interface PublicUpsertOptions extends PublicUpdateOptions {
@@ -1067,7 +1525,13 @@ type WithDefaultId<T> = T extends object ? T & {
1067
1525
  } : {
1068
1526
  _id?: unknown;
1069
1527
  };
1070
- type PublicOutput<T> = T extends object ? T & Record<string, unknown> : Record<string, unknown>;
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>;
1071
1535
  type ModelDocument<TModel = unknown> = mongoose.Document & TModel;
1072
1536
  type TrimLeft<S extends string> = S extends ` ${infer Rest}` ? TrimLeft<Rest> : S;
1073
1537
  type TrimRight<S extends string> = S extends `${infer Rest} ` ? TrimRight<Rest> : S;
@@ -1085,7 +1549,12 @@ type DeepPickSingle<T, TPath extends string> = TPath extends `${infer Head}.${in
1085
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];
1086
1550
  } : {} : {} : TPath extends keyof WithDefaultId<T> ? Pick<WithDefaultId<T>, TPath> : {};
1087
1551
  type DeepPick<T, TPaths extends string> = Simplify<UnionToIntersection<DeepPickSingle<T, TPaths>>>;
1088
- type SelectedPublicOutput<T, TProjection> = string extends PositiveProjectionPaths<TProjection> ? PublicOutput<T> : [PositiveProjectionPaths<TProjection>] extends [never] ? PublicOutput<T> : DeepPick<T, Extract<PositiveProjectionPaths<TProjection>, string>>;
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>>;
1089
1558
  type PopulateInput = Populate | string;
1090
1559
  type PopulateInputs<TPopulate> = TPopulate extends readonly (infer U)[] ? U : TPopulate;
1091
1560
  type PopulatePath<TPopulateEntry> = TPopulateEntry extends string ? TPopulateEntry : TPopulateEntry extends {
@@ -1102,7 +1571,7 @@ type PopulateEntryShape<T, TPath extends string, TValue> = TPath extends `${infe
1102
1571
  }, TPath> : {};
1103
1572
  type PopulateOutputShape<T, TPopulateEntry> = PopulatePath<TPopulateEntry> extends infer TPath extends string ? PopulateEntryShape<T, TPath, PopulateLeafValue<PathValue<WithDefaultId<T>, TPath>, TPopulateEntry>> : {};
1104
1573
  type PopulateOutput<T, TPopulate> = string extends PopulatePath<PopulateInputs<TPopulate>> ? {} : [PopulatePath<PopulateInputs<TPopulate>>] extends [never] ? {} : Simplify<UnionToIntersection<PopulateOutputShape<T, Extract<PopulateInputs<TPopulate>, PopulateInput>>>>;
1105
- type SelectedPopulatedPublicOutput<T, TProjection, TPopulate> = Simplify<SelectedPublicOutput<T, TProjection> & PopulateOutput<T, TPopulate>>;
1574
+ type SelectedPopulatedPublicOutput<T, TProjection, TPopulate, TVirtuals extends object = Record<never, never>> = Simplify<SelectedPublicOutput<T, TProjection, TVirtuals> & PopulateOutput<T, TPopulate>>;
1106
1575
  type Unwrap<T> = NonNullable<Descend<T>>;
1107
1576
  type DeepFieldPath<T, Depth extends number = 4> = [Depth] extends [never] ? never : Unwrap<T> extends NonTraversable ? never : {
1108
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);
@@ -1233,7 +1702,16 @@ interface SubPopulate {
1233
1702
  type GuardHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, permissions: AccessRouterPermissions) => boolean | Promise<boolean>;
1234
1703
  type Validation = boolean | string | string[] | GuardHook;
1235
1704
  type SelectAccess = 'list' | 'create' | 'read' | 'update' | string;
1236
- type RouteGuardAccess = 'new' | 'list' | 'read' | 'update' | 'upsert' | 'delete' | 'create' | 'distinct' | 'count' | 'subs' | string;
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 & {});
1237
1715
  type DocPermissionsAccess = 'list' | 'create' | 'read' | 'update' | string;
1238
1716
  type BaseFilterAccess = 'list' | 'read' | 'update' | 'delete' | string;
1239
1717
  type DecorateAccess = 'list' | 'create' | 'read' | 'update' | string;
@@ -1302,6 +1780,7 @@ interface RootModelNewQueryEntry extends RootQueryEntryBase<'model', 'new'> {
1302
1780
  options?: {
1303
1781
  skim?: boolean;
1304
1782
  includePermissions?: boolean;
1783
+ includeFieldPermissions?: boolean;
1305
1784
  };
1306
1785
  }
1307
1786
  interface RootModelListQueryEntry extends RootQueryEntryBase<'model', 'list'> {
@@ -1431,7 +1910,7 @@ interface Task {
1431
1910
  options?: Record<string, unknown>;
1432
1911
  }
1433
1912
  interface AccessRouterBaseRequest extends AccessRouterRequest {
1434
- query: Record<'skip' | 'limit' | 'page' | 'page_size' | 'try_list' | 'skim' | 'include_permissions' | 'include_count' | 'include_extra_headers' | 'returning_all', string>;
1913
+ query: Record<'skip' | 'limit' | 'page' | 'page_size' | 'try_list' | 'skim' | 'include_permissions' | 'include_field_permissions' | 'include_count' | 'include_extra_headers' | 'returning_all', string>;
1435
1914
  }
1436
1915
  interface ModelRequest extends AccessRouterBaseRequest {
1437
1916
  macl: Core;
@@ -1522,6 +2001,15 @@ interface FindArgs<T = unknown> {
1522
2001
  interface FindOptions {
1523
2002
  skim?: boolean;
1524
2003
  includePermissions?: boolean;
2004
+ /**
2005
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
2006
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
2007
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
2008
+ * Enabling field maps also computes doc permissions as grant input (even
2009
+ * under skim or when `includePermissions` is false), so the maps match the
2010
+ * `includePermissions: true` output.
2011
+ */
2012
+ includeFieldPermissions?: boolean;
1525
2013
  includeCount?: boolean;
1526
2014
  populateAccess?: PopulateAccess;
1527
2015
  lean?: boolean;
@@ -1544,6 +2032,15 @@ interface FindOneOptions {
1544
2032
  skim?: boolean;
1545
2033
  lean?: boolean;
1546
2034
  includePermissions?: boolean;
2035
+ /**
2036
+ * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions`
2037
+ * when omitted. `includePermissions: true` + `includeFieldPermissions: false`
2038
+ * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely.
2039
+ * Enabling field maps also computes doc permissions as grant input (even
2040
+ * under skim or when `includePermissions` is false), so the maps match the
2041
+ * `includePermissions: true` output.
2042
+ */
2043
+ includeFieldPermissions?: boolean;
1547
2044
  }
1548
2045
  interface FindByIdArgs<T = unknown> {
1549
2046
  select?: Projection;
@@ -1597,6 +2094,161 @@ type ModelHook<TValue = unknown> = HookChain<TValue, ModelHookContext, ModelRequ
1597
2094
  type ModelListHook<TValue = unknown> = HookChain<TValue[], ModelHookContext, ModelRequest>;
1598
2095
  type DataHook<TValue = unknown> = HookChain<TValue, DataHookContext, DataRequest>;
1599
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
+ };
1600
2252
 
1601
2253
  interface DefaultFindOneArgs<TModel = unknown> extends Omit<FindOneArgs<TModel>, 'overrides'> {
1602
2254
  }
@@ -1609,7 +2261,33 @@ type NestedRequestSchema = {
1609
2261
  default?: RequestSchema;
1610
2262
  data?: RequestSchema;
1611
2263
  };
1612
- type SubRouteGuardOptions = Record<string, Validation | Record<string, Validation>>;
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>>;
1613
2291
  interface RequestSchemas {
1614
2292
  create?: RequestSchema;
1615
2293
  update?: RequestSchema;
@@ -1678,25 +2356,93 @@ interface GlobalOptions {
1678
2356
  }
1679
2357
  interface RootRouterOptions {
1680
2358
  basePath: string;
2359
+ /** Guard for the batch endpoint. Entries use target base operations, never basic/advanced variant names. */
1681
2360
  operationAccess?: Validation;
1682
2361
  maxBatchEntries?: number;
1683
2362
  maxOrderGroups?: number;
1684
2363
  /** Whole root operations per order group; separate from requestComplexity.maxBulkConcurrency persistence admission. */
1685
2364
  maxConcurrentOperations?: number;
1686
2365
  }
1687
- interface OperationAccess {
2366
+ /** Base-operation field grants used by PermissionSchema. Basic/advanced route keys do not grant fields. */
2367
+ interface FieldOperationAccess {
2368
+ new?: Validation;
2369
+ list?: Validation;
2370
+ create?: Validation;
2371
+ read?: Validation;
2372
+ update?: Validation;
2373
+ upsert?: Validation;
2374
+ delete?: Validation;
2375
+ distinct?: Validation;
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. */
1688
2391
  new?: Validation;
2392
+ /** Base list guard and fallback for both list routes; also governs model read-to-list retries. */
1689
2393
  list?: Validation;
2394
+ /** Base create guard and fallback for both model create routes. */
1690
2395
  create?: Validation;
2396
+ /** Base read guard and fallback for both read variants; advancedRead covers both POST read routes. */
1691
2397
  read?: Validation;
2398
+ /** Base update guard and fallback for both model update routes. */
1692
2399
  update?: Validation;
2400
+ /** Base upsert guard and fallback for both upserts; create/update route variants do not gate its branches. */
1693
2401
  upsert?: Validation;
2402
+ /** DELETE /:id guard; unpaired, with no basic/advanced override. */
1694
2403
  delete?: Validation;
2404
+ /** Base distinct guard and fallback for GET and POST /distinct/:field. */
1695
2405
  distinct?: Validation;
2406
+ /** Base count guard and fallback for GET and POST /count. */
1696
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
+ */
1697
2442
  subs?: Validation | SubRouteGuardOptions;
1698
2443
  }
1699
- type PermissionRule = Validation | OperationAccess;
2444
+ type PermissionRule = Validation | FieldOperationAccess;
2445
+ /** Field grants by base operation. Put basic/advanced route guards in operationAccess, not field-rule objects. */
1700
2446
  type PermissionSchema<TField extends string = string> = Partial<Record<TField, PermissionRule>>;
1701
2447
  interface DocPermissions {
1702
2448
  list?: ModelDocPermissionsHook;
@@ -1714,8 +2460,28 @@ interface DefaultModelRouterOptions<TModel = unknown> {
1714
2460
  parentPath?: string;
1715
2461
  queryRouteSegment?: string;
1716
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
+ */
1717
2471
  operationAccess?: Validation | OperationAccess;
1718
2472
  modelPermissionPrefix?: string;
2473
+ /**
2474
+ * When `true`, list/read/update requests without an explicit `select` return
2475
+ * field-less rows (only `_id`, plus the permission field when requested)
2476
+ * instead of all allowed fields. Applies to plain and advanced PATCH
2477
+ * (returned updated record honors `select`, including `?select=` on plain
2478
+ * PATCH and body `select` on advanced PATCH). Only an omitted (`undefined`)
2479
+ * select triggers this; explicit selects — including backend `defaults` and
2480
+ * an explicitly empty string/array (which keep their current meaning) — are
2481
+ * unaffected. Counts, ordering, includes and permission enforcement are
2482
+ * unchanged. Defaults to `false`.
2483
+ */
2484
+ requireExplicitSelect?: boolean;
1719
2485
  /**
1720
2486
  * Extra fields allowed in `sort` regardless of `list`/`read` field policy.
1721
2487
  * Merged as a union with permission-derived allowed fields (`id`/`_id` are
@@ -1729,6 +2495,25 @@ interface DefaultModelRouterOptions<TModel = unknown> {
1729
2495
  * Defaults to `false` (strict: disallowed sort returns `BadRequest`).
1730
2496
  */
1731
2497
  stripDisallowedSort?: boolean;
2498
+ /**
2499
+ * When `true`, the document permissions field (`documentPermissionField`,
2500
+ * default `_permissions`) is removed from all outputs — including its empty
2501
+ * placeholder — and client `includePermissions`/`includeFieldPermissions`
2502
+ * input (query params, body/root-batch options and service-direct options)
2503
+ * as well as per-operation `defaults` are ignored (both resolve to `false`).
2504
+ * Output matches `includePermissions: false` with the permissions field
2505
+ * removed entirely. Enforcement still runs; only output metadata is
2506
+ * affected. Defaults to `false`.
2507
+ */
2508
+ stripPermissionsField?: boolean;
2509
+ /**
2510
+ * When `true`, client `includeFieldPermissions` input (query params,
2511
+ * body/root-batch options and service-direct options) as well as
2512
+ * per-operation `defaults` are ignored and field maps (`_view`/`_edit`)
2513
+ * are never computed. `docPermissions`-hook keys still follow
2514
+ * `includePermissions`. Defaults to `false`.
2515
+ */
2516
+ disableFieldPermissions?: boolean;
1732
2517
  }
1733
2518
  interface ExtendedDefaultModelRouterOptions<TModel = unknown> extends DefaultModelRouterOptions<TModel> {
1734
2519
  'operationAccess.default'?: Validation;
@@ -1741,19 +2526,92 @@ interface ExtendedDefaultModelRouterOptions<TModel = unknown> extends DefaultMod
1741
2526
  'operationAccess.create'?: Validation;
1742
2527
  'operationAccess.distinct'?: Validation;
1743
2528
  'operationAccess.count'?: Validation;
1744
- 'operationAccess.subs'?: SubRouteGuardOptions;
1745
- }
1746
- interface ModelRouterOptions<TModel = unknown> extends DefaultModelRouterOptions<TModel> {
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> {
1747
2546
  modelName?: string;
1748
2547
  basePath?: string;
1749
2548
  /**
1750
2549
  * Update grants define assignment boundaries: authorized leaves preserve omitted
1751
2550
  * siblings; a whole-object/array grant permits replacement (no generic deep merge).
1752
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
+ * ```
1753
2601
  */
1754
- permissionSchema?: PermissionSchema<AccessRouterFieldKey<TModel>>;
2602
+ virtuals?: ModelVirtuals<TModel, TVirtuals>;
1755
2603
  alwaysSelectFields?: string[];
1756
2604
  docPermissions?: DocPermissions | ModelDocPermissionsHook;
2605
+ /**
2606
+ * Allowlist of `docPermissions`-hook keys exposed in API responses.
2607
+ * Enforcement always uses the full hook map; only serialization is reduced,
2608
+ * after all grant computations and decorate hooks have run. `_view`/`_edit`
2609
+ * are always kept. `undefined` (default) exposes everything (current
2610
+ * behavior); any array (including `[]`) exposes `_view`/`_edit` plus the
2611
+ * listed keys. Note `guard()` conditions on stripped doc-level keys fail
2612
+ * closed — allowlist any key a custom route guard evaluates.
2613
+ */
2614
+ exposedDocPermissionKeys?: string[];
1757
2615
  baseFilter?: ModelBaseFilterHook | Record<string, ModelBaseFilterHook>;
1758
2616
  overrideFilter?: ModelOverrideFilterHook | Record<string, ModelOverrideFilterHook>;
1759
2617
  decorate?: ModelHook<TModel> | Record<string, ModelHook<TModel>>;
@@ -1793,9 +2651,16 @@ interface DataRouterOptions<TData = unknown> {
1793
2651
  resolveIdFilter?: DataIdentifierHook<TData>;
1794
2652
  parentPath?: string;
1795
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
+ */
1796
2660
  operationAccess?: Validation | OperationAccess;
1797
2661
  dataName?: string;
1798
2662
  basePath?: string;
2663
+ /** Base-operation field grants, independent of operationAccess's basic/advanced route guards. */
1799
2664
  permissionSchema?: PermissionSchema<AccessRouterFieldKey<TData>>;
1800
2665
  baseFilter?: DataBaseFilterHook | Record<string, DataBaseFilterHook>;
1801
2666
  overrideFilter?: DataOverrideFilterHook | Record<string, DataOverrideFilterHook>;
@@ -1803,7 +2668,7 @@ interface DataRouterOptions<TData = unknown> {
1803
2668
  decorateAll?: DataListHook<TData> | Record<string, DataListHook<TData>>;
1804
2669
  requestSchemas?: DataRequestSchemas;
1805
2670
  }
1806
- interface ExtendedModelRouterOptions<TModel = unknown> extends ModelRouterOptions<TModel>, ExtendedDefaultModelRouterOptions<TModel> {
2671
+ interface ExtendedModelRouterOptions<TModel = unknown, TVirtuals extends object = Record<never, never>> extends ModelRouterOptions<TModel, TVirtuals>, ExtendedDefaultModelRouterOptions<TModel> {
1807
2672
  'alwaysSelectFields.default'?: string[];
1808
2673
  'alwaysSelectFields.list'?: string[];
1809
2674
  'alwaysSelectFields.create'?: string[];
@@ -1885,8 +2750,29 @@ interface ExtendedModelRouterOptions<TModel = unknown> extends ModelRouterOption
1885
2750
  'defaults.publicReadOptions'?: PublicReadOptions;
1886
2751
  'defaults.publicUpdateArgs'?: PublicUpdateArgs;
1887
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
+ }>;
1888
2767
  }
1889
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;
1890
2776
  'requestSchemas.advancedList'?: RequestSchema;
1891
2777
  'requestSchemas.advancedReadFilter'?: RequestSchema;
1892
2778
  'requestSchemas.advancedRead'?: RequestSchema;
@@ -1993,4 +2879,4 @@ declare function fromIoTs<TValue = unknown>(decoder: IoTsDecoderLike<TValue>): R
1993
2879
  declare function fromSuperstruct<TStruct, TOutput = unknown>(struct: TStruct, validate: SuperstructValidateLike): RequestSchemaValidator<TOutput>;
1994
2880
  declare function fromVine<TValue = unknown>(validator: VineValidatorLike<TValue>): RequestSchemaValidator<TValue>;
1995
2881
 
1996
- 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 PublicReadOptions 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 Filter as a7, FilterOperator as a8, type FindAccess as a9, type ModelDocument as aA, type ModelDocumentHook as aB, type ModelHook as aC, type ModelHookContext as aD, type ModelIdentifierHook as aE, type ModelListHook as aF, type ModelOverrideFilterHook as aG, type ModelRequest as aH, type ModelRouterOptions as aI, type ModelValidateHook as aJ, PERMISSIONS as aK, PERMISSION_KEYS as aL, type ParentRef as aM, type PathValue as aN, type PermissionSchema as aO, type Populate as aP, type PopulateAccess as aQ, type PrepareAccess as aR, type Projection as aS, type PublicCreateArgs as aT, type PublicCreateOptions as aU, type PublicErrorResult as aV, type PublicListArgs as aW, type PublicListOptions as aX, type PublicListResult as aY, type PublicOutput as aZ, type PublicReadArgs as a_, type FindArgs as aa, type FindByIdArgs as ab, type FindByIdOptions as ac, type FindOneArgs as ad, type FindOneOptions as ae, type FindOptions as af, type GlobalOptions as ag, type GlobalPermissionValue as ah, type GuardHook as ai, type IdentifierHook as aj, type Include as ak, type IoTsContextEntryLike as al, type IoTsDecodeErrorLike as am, type IoTsDecoderLike as an, type JoiSchemaLike as ao, type JoiValidationErrorDetailLike as ap, type KeyValueProjection as aq, type LegacyInclude as ar, type ListQueryInput as as, type ListResult as at, MIDDLEWARE as au, type MaybePromise as av, type ModelBaseFilterHook as aw, type ModelChangeHook as ax, type ModelDeleteHook as ay, type ModelDocPermissionsHook as az, type AccessRouterLogger as b, type SubdocumentBulkRecord as b$, type PublicServiceResult as b0, type PublicSingleResult as b1, type PublicUpdateArgs as b2, type PublicUpdateOptions as b3, type PublicUpsertArgs as b4, type PublicUpsertOptions as b5, type ReadQueryInput as b6, type Request as b7, type RequestSchemaAdapter as b8, type RequestSchemaFailure as b9, type RootModelUpdateQueryEntry as bA, type RootModelUpsertQueryEntry as bB, type RootOperationResult as bC, type RootQueryEntry as bD, type RootRouterOptions as bE, type RootSubOperation as bF, type RootTarget as bG, type RouteGuardAccess as bH, type SelectAccess as bI, type SelectedPopulatedPublicOutput as bJ, type SelectedPublicOutput as bK, type ServiceResult as bL, type SingleResult as bM, type Sort as bN, type SortOrder as bO, type StandardSchemaFailure as bP, type StandardSchemaInferOutput as bQ, type StandardSchemaIssue as bR, type StandardSchemaPathSegment as bS, type StandardSchemaResult as bT, type StandardSchemaSuccess as bU, type StandardSchemaV1 as bV, StatusCodes as bW, type SubListBody as bX, type SubPopulate as bY, type SubQueryEntry as bZ, type SubReadBody as b_, type RequestSchemaIssue as ba, type RequestSchemaLike as bb, type RequestSchemaOptions as bc, type RequestSchemaResult as bd, type RequestSchemaSuccess as be, type RequestSchemaValidator as bf, type RequestSchemas as bg, type RootDataListQueryEntry as bh, type RootDataOperation as bi, type RootDataReadByFilterQueryEntry as bj, type RootDataReadByIdQueryEntry as bk, type RootModelCountQueryEntry as bl, type RootModelCreateQueryEntry as bm, type RootModelDeleteQueryEntry as bn, type RootModelDistinctQueryEntry as bo, type RootModelListQueryEntry as bp, type RootModelNewQueryEntry as bq, type RootModelOperation as br, type RootModelReadByFilterQueryEntry as bs, type RootModelReadByIdQueryEntry as bt, type RootModelSubBulkUpdateQueryEntry as bu, type RootModelSubCreateQueryEntry as bv, type RootModelSubDeleteQueryEntry as bw, type RootModelSubListQueryEntry as bx, type RootModelSubReadQueryEntry as by, type RootModelSubUpdateQueryEntry as bz, type AccessRouterRequest as c, type OpenApiResponses as c$, type SubdocumentBulkUpdateInput as c0, type SubdocumentCreateInput as c1, type SubdocumentCreateOptions as c2, type SubdocumentId as c3, type SubdocumentListOptions as c4, type SubdocumentName as c5, type SubdocumentParentArgs as c6, type SubdocumentParentOptions as c7, type SubdocumentReadOptions as c8, type SubdocumentRecord as c9, type YupValidationErrorLike as cA, defineRequestSchema as cB, fromAjv as cC, fromArkType as cD, fromIoTs as cE, fromJoi as cF, fromStandardSchema as cG, fromSuperstruct as cH, fromValibot as cI, fromVine as cJ, fromYup as cK, fromZod as cL, parseBody as cM, parseBodyWithSchema as cN, parseNestedBodyWithSchema as cO, parsePathParam as cP, parseQuery as cQ, AccessRuntime as cR, DataService as cS, Service as cT, PublicService as cU, type OpenApiRouterOptions as cV, type OpenApiRegistryOptions as cW, type OpenApiRouteDescriptor as cX, type OpenApiDocumentOptions as cY, type OpenApiParameter as cZ, type OpenApiSchema as c_, type SuperstructFailureLike as ca, type SuperstructValidateLike as cb, type Task as cc, type TaskType as cd, type TransformAccess as ce, type TypedFilter as cf, type UpdateByIdArgs as cg, type UpdateByIdOptions as ch, type UpdateOneArgs as ci, type UpdateOneOptions as cj, type UpdateQueryInput as ck, type UpsertArgs as cl, type UpsertOptions as cm, type UpsertQueryInput as cn, type ValibotIssueLike as co, type ValibotPathItemLike as cp, type ValibotSafeParseLike as cq, type ValibotSafeParseResult as cr, type ValidateAccess as cs, type ValidateRule as ct, type Validation as cu, type ValidationError as cv, type VineValidationErrorLike as cw, type VineValidationMessageLike as cx, type VineValidatorLike as cy, type YupSchemaLike as cz, type AccessRouterRequestExtensions as d, type AccessRouterPermissionMap as d0, type AccessRouterPermissions as d1, type OpenApiMethod as d2, type Permissions as d3, defaultRuntime as d4, 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 };
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 };