import { z } from 'zod'; import mongoose from 'mongoose'; import express from 'express'; interface BooleanObject { [key: string]: boolean; } interface AccessRouterPermissionMap { } type AccessRouterPermissionKey = Extract; type PermissionName = AccessRouterPermissionKey | (string & {}); declare class Permission { private readonly permissions; private readonly permissionKeys; constructor(permissions: BooleanObject); get keys(): readonly string[]; private normalizePermissionArgs; hasKey(permission: PermissionName): boolean; has(permission: PermissionName): boolean; hasAny(permissionOrPermissions: PermissionName | readonly PermissionName[], ...permissions: PermissionName[]): boolean; hasAll(permissionOrPermissions: PermissionName | readonly PermissionName[], ...permissions: PermissionName[]): boolean; } type Permissions = Permission; type AccessRouterPermissions = Permission; declare class RequestConcurrencyScheduler { readonly limit: number; private activeWork; private readonly waiters; constructor(limit: number); map(items: TInput[], iteratee: (item: TInput, index: number, scheduled: true) => Promise): Promise; run(iteratee: (scheduled: true) => Promise): Promise; work(operation: () => TOutput | PromiseLike): Promise; } /** * Shared hook-orchestration gate for VIRT-03 finalization (VIRT-00A D7). * * Distinct from {@link RequestConcurrencyScheduler.work} (leaf persistence * permits): this gate admits virtual getter bodies only, never persistence * ops. A single gate instance is shared across all rows of a top-level list * so peak active getter work stays at or below the configured limit instead * of multiplying by row count. Callers hold the gate only for the getter body * itself — never while awaiting descendant finalization — so limit-1 * configurations complete without deadlock. FIFO admission keeps completion * order independent of input order; the surrounding row map preserves stable * index-keyed output association regardless of completion order. */ declare class SharedHookGate { readonly limit: number; private active; private readonly waiters; constructor(limit: number); run(operation: () => TOutput | PromiseLike): Promise; } /** Internal effective data policy, including trusted custom accesses; virtual configuration keys stay narrow. */ type VirtualProjectionAccess = 'list' | 'create' | 'read' | 'update' | (string & {}); type VirtualCandidateAuthState = 'granted' | 'deferred'; interface VirtualProjectionSnapshot { permissionSchema?: Record | null; virtuals?: Record | null; /** Raw `alwaysSelectFields` option: array or per-access record. */ alwaysSelectFields?: string[] | Record | null; modelPermissionPrefix?: string; requireExplicitSelect?: boolean; documentPermissionField?: string; } interface PlanVirtualCandidate { name: string; descriptor: { get: (...args: never[]) => unknown; dependsOn: string[]; }; authState: VirtualCandidateAuthState; dependsOn: string[]; virtualAccess: VirtualProjectionAccess; outputAccess: VirtualProjectionAccess; } interface VirtualProjectionPlan { receivingModelName: string; scopePath: string[]; virtualAccess: VirtualProjectionAccess; outputAccess: VirtualProjectionAccess; docPermissionsAccess: VirtualProjectionAccess; selectionMode: 'all' | 'include' | 'exclude'; /** Normalized effective select (empty = omitted/effectively empty). */ effectiveSelectNorm: string[]; /** Persisted output eligibility + virtual names eligible for output. */ outputSelection: string[]; outputPersistedFields: string[]; outputVirtualNames: string[]; /** Safe Mongoose projection: no virtuals, no `.sub` paths, no `-_id`. */ persistedFetchSelection: string[]; candidates: PlanVirtualCandidate[]; virtualOnlyDeps: string[]; internalOnlyFields: string[]; depProvenance: Record; outputIdExcluded: boolean; fetchIdRetained: boolean; internalIdentityFields: string[]; internalJoinFields: string[]; childPlans: Record; relatedPlans: VirtualProjectionPlan[]; } type VirtualFinalizeAccess = VirtualProjectionAccess; interface FinalizeSnapshot extends VirtualProjectionSnapshot { documentPermissionField?: string; exposedDocPermissionKeys?: string[] | undefined; stripPermissionsField?: boolean; disableFieldPermissions?: boolean; } interface FinalizeRelatedTarget { /** Output path in the parent scope (top-level name for v1; dotted supported). */ path: string; plan: VirtualProjectionPlan; snapshot: FinalizeSnapshot; virtualAccess: VirtualFinalizeAccess; outputAccess: VirtualFinalizeAccess; docPermissionsAccess: VirtualFinalizeAccess; } /** * VIRT-04 direct-operation virtual wiring (VIRT-00A D1/D6/D7/D8). * * - Access matrix: list `list/list/list`; read/readFilter `read/read/read` * (read using list fallback becomes `list/list/list` with * `operation: 'read'` preserved); create/create-branch `create/read/create`; * update/update-branch `update/read/update`; new template * `create/create/create` (see `new()` note on ignored `args.select`). * - Effective mutation output selection is carried on the internal plan * into the shared finalizer before evaluation (explicit select, * `returningAll: false` implicits, decorator/task selection preserved * via existing public picks after decorate/tasks). Internal create/update * args omit `select`; `overrides.effectiveSelect` is the deliberate * transport (see `interfaces/service-create.ts`, `service-update.ts`). * - Output-only admission runs before validation/persistence; computed * values are assigned only to output copies (finalizer), never to * persisted docs/snapshots. * - `maxHookConcurrency` bounds active getter + row-finalization work via * ONE bounded row map + shared `SharedHookGate` (stable order, limit-1 * completes, no persistence permits held). */ type VirtualOperationAccess = 'list' | 'create' | 'read' | 'update'; declare class Service> extends Base { protected model: any; protected options: ModelRouterOptions; defaults: Defaults; protected baseFields: string[]; protected baseFieldsExt: string[]; private readonly persist; findRawParentDoc(args: { filter: Filter; select: string; populate: unknown; lean: boolean; }): any; protected createModelAdapter(modelName: string): any; protected getModelRouterOptions(modelName: string): ModelRouterOptions; /** * Router-level permission output postures (see `ModelRouterOptions`). * Absolute: they win over client input and per-operation `defaults`. */ getPermissionsPosture(): { stripPermissionsField: boolean; disableFieldPermissions: boolean; }; /** * Router posture `stripPermissionsField`: drop the whole document * permissions field (including the empty placeholder) from output instead * of emitting permission metadata. Returns true when applied. */ private stripPermissionsOutput; private asServiceHookContext; private getEffectiveAllowedSortFields; /** * With `requireExplicitSelect`, an omitted client select behaves as an * `_id`-only projection, yielding field-less rows. Explicit selects pass * through untouched. */ protected resolveEffectiveSelect(select: Projection | undefined): Projection | undefined; /** * Capture a coherent virtual snapshot for an operation (VIRT-00A D1/D2, * VIRT-01 copy-on-write). Planner and finalizer use this same object for * the operation so in-flight descriptor replacement cannot mix versions. * Public for VIRT-06 subdocument routes (same coherence per sub op). */ captureVirtualSnapshot(): FinalizeSnapshot; getVirtualGlobalPermissions(): { has: (key: string) => boolean; hasKey: (key: string) => boolean; }; getVirtualHookConcurrencyLimit(): number; buildVirtualPlan(args: { snapshot: FinalizeSnapshot; virtualAccess: VirtualOperationAccess; outputAccess: VirtualOperationAccess; docPermissionsAccess: VirtualOperationAccess; requestedSelect?: Projection; effectiveSelect?: Projection; internalFetch?: { baseFields?: string[]; joinFields?: string[]; identityFields?: string[]; extraPersistedFields?: string[]; trustedOverrideSelect?: Projection; }; }): VirtualProjectionPlan; /** * VIRT-06 sub-scope planner. * * Builds a `planVirtualProjection` with `scopePath=[sub,'sub']` for * dedicated subdocument routes (`virtuals..sub..`). * `dependsOn` resolves relative to that scope (VIRT-00A D5); literal * `.sub` segments never become DB/response names (planner strips them). * Uses the same coherent `snapshot` captured once per sub op. */ buildSubVirtualPlan(sub: string, snapshot: FinalizeSnapshot, virtualAccess: VirtualOperationAccess, outputAccess: VirtualOperationAccess, docPermissionsAccess: VirtualOperationAccess, requestedSelect?: Projection, effectiveSelect?: Projection): VirtualProjectionPlan; /** * VIRT-06 internal accessors for dedicated subdocument routes. * `model-subdocument-service` uses these instead of reaching into * protected `req`/`modelName`/`model`, preserving raw docs for * persistence (never substituting trimmed DTOs) and keeping request * ownership inside `Service`. */ getServiceRequest(): ModelRequest; getServiceModelName(): string; getServiceMongooseModel(): unknown; /** * VIRT-06 owning-parent internal grants (VIRT-00A D2). * * Dedicated subdocument routes reuse the owning parent's internally * computed `read` grants with scoped `permissionSchema..sub` rules; * v1 adds NO independent embedded `docPermissions` hook family. `operation` * stays the initiating sub op (`subList`/`subRead`/`subCreate`/`subUpdate`/ * `subBulkUpdate`) for logs, while `docPermissionsAccess` is `read`. * Never throws: hook failure yields `{}` (fail-closed downstream). */ getSubParentGrants(parentDoc: unknown, operation: string): Promise>; /** * VIRT-06 bounded sub-row finalization (VIRT-00A D7, VIRT-03 strategy). * * Finalizes already-filtered (visible-only) sub rows through the shared * boundary with scope ` [sub,'sub']`, owning-parent `read` grants, and * scoped rules. Children (deeper nested + related via `related`) finalize * before sub getters; only finalized children appear in getter views. * Uses ONE bounded row map + shared `SharedHookGate` (stable order, * peak ≤ `maxHookConcurrency`, limit-1 completes, no persistence permits * held). Callers must filter rows (existing row/operation filters) BEFORE * calling; hidden rows never reach getters. Never mutates inputs. */ finalizeSubRowsWithVirtuals(args: { sub: string; rows: unknown[]; plan: VirtualProjectionPlan; snapshot: FinalizeSnapshot; parentGrants: Record; virtualAccess: VirtualOperationAccess; outputAccess: VirtualOperationAccess; docPermissionsAccess: VirtualOperationAccess; operation: string; related?: FinalizeRelatedTarget[]; }): Promise[]>; /** * VIRT-06 single sub-row finalization (same boundary/bounds as list). * Returns `null` when input is nullish (caller maps to `null` for * denied-post-persist single visibility, never a retryable error). */ finalizeSingleSubRowWithVirtuals(args: { sub: string; row: unknown; plan: VirtualProjectionPlan; snapshot: FinalizeSnapshot; parentGrants: Record; virtualAccess: VirtualOperationAccess; outputAccess: VirtualOperationAccess; docPermissionsAccess: VirtualOperationAccess; operation: string; related?: FinalizeRelatedTarget[]; }): Promise | null>; /** * VIRT-06 populate finalization for sub parents (VIRT-05 path). * * After Mongoose `populate` resolves on the raw parent (e.g. * `contacts.friend` via `genSubPopulate`), finalize each actual target * with its own plan/context before sub getters. Handles null/scalar/array * + dotted through embedded arrays; trims even without virtuals. Bounded * via shared gate; limit-1 completes. No-op when no entries. */ finalizePopulateForSubParent(parentDoc: unknown, populateEntries: Populate[], parentOperation: string, hookLimit?: number, sharedGate?: SharedHookGate): Promise; /** * Output-only write admission (VIRT-04 requirement 4). Excludes registered * virtual names for any access (including bare `true` rules and inactive * descriptors) and sanitizes registered embedded virtual keys inside * whole-object/array grants. Trusted prepare/transform authority is * preserved (this runs on client data before validation only); computed * values are assigned only to output copies by the finalizer. */ private admitWriteDataForVirtuals; private filterAllowedFieldsForVirtuals; private stripVirtualsFromFilterForSnapshot; /** * Preserve the `permissions` Mongoose virtual (from `permissionsPlugin`) for * hydrated outputs. `isolateDocForOutput` uses `toObject({ virtuals: false })` * so Mongoose getter virtuals never leak into package-virtual planning; * `lean: true` therefore drops them (explicit parity rule). For `lean: false` * (hydrated Mongoose documents) the historical contract exposes * `doc.permissions` as an alias of the document-permission field. The shared * finalizer keeps only the document-permission field, so restore the alias * here for backward compatibility when the input was hydrated and the * virtual existed. Lean outputs stay without the virtual. Models without * the plugin never had the virtual and stay unchanged. */ private preservePermissionsVirtual; /** * VIRT-05 populate + association helpers. * * - Populate targets finalize with their own plan/context before parent * getters (target trimming even without virtuals). Handles null, scalar * IDs when population is skipped, arrays, and dotted paths through * embedded arrays (e.g. `contacts.friend`). One evaluation per logical * target output, no in-place mutation of shared/cached instances (targets * are replaced, never mutated; parents are fresh per-query docs, consistent * with `includeDocs` in-place attachment), isolated copies per parent, * bounded via the shared `SharedHookGate` + `mapWithConcurrencyLimit` * (no unbounded fan-out, limit-1 completes, no persistence permits held). * - Association transport stays service-only (symbols, outside serializers * and finalized output input): `find` fetches `foreignField` fetch-only, * captures immutable values before trim, and returns them aligned to rows. */ private getAssociationFieldFromOptions; private buildDbSelectForAssociation; private captureAssociationValues; private captureTargetSnapshotForPopulate; private resolvePopulateMeta; private isScalarPopulateValue; private finalizeSinglePopulateTarget; private finalizePopulatePath; private finalizePopulateForSingleDoc; private beginOp; private completeOp; constructor(req: ModelRequest, modelName: string); findOne(filter: Filter, args?: FindOneArgs, options?: FindOneOptions): Promise | ErrorResult>; findById(id: string, args?: FindByIdArgs, options?: FindByIdOptions): Promise | ErrorResult>; find(filter: Filter, args?: FindArgs, options?: FindOptions, decorate?: (doc: unknown, context?: ModelHookContext) => unknown): Promise | ErrorResult>; create(data: Record | Record[], args?: CreateArgs, options?: CreateOptions, decorate?: (doc: unknown, context?: ModelHookContext) => unknown): Promise | ErrorResult>; private formatBulkValidationIssue; new(args?: { select?: string[]; }, options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; }): Promise>; /** * Update at authorized policy paths: admitted leaves preserve omitted siblings; * whole-authorized objects/arrays replace. Shared by updateById and existing-row * upsert. Validation precedes trusted prepare output (not re-filtered); partial * prepare containers preserve omissions. Transform retains explicit document-setter * replacement authority. Send nested client JSON, not literal dotted update keys. */ updateOne(filter: Filter, data: Record, args?: UpdateOneArgs, options?: UpdateOneOptions, decorate?: (doc: unknown, context?: ModelHookContext) => unknown): Promise | ErrorResult>; updateById(id: string, data: Record, args?: UpdateByIdArgs, options?: UpdateByIdOptions, decorate?: (doc: unknown, context?: ModelHookContext) => unknown): Promise | ErrorResult>; upsert(filter: Filter, data: Record, args?: UpsertArgs, options?: UpsertOptions, decorate?: (doc: unknown, context?: ModelHookContext) => unknown): Promise>; delete(id: string): Promise | ErrorResult>; /** * Check existence under row policy (read by default). Terminal false returns a * Forbidden ErrorResult before adapter dispatch, in both includeId modes. * Allowed matches return true or an `{ _id }` record; misses return false or null. * Custom HTTP routes must map ErrorResult to their intended response status. */ exists(filter: Filter, options: ExistsOptions & { includeId: true; }): Promise | ErrorResult>; /** Boolean existence; terminal false policy returns Forbidden, not successful false. */ exists(filter: Filter, options?: ExistsOptions): Promise | ErrorResult>; protected isValidDistinctFieldName(field: unknown): boolean; protected authorizeDistinctField(field: string): Promise; distinct(field: string, args?: DistinctArgs): Promise | ErrorResult>; count(filter: Filter, access?: BaseFilterAccess): Promise | ErrorResult>; /** * Trusted count for correlated includes (ACI-03): the caller already * resolved parent references, revalidated expanded operands, and applied * explicit `count` access via `genFilter`. This skips client-filter * validation and subquery parsing so substituted parent data stays inert. */ countTrusted(authorizedFilter: Filter): Promise | ErrorResult>; /** * Group target document IDs by original parent-key aliases using one aggregate. * Resolves the requested row policy (legacy count includes pass `count`), casts * authorized filters/foreign operands with the target schema, and deduplicates IDs * independently of pagination. Cast failures return BadRequest; false policy returns * Forbidden. Casting does not execute countDocuments middleware/plugins. */ countByFieldValues(foreignField: string, values: unknown[], filter?: Filter, access?: BaseFilterAccess): Promise>> | ErrorResult>; getDocPermissions(doc: unknown): Record; private resolveFindOneArgs; private resolveFindOneOptions; private resolveFindByIdArgs; private resolveFindByIdOptions; private resolveFindArgs; private resolveFindOptions; private resolveCreateArgs; private resolveCreateOptions; private resolveUpdateOneArgs; private resolveUpdateOneOptions; private resolveUpdateByIdArgs; private resolveUpdateByIdOptions; private resolveUpsertArgs; private resolveUpsertOptions; private resolveExistsOptions; private getFieldPermissionAccess; private getAccessibleIdSet; listSub(id: SubdocumentId, sub: SubdocumentName, options?: SubdocumentListOptions): Promise; readSub(id: SubdocumentId, sub: SubdocumentName, subId: SubdocumentId, options?: SubdocumentReadOptions): Promise; /** * Apply authorized policy-path updates, preserving protected siblings, then save. * Output requires post-save parent read and subdocument read operation/row access; * hidden output is successful `data: null` (direct/root-entry 200), not a failed write. * Visible output uses read fields plus `_id`. Do not retry solely for null output. */ updateSub(id: SubdocumentId, sub: SubdocumentName, subId: SubdocumentId, data: Record): Promise; /** * Save authorized targeted updates, then return readable targets in stored order. * Requires parent/subdocument read access for output, not subdocument list access; * uses read fields plus `_id`. Hidden output succeeds with `data: [], count: 0` * (direct/root-entry 200); count measures visible rows, not all updated rows. */ bulkUpdateSub(id: SubdocumentId, sub: SubdocumentName, data: SubdocumentBulkUpdateInput | Record): Promise; /** * Append/prepend admitted rows, then return the visible full array (including when * input is empty). Output requires post-save parent read and subdocument list AND * read guards/row filters; fields use read projection plus `_id`. * Hidden output succeeds with code `created`, `data: [], count: 0` * (direct/root-entry 201). Visible count/order follow filtered stored rows. * Empty output does not mean the write failed; addFirst is a service option. */ createSub(id: SubdocumentId, sub: SubdocumentName, data: SubdocumentCreateInput, options?: SubdocumentCreateOptions): Promise; deleteSub(id: SubdocumentId, sub: SubdocumentName, subId: SubdocumentId): Promise; getParentDoc(id: SubdocumentId, sub: SubdocumentName, args?: SubdocumentParentArgs, options?: SubdocumentParentOptions): Promise; } declare class PublicService> extends Service { _list(filter: Filter, args?: Omit & { select?: TSelect; populate?: TPopulate; }, options?: PublicListOptions): Promise> | ErrorResult>; _create(data: unknown, args?: Omit & { select?: TSelect; populate?: TPopulate; }, options?: PublicCreateOptions): Promise> | ErrorResult>; _new(args?: { select?: string[]; }, options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; }): Promise>>; _read(id: string, args?: Omit & { select?: TSelect; populate?: TPopulate; }, options?: PublicReadOptions): Promise> | ErrorResult>; _readFilter(filter: Filter, args?: Omit & { select?: TSelect; populate?: TPopulate; sort?: Sort; }, options?: PublicReadOptions): Promise> | ErrorResult>; _update(id: string, data: Record, args?: Omit & { select?: TSelect; populate?: TPopulate; }, options?: PublicUpdateOptions): Promise> | ErrorResult>; /** * With an _id, check update-policy existence then update: terminal denial returns * Forbidden, an allowed miss remains Unauthorized, and neither creates a row. * Without an _id, use the create path. * * VIRT-04 D1/D6: both branches keep `operation: 'upsert'` while accesses * follow the taken branch (update/read/update vs create/read/create). * Effective selection is carried via internal `overrides.effectiveSelect` * before evaluation; presentation picks after decorate/tasks are preserved. * Internal `updateById`/`create` are called directly (not via `_update`/ * `_create`) so the initiating operation is not overwritten with the * branch name. */ _upsert(data: Record, args?: Omit & { select?: TSelect; populate?: TPopulate; }, options?: PublicUpsertOptions): Promise> | ErrorResult>; _delete(id: string): Promise | ErrorResult>; _distinct(field: string, options?: DistinctArgs): Promise | ErrorResult>; _count(filter: Filter, access?: BaseFilterAccess): Promise | ErrorResult>; private resolvePublicListArgs; private resolvePublicListOptions; private resolvePublicCreateArgs; private resolvePublicCreateOptions; private resolvePublicReadArgs; private resolvePublicReadFilterArgs; private resolvePublicReadOptions; private resolvePublicUpdateArgs; private resolvePublicUpdateOptions; } type ValidationError = { detail: string; pointer?: string; parameter?: string; }; type RequestSchemaIssue = { message: string; path?: Array; }; type RequestSchemaSuccess = { success: true; data: T; }; type RequestSchemaFailure = { success: false; issues: RequestSchemaIssue[]; }; type RequestSchemaResult = RequestSchemaSuccess | RequestSchemaFailure; type RequestSchemaValidator = (value: unknown) => RequestSchemaResult | Promise>; type RequestSchemaAdapter = { validate: RequestSchemaValidator; openapi?: Record; }; type RequestSchemaOptions = { openapi?: Record; }; type StandardSchemaPathSegment = { key: PropertyKey; }; type StandardSchemaIssue = { message: string; path?: ReadonlyArray; }; type StandardSchemaSuccess = { value: T; issues?: undefined; }; type StandardSchemaFailure = { issues: ReadonlyArray; }; type StandardSchemaResult = StandardSchemaSuccess | StandardSchemaFailure; type StandardSchemaV1 = { readonly '~standard': { readonly version: 1; readonly vendor: string; readonly validate: (value: unknown, options?: { readonly libraryOptions?: Record | undefined; }) => StandardSchemaResult | Promise>; readonly types?: { readonly input: Input; readonly output: Output; }; }; }; type StandardSchemaInferOutput = NonNullable['output']; type RequestSchemaLike = RequestSchemaValidator | RequestSchemaAdapter | StandardSchemaV1; type YupValidationErrorLike = { name: 'ValidationError'; message: string; errors: ReadonlyArray; path?: string; inner?: ReadonlyArray; }; type YupSchemaLike = { validate: (value: unknown, options?: { abortEarly?: boolean; stripUnknown?: boolean; }) => Promise | T; }; type JoiValidationErrorDetailLike = { message: string; path?: ReadonlyArray; }; type JoiSchemaLike = { validate: (value: unknown, options?: { abortEarly?: boolean; stripUnknown?: boolean; }) => { value: T; error?: { details?: ReadonlyArray; }; } | Promise<{ value: T; error?: { details?: ReadonlyArray; }; }>; }; type AjvErrorObjectLike = { message?: string; instancePath?: string; schemaPath?: string; params?: { missingProperty?: string; }; }; type AjvValidationErrorLike = { ajv: true; validation: true; errors: ReadonlyArray; }; /** * Structural AJV contract; AJV is not a runtime dependency of access-router. * Sync validators return boolean verdicts and expose mutable errors, snapshotted * in the same turn. Tagged `$async: true` validators resolve with data (even * false/true/null), or reject with AJV ValidationError diagnostics. * * Untagged promises/thenables are unsupported. Migrate async boolean-verdict * wrappers to RequestSchemaValidator, or return data/reject validation errors * and retain the literal `$async: true` tag. Real AJV ValidateFunction and * AsyncValidateFunction overloads infer output via their type guard without casts; a synchronous * structural validator's output type can be supplied explicitly to fromAjv. * AJV can type inline async schemas through a sync overload; an AsyncSchema-typed * schema selects its async compile/compileAsync overload. Runtime uses the tag. */ type AjvValidatorLike = ({ (value: unknown): boolean; $async?: false; } | { (value: unknown): PromiseLike; $async: true; }) & { errors?: ReadonlyArray | null; }; type ValibotPathItemLike = { key?: unknown; }; type ValibotIssueLike = { message: string; path?: ReadonlyArray; }; type ValibotSafeParseResult = { success: true; output: T; issues?: undefined; } | { success: false; output?: undefined; issues: ReadonlyArray; }; type ValibotSafeParseLike = (schema: TSchema, value: unknown, config?: { abortEarly?: boolean; }) => ValibotSafeParseResult | Promise>; type ArkTypeProblemLike = { path?: ReadonlyArray; message?: string; problem?: string; }; type ArkTypeErrorsLike = ReadonlyArray & { summary?: string; readonly arkKind?: string; readonly ' arkKind'?: string; }; type ArkTypeLike = { (value: unknown): T | ArkTypeErrorsLike | Promise; errors?: unknown; readonly '~standard'?: StandardSchemaV1['~standard']; }; type IoTsContextEntryLike = { key: string; }; type IoTsDecodeErrorLike = { message?: string; context: ReadonlyArray; }; type IoTsDecoderLike = { decode: (value: unknown) => { _tag: 'Left'; left: ReadonlyArray; } | { _tag: 'Right'; right: T; }; }; type SuperstructFailureLike = { message?: string; path?: ReadonlyArray; key?: string | number; branch?: ReadonlyArray; failures?: () => ReadonlyArray; }; type SuperstructValidateLike = (value: unknown, struct: TStruct) => readonly [failure: SuperstructFailureLike, value: undefined] | readonly [failure: undefined, value: TOutput] | Promise; type VineValidationMessageLike = { field: string; message: string; index?: number; }; type VineValidationErrorLike = { name?: string; code?: string; status?: number; messages: ReadonlyArray; }; type VineValidatorLike = { validate: (value: unknown) => Promise | T; }; type ListQueryInput = { skip?: string; limit?: string; page?: string; page_size?: string; skim?: 'true' | 'false'; include_permissions?: 'true' | 'false'; include_field_permissions?: 'true' | 'false'; include_count?: 'true' | 'false'; include_extra_headers?: 'true' | 'false'; }; type CreateQueryInput = { include_permissions?: 'true' | 'false'; include_field_permissions?: 'true' | 'false'; }; type ReadQueryInput = { include_permissions?: 'true' | 'false'; include_field_permissions?: 'true' | 'false'; try_list?: 'true' | 'false'; }; type UpdateQueryInput = { returning_all?: 'true' | 'false'; include_permissions?: 'true' | 'false'; include_field_permissions?: 'true' | 'false'; }; type UpsertQueryInput = { returning_all?: 'true' | 'false'; include_permissions?: 'true' | 'false'; include_field_permissions?: 'true' | 'false'; }; type AdvancedListBody = { filter?: Filter | unknown[]; select?: Projection; sort?: Sort; populate?: Populate[] | string; include?: Include | Include[]; tasks?: Task | Task[]; skip?: string | number; limit?: string | number; page?: string | number; pageSize?: string | number; options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; includeCount?: boolean; includeExtraHeaders?: boolean; populateAccess?: PopulateAccess; }; }; type CountBody = { filter?: Filter | unknown[]; }; type AdvancedReadFilterBody = { filter?: Filter | unknown[]; select?: Projection; sort?: Sort; populate?: Populate[] | string; include?: Include | Include[]; tasks?: Task | Task[]; options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; tryList?: boolean; populateAccess?: PopulateAccess; }; }; type AdvancedReadBody = { select?: Projection; populate?: Populate[] | string; include?: Include | Include[]; tasks?: Task | Task[]; options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; tryList?: boolean; populateAccess?: PopulateAccess; }; }; type AdvancedCreateBody = { data: unknown; select?: Projection; populate?: Populate[] | string; tasks?: Task | Task[]; options?: { includePermissions?: boolean; includeFieldPermissions?: boolean; populateAccess?: PopulateAccess; }; }; type AdvancedUpdateBody = { data: unknown; select?: Projection; populate?: Populate[] | string; tasks?: Task | Task[]; options?: { returningAll?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; populateAccess?: PopulateAccess; }; }; type AdvancedUpsertBody = { data: Record; select?: Projection; populate?: Populate[] | string; tasks?: Task | Task[]; options?: { returningAll?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; populateAccess?: PopulateAccess; }; }; type DistinctBody = { filter?: Filter | unknown[]; }; type SubListBody = { filter?: Filter; select?: string[]; }; type SubReadBody = { select?: string[]; populate?: SubPopulate | SubPopulate[] | string | string[]; }; interface RequestComplexityOptions { maxDepth?: number; maxNodes?: number; maxLogicalClauses?: number; maxInValues?: number; maxBulkItems?: number; maxIncludeCount?: number; maxSubQueryCount?: number; /** * Defaults to 10. Shared request-and-runtime-owned ceiling on awaited model-service * adapter/document persistence operations, including nested includes/root entries. * Recursive orchestration holds no permit; internal map/run bounds are per call. * One operation may issue multiple driver commands via Mongoose middleware/populate; * direct DB/network calls from trusted application hooks are outside this ceiling. * Not a process-wide connection limit or protection against competing edits. */ maxBulkConcurrency?: number; /** * Defaults to 10. Per-request/per-runtime ceiling on concurrently awaited * virtual-getter + row-finalization orchestration work (VIRT-00A D7), NOT a * per-row multiplier and NOT leaf persistence admission. Top-level lists * finalize rows through ONE bounded map with a shared hook gate held only * for getter bodies (stable index-keyed order regardless of completion * order), so peak active getter work stays at or below this limit — never * `rows × getters`. Nested include/populate/embedded work uses bounded * child maps with a finite bound derived from the same limit; recursion * depth is additionally bounded by `maxCorrelatedDepth` and the correlated * budgets. Recursive orchestration never holds leaf persistence permits * (`RequestConcurrencyScheduler.work`, which admits only leaf * adapter/document persistence ops) while awaiting descendants or hooks, * so limit-1 configurations complete. Trusted direct DB/network I/O issued * by virtual getters themselves runs outside the leaf persistence ceiling * (no dataloader/batching in v1). Not a process-wide connection limit. */ maxHookConcurrency?: number; /** Cumulative correlated target executions per request/runtime (default 100); permit release does not refund this budget. */ maxCorrelatedQueries?: number; /** Correlated template nesting bound (default 5), independent of persistence concurrency. */ maxCorrelatedDepth?: number; } type OpenApiMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; type OpenApiSchema = Record; type OpenApiSchemaSource = unknown; type OpenApiParameter = { name: string; in: 'path' | 'query'; required?: boolean; description?: string; schema?: OpenApiSchema; }; type OpenApiResponse = { description: string; content?: Record; }; type OpenApiResponses = Record; type OpenApiRouteDescriptor = { method: OpenApiMethod; path: string; operationId?: string; summary?: string; description?: string; tags?: string[]; acl?: string; deprecated?: boolean; query?: OpenApiSchemaSource; body?: OpenApiSchemaSource; pathParams?: Record; responses?: OpenApiResponses; /** * When true, replaces an existing registration with the same method/path * even when the descriptors differ. When false (default), conflicting * registrations throw at registration time so consumers can detect * accidental route shadowing. */ allowReplace?: boolean; /** * When true, this exact descriptor is idempotent and registering the same * method/path with an equivalent descriptor again is silently accepted. * Differing re-registrations still throw unless {@link allowReplace} is set. * Additionally, an idempotent descriptor may share its operationId with * another idempotent descriptor mounted at a different path on the same * registry, so reserved operations (e.g. the root batch route) can be * exposed from multiple base paths without triggering strict-mode * duplicate-operationId rejection. */ idempotent?: boolean; }; type OpenApiRegistryOptions = { /** * Throw when the same method/path is registered twice with differing * descriptors, instead of silently replacing. Default: true (strict). * The {@link AccessRuntime} registry is constructed in strict mode by * default; construct {@link OpenApiRegistry} directly with * `rejectConflicts: false` for legacy replacement semantics. */ rejectConflicts?: boolean; /** * Throw when the same operationId is registered more than once, including * when {@link OpenApiRouteDescriptor.allowReplace} is set. Default: true. */ rejectDuplicateOperationIds?: boolean; }; type OpenApiDocumentOptions = { title: string; version: string; description?: string; servers?: Array<{ url: string; description?: string; }>; }; type OpenApiRouterOptions = Partial & { jsonPath?: string; docsPath?: string | false; swaggerUiCssUrl?: string; swaggerUiBundleUrl?: string; }; type ModelReferenceMap = { [key: string]: string | ModelReferenceMap; }; declare class AccessRuntime { private readonly allowGlobalModelLookup; constructor(options?: { allowGlobalModelLookup?: boolean; }); private readonly globalOptions; private readonly defaultModelOptions; private readonly modelOptions; private readonly modelJsonSchemas; private readonly dataOptions; private readonly modelRefs; private readonly modelSubs; private readonly modelAtts; private readonly modelInstances; private readonly openApiRegistry; private openApiPathPrefix; registerModelInstance(modelName: string, model: mongoose.Model): void; hasModelInstance(modelName: string): boolean; getModelInstance(modelName: string): mongoose.Model | null; registerOpenApiRoute(route: OpenApiRouteDescriptor): void; /** * Compose OpenAPI paths while synchronously constructing mounted routers. * Prefixes apply before collision checks; prior descriptors and Express paths * are unchanged. Nested calls compose and the prior scope is restored even on * failure. The callback must finish registration synchronously (no async work). */ withOpenApiPathPrefix(prefix: string, build: () => T): T; /** * Enable strict OpenAPI collision detection for the remainder of this * runtime's lifetime. After this call, conflicting method/path * registrations and duplicate operationIds throw {@link OpenApiCollisionError} * instead of silently replacing. Existing already-registered routes are * not retroactively re-validated. */ enableOpenApiCollisionDetection(): void; getOpenApiRoutes(): OpenApiRouteDescriptor[]; clearOpenApiRoutes(): void; getOpenApiSpec(info: OpenApiDocumentOptions): { openapi: string; info: Omit; servers?: OpenApiDocumentOptions["servers"]; paths: Record; }; responses: OpenApiResponses; }>>; }; createBootstrapSnapshot(): { globalOptions: GlobalOptions; defaultModelOptions: DefaultModelRouterOptions; modelOptions: Record; dataOptions: Record; modelInstances: Record>; modelJsonSchemas: Record>; modelRefs: Record; modelSubs: Record; modelAtts: Record; openApi: { routes: OpenApiRouteDescriptor[]; options: Required; }; }; restoreBootstrapSnapshot(snapshot: ReturnType): void; setGlobalOptions(options: GlobalOptions): void; setGlobalOption(key: K, value: GlobalOptions[K]): void; getGlobalOptions(): GlobalOptions; getGlobalOption(key: K, defaultValue?: GlobalOptions[K]): GlobalOptions[K]; setDefaultModelOptions(options: DefaultModelRouterOptions): void; setDefaultModelOption(key: K, value: ExtendedDefaultModelRouterOptions[K]): void; getDefaultModelOptions(): DefaultModelRouterOptions; getDefaultModelOption(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions[K]; private createModelOptions; private getOrCreateModelOptions; setModelOptions>(modelName: string, options: ModelRouterOptions): void; setModelOption, TModel = unknown, TVirtuals extends object = Record>(modelName: string, key: K, value: ExtendedModelRouterOptions[K]): void; getModelOptions>(modelName: string): ModelRouterOptions; getModelOption, TModel = unknown, TVirtuals extends object = Record>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions[K]): ExtendedModelRouterOptions[K]; getExactModelOption, TModel = unknown, TVirtuals extends object = Record>(modelName: string, key: K | string): ExtendedModelRouterOptions[K]; /** * List registered virtual leaf names for a scope (VIRT-01). * Scope containers (`{ sub }`) are not leaves. Inapplicable names are * still listed here; applicability is decided per-access by * {@link resolveVirtualDescriptor}. Request-controlled include collisions * are validated by VIRT-05, not guessed here. */ getVirtualNames(modelName: string, scopePath?: string[]): string[]; /** * Whether a field is a registered virtual leaf in a scope (VIRT-01). * Inapplicable registered names stay virtual (never persisted), even when * no getter applies for the current access. Containers and persisted * fields return false. */ isVirtualField(modelName: string, fieldName: string, scopePath?: string[]): boolean; /** * Resolve an applicable virtual descriptor with exact → `.default` → bare * semantics (VIRT-01, VIRT-00A D1/D5). Returns `undefined` when no getter * applies for `access`. Crucially, an access-record object is NEVER * interpreted as a getter descriptor when the access is absent: an * inapplicable registered name stays virtual (excluded from persisted * projections, write admission, and DB sort/filter/distinct). * Shared record-level `dependsOn` is inherited when a leaf lacks its own. */ resolveVirtualDescriptor(modelName: string, virtualName: string, access: string, scopePath?: string[]): { get: (...args: never[]) => unknown; dependsOn: string[]; } | undefined; getModelNames(): string[]; hasModel(modelName: string): boolean; getModelJsonSchema(modelName: string): Record; private createDataOptions; private getOrCreateDataOptions; setDataOptions(dataName: string, options: DataRouterOptions): void; setDataOption, TData = unknown>(dataName: string, key: K, value: ExtendedDataRouterOptions[K]): void; getDataOptions(dataName: string): DataRouterOptions; getDataSnapshot(dataName: string): readonly TData[]; getDataOption, TData = unknown>(dataName: string, key: K | string, defaultValue?: ExtendedDataRouterOptions[K]): ExtendedDataRouterOptions[K]; getExactDataOption, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions[K]; getDataNames(): string[]; ensureModelMeta(modelName: string): void; getModelRef(modelName: string, refPath: string): string | null; getModelSub(modelName: string): string[]; getModelAtt(modelName: string): string[]; } declare const defaultRuntime: AccessRuntime; type CrossResourceModelOperation = 'list' | 'read' | 'count'; interface CorrelatedExecState { totalQueries: number; scheduler: RequestConcurrencyScheduler; } declare class Base> { protected req: ModelRequest; protected modelName: string; protected readonly requestRuntime: AccessRuntime; constructor(req: ModelRequest, modelName: string); decorate(doc: T, access: DecorateAccess, context: ModelHookContext): Promise; decorateAll(docs: T[], access: DecorateAllAccess, context: ModelHookContext): Promise; genAllowedFields(doc: unknown, access: SelectAccess, baseFields?: string[]): Promise; genDocPermissions(doc: unknown, access: DocPermissionsAccess, context: ModelHookContext): Promise>; genFilter(access?: BaseFilterAccess, filter?: Filter): Promise>; getIdentifier(): string | null; genIDFilter(id: string): Promise>; genPopulate(access?: SelectAccess, populate?: Populate | Populate[] | string | null, subPaths?: string[]): Promise; genSelect(access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; genQuerySelect(access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; addEmptyPermissions(doc: T): T; addDocPermissions(doc: T, access: DocPermissionsAccess, context: ModelHookContext): Promise; addFieldPermissions(doc: T, access: DocPermissionsAccess, context: ModelHookContext): Promise; pickAllowedFields(doc: T, access: SelectAccess, baseFields?: string[]): Promise; trimOutputFields(doc: T, access: SelectAccess, baseFields?: string[]): Promise; prepare(allowedData: T, access: PrepareAccess, context: ModelHookContext): Promise; runTasks(docObject: T, tasks: Task | Task[]): T; transform(doc: T, access: TransformAccess, context: ModelHookContext): Promise; afterPersist(doc: T, access: AfterPersistAccess, context: ModelHookContext): Promise; changes(doc: Record, context: ModelHookContext): Promise; beforeDelete(doc: T, context: ModelHookContext): Promise; afterDelete(doc: T, context: ModelHookContext): Promise; validate(allowedData: unknown, access: ValidateAccess, context: ModelHookContext): Promise; checkIfModelPermissionExists(accesses: DocPermissionsAccess[]): boolean; protected validateClientFilter(filter: Filter | null | undefined): string[]; getRequestComplexity(): Required; protected getClientRequestErrorResult(error: unknown): ErrorResult | null; protected throwClientRequestError(code: ErrorResult['code'], detail: string): never; protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation): Promise>>; protected processInclude(include: Include | Include[]): { includes: LegacyInclude[]; correlatedIncludes: CorrelatedInclude[]; includeLocalFields: string[]; includePaths: string[]; correlatedReferenceFields: string[]; }; private validateIncludeOutputPath; /** * VIRT-05 preflight: request include output paths vs registered virtual * paths with ancestor/descendant checks per receiving scope, before any * target dispatch. Registered virtual names remain virtual even when no * getter applies; an include that would overwrite a virtual (equal, * ancestor, or descendant, including bracket equivalents) fails closed * with `BadRequest`. Ordinary non-virtual collisions stay permitted and * existing permission-metadata protections above are preserved. */ private collectVirtualDbPathsForPreflight; private validateIncludeVirtualCollision; private assertIncludeOutputPaths; private getForeignKeyValues; private getForeignKey; private buildForeignKeyIndex; private getIndexedMatches; private getIndexedCount; private sanitizeIncludeArgs; private assertIncludeForeignField; protected includeDocs(docs: TDoc | TDoc[], include: Include | Include[]): Promise; private includeDocsRead; private includeDocsList; private includeDocsCount; protected getCorrelatedExecState(): CorrelatedExecState; protected claimCorrelatedQuerySlot(): void; private sanitizeCorrelatedArgs; protected includeCorrelatedDocs(docs: TDoc[], correlatedIncludes: CorrelatedInclude[], snapshots?: unknown[], depth?: number): Promise; private includeCorrelatedRead; private includeCorrelatedList; private includeCorrelatedCount; protected parseClientData(filter: TValue, scheduler?: RequestConcurrencyScheduler, scheduled?: boolean): Promise; private handleSubQuery; private handleDate; } declare class DataService { protected req: DataRequest; protected dataName: string; protected options: DataRouterOptions; protected data: T[]; constructor(req: DataRequest, dataName: string); findOne(filter: DataFilter, args?: DataFindOneArgs, options?: DataFindOneOptions): Promise> | ErrorResult>; findById(id: string, args?: DataFindOneArgs, options?: DataFindOneOptions): Promise> | ErrorResult>; find(filter: DataFilter, args?: DataFindArgs, options?: DataFindOptions): Promise> | ErrorResult>; decorate(doc: TDoc, access: DecorateAccess, context?: DataHookContext): Promise; decorateAll(docs: TDoc[], access: DecorateAllAccess, context?: DataHookContext): Promise; getRequestComplexity(): Required; genAllowedFields(doc: unknown, access: SelectAccess, baseFields?: string[]): Promise; genFilter(access?: BaseFilterAccess, filter?: DataFilter): Promise>; genIDFilter(id: string): Promise>; genSelect(access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; genQuerySelect(access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; pickAllowedFields(doc: TDoc, access: SelectAccess, baseFields?: string[]): Promise; trimOutputFields(doc: TDoc, access: SelectAccess, baseFields?: string[]): Promise; } type InternalModelHookContext = ModelHookContext & { fieldPermissionAccess?: { readIds?: Set; updateIds?: Set; }; }; declare class Core { private req; private caches; private cachedPermissions; constructor(req: AccessRouterBaseRequest); getIdentifier(modelName: string): string; genIDFilter(modelName: string, id: string): Promise>; genFilter(modelName: string, access?: BaseFilterAccess, _filter?: Filter): Promise>; private removePrefix; genAllowedFields(modelName: string, doc: unknown, access: SelectAccess, baseFields?: string[]): Promise; pickAllowedFields(modelName: string, doc: T, access: SelectAccess, baseFields?: string[]): Promise; genSelect(modelName: string, access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; genPopulate(modelName: string, access?: SelectAccess | BaseFilterAccess, _populate?: Populate | Populate[] | string | null, subPaths?: string[]): Promise<(string | Populate)[]>; validate(modelName: string, allowedData: unknown, access: ValidateAccess, context: ModelHookContext): Promise; prepare(modelName: string, allowedData: T, access: PrepareAccess, context: ModelHookContext): Promise; transform(modelName: string, doc: T, access: TransformAccess, context: ModelHookContext): Promise; afterPersist(modelName: string, doc: T, access: AfterPersistAccess, context: ModelHookContext): Promise; changes(modelName: string, doc: Record, context: ModelHookContext): Promise; beforeDelete(modelName: string, doc: T, context: ModelHookContext): Promise; afterDelete(modelName: string, doc: T, context: ModelHookContext): Promise; genDocPermissions(modelName: string, doc: unknown, access: DocPermissionsAccess, context: ModelHookContext): Promise<{}>; /** * VIRT-03 internal grant resolution (VIRT-00A D1/D2). * * Returns full internal document grants for virtual authorization without * consulting response-metadata switches (`includePermissions`, * `includeFieldPermissions`, `skim`, `stripPermissionsField`, * `exposedDocPermissionKeys`, `disableFieldPermissions`): those switches * affect serialization at the caller boundary only, never authorization * inputs. When `supplied` is a plain object it is reused as-is (no second * hook invocation for the same output-stage access); otherwise the correct * `docPermissionsAccess` hook runs once. The result populates scope-aware * `context.docPermissions` (full grants, distinct from serialized metadata). */ resolveFinalizerDocPermissions(modelName: string, doc: unknown, access: DocPermissionsAccess, context: ModelHookContext, supplied?: unknown): Promise>; addEmptyPermissions(modelName: string, doc: T): T; addDocPermissions(modelName: string, doc: T, access: DocPermissionsAccess, context: ModelHookContext): Promise; addFieldPermissions(modelName: string, doc: T, access: DocPermissionsAccess, context: InternalModelHookContext): Promise; decorate(modelName: string, doc: T, access: DecorateAccess, context: ModelHookContext): Promise; decorateAll(modelName: string, docs: T[], access: DecorateAllAccess, context: ModelHookContext): Promise; runTasks(modelName: string, docObject: T, task: Task | Task[]): T; getPermissions(): Permission; setPermissions(): Promise; canActivate(routeGuard: Validation): Promise; isAllowed(modelName: string, access: RouteGuardAccess | string): Promise; /** Authorize route entry using explicit server-owned basic/advanced metadata. */ isAllowedRoute(modelName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise; getService>(modelName: string): Service; getPublicService>(modelName: string): PublicService; service>(modelName: string): PublicService; svc>(modelName: string): PublicService; private getGlobalPermissions; } declare class DataCore { private req; private caches; private cachedPermissions; constructor(req: AccessRouterBaseRequest); genIDFilter(dataName: string, id: string): Promise>; genFilter(dataName: string, access?: BaseFilterAccess, _filter?: Filter): Promise>; genAllowedFields(dataName: string, _doc: unknown, access: SelectAccess, baseFields?: string[]): Promise; pickAllowedFields(dataName: string, doc: TDoc, access: SelectAccess, baseFields?: string[]): Promise; genSelect(dataName: string, access: SelectAccess, targetFields?: Projection, skipChecks?: boolean, subPaths?: string[]): Promise; decorate(dataName: string, doc: TDoc, access: DecorateAccess, context?: DataHookContext): Promise; decorateAll(dataName: string, docs: TDoc[], access: DecorateAllAccess, context?: DataHookContext): Promise; getPermissions(): Permission; setPermissions(): Promise; canActivate(routeGuard: Validation): Promise; isAllowed(dataName: string, access: RouteGuardAccess | string): Promise; /** Authorize route entry using explicit server-owned basic/advanced metadata. */ isAllowedRoute(dataName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise; getService(dataName: string): DataService; service(dataName: string): DataService; svc(dataName: string): DataService; private getGlobalPermissions; } declare enum StatusCodes { OK = 200, Created = 201, BadRequest = 400, Unauthorized = 401, Forbidden = 403, NotFound = 404, UnprocessableContent = 422 } declare enum Codes { Success = "success", Created = "created", BadRequest = "bad_request", Unauthorized = "unauthorized", Forbidden = "forbidden", NotFound = "not_found" } declare enum CustomHeaders { TotalCount = "wtt-total-count", ReturnedCount = "wtt-returned-count", Page = "wtt-page", PageSize = "wtt-page-size", TotalPages = "wtt-total-pages", HasNextPage = "wtt-has-next-page", HasPreviousPage = "wtt-has-previous-page" } declare enum FilterOperator { SubQuery = 0, Date = 1 } declare const MIDDLEWARE: unique symbol; declare const DATA_MIDDLEWARE: unique symbol; declare const PERMISSIONS: unique symbol; declare const PERMISSION_KEYS: unique symbol; interface AccessRouterRequestExtensions { macl?: Core; dacl?: DataCore; [PERMISSIONS]?: AccessRouterPermissions; [PERMISSION_KEYS]?: string[]; [MIDDLEWARE]?: unknown[] | unknown; [DATA_MIDDLEWARE]?: unknown[] | unknown; [key: string]: unknown; [key: symbol]: unknown; } type AccessRouterRequest = express.Request & AccessRouterRequestExtensions; interface PublicCreateArgs { select?: Projection; populate?: Populate[] | string; tasks?: Task | Task[]; } interface CreateArgs extends Omit { overrides?: { populate?: Populate[] | string; /** * VIRT-04 internal plan transport (VIRT-00A D6). * Effective mutation output selection carried into internal finalization * before virtual evaluation. Internal create args omit `select` publicly; * this trusted transport supplies the effective selection (explicit * `select` or `undefined` for all) without changing public presentation * (public `_create` still picks after decorate/tasks). */ effectiveSelect?: Projection; /** * VIRT-04 initiating operation (VIRT-00A D1). Upsert branches keep * `operation: 'upsert'` while virtual/output/doc accesses follow the * taken branch. Defaults to `'create'` when absent. */ operation?: string; }; } interface PublicCreateOptions { skim?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; populateAccess?: PopulateAccess; } interface CreateOptions extends PublicCreateOptions { } interface PublicListArgs { select?: Projection; populate?: Populate[] | string; include?: Include | Include[]; sort?: Sort; skip?: string | number; limit?: string | number; page?: string | number; pageSize?: string | number; tasks?: Task | Task[]; } interface PublicListOptions { skim?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; includeCount?: boolean; populateAccess?: PopulateAccess; lean?: boolean; } interface PublicReadArgs { select?: Projection; populate?: Populate[] | string; include?: Include | Include[]; tasks?: Task | Task[]; } interface PublicReadOptions { skim?: boolean; populateAccess?: PopulateAccess; lean?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; /** Retry an authorized read miss with list access (default true); requires list operation access. Forbidden/BadRequest never retry. */ tryList?: boolean; } interface PublicUpdateArgs { select?: Projection; populate?: Populate[] | string; tasks?: Task | Task[]; } interface PublicUpsertArgs extends PublicUpdateArgs { } interface UpdateOneArgs extends Omit { overrides?: { /** Trusted replacement: nullish generates row policy; false returns Forbidden without filter generation or persistence. */ filter?: Filter; populate?: Populate[] | string; /** * VIRT-04 internal plan transport (VIRT-00A D6). * Effective mutation output selection carried into internal finalization * before virtual evaluation (explicit `select`, `returningAll: false` * implicit `Object.keys(data)+_id`, or `undefined` for all). Public * `_update` still enforces presentation pick after decorate/tasks. */ effectiveSelect?: Projection; /** * VIRT-04 initiating operation (VIRT-00A D1). Upsert branches keep * `operation: 'upsert'` while accesses follow the taken branch. * Defaults to `'update'` when absent. */ operation?: string; }; } interface UpdateByIdArgs extends Omit { overrides?: { populate?: Populate[] | string; /** Nullish resolves the identifier; false denies without ID/filter generation or persistence. Objects still receive row policy. */ idFilter?: Filter; /** * VIRT-04 internal plan transport (VIRT-00A D6): effective output * selection for virtual planning before decorate/tasks. */ effectiveSelect?: Projection; /** VIRT-04 initiating operation (VIRT-00A D1); defaults to `'update'`. */ operation?: string; }; } interface UpsertArgs extends UpdateOneArgs { } interface PublicUpdateOptions { skim?: boolean; returningAll?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; populateAccess?: PopulateAccess; } interface PublicUpsertOptions extends PublicUpdateOptions { } interface UpdateOneOptions extends Omit { } interface UpdateByIdOptions extends UpdateOneOptions { } interface UpsertOptions extends UpdateOneOptions { } type DataFilter = Filter; interface DataFindOneArgs { select?: TSelect; } interface DataFindOneOptions { access?: FindAccess; } interface DataFindArgs { select?: TSelect; sort?: Sort; skip?: string | number; limit?: string | number; page?: string | number; pageSize?: string | number; } interface DataFindOptions { } interface KeyValueProjection { [key: string]: 1 | -1; } type Projection = string[] | string | KeyValueProjection; type SortOrder = -1 | 1 | 'asc' | 'ascending' | 'desc' | 'descending'; type Sort = string | { [key: string]: SortOrder; } | [string, SortOrder][] | undefined | null; type Primitive = string | number | boolean | bigint | symbol | null | undefined; type NonTraversable = Primitive | Date | RegExp | Function; type PrevDepth = [never, 0, 1, 2, 3, 4, 5]; type IsUnknown = unknown extends T ? ([keyof T] extends [never] ? true : false) : false; type Descend = T extends readonly (infer U)[] ? U : T; type ArrayValue = T extends readonly (infer U)[] ? U : T; type ComparableValue = string | number | bigint | Date; type WithDefaultId = T extends object ? T & { _id?: unknown; } : { _id?: unknown; }; /** * Public output with optional computed virtual shape (VIRT-01, VIRT-00A D3). * Virtual names are NEVER added to persisted `Filter`, sort/distinct inputs, * or client write types — only selected output shape gains optional virtuals. */ type WithVirtuals> = TModel & Partial; type PublicOutput> = WithVirtuals extends object ? WithVirtuals & Record : Record; type ModelDocument = mongoose.Document & TModel; type TrimLeft = S extends ` ${infer Rest}` ? TrimLeft : S; type TrimRight = S extends `${infer Rest} ` ? TrimRight : S; type Trim = TrimLeft>; type SplitProjectionString = S extends `${infer Head} ${infer Tail}` ? SplitProjectionString | SplitProjectionString : Trim; type ProjectionTokens = TProjection extends readonly string[] ? TProjection[number] : TProjection extends string ? SplitProjectionString : TProjection extends KeyValueProjection ? { [K in Extract]: TProjection[K] extends 1 ? K : never; }[Extract] : never; type PositiveProjectionPaths = ProjectionTokens extends infer TToken ? TToken extends string ? TToken extends '' ? never : TToken extends `-${string}` ? never : TToken : never : never; type UnionToIntersection = (T extends unknown ? (value: T) => void : never) extends (value: infer I) => void ? I : never; type Simplify = { [K in keyof T]: T[K]; } & {}; type DeepPickSingle = TPath extends `${infer Head}.${infer Tail}` ? Head extends keyof WithDefaultId ? Head extends string ? { [K in Head]: WithDefaultId[K] extends readonly (infer U)[] ? Array>> : WithDefaultId[K] extends object ? Simplify[K], Tail>> : WithDefaultId[K]; } : {} : {} : TPath extends keyof WithDefaultId ? Pick, TPath> : {}; type DeepPick = Simplify>>; /** * Literal-select output with optional virtuals (VIRT-01, VIRT-00A D3). * Virtual outputs stay optional (`Partial`) because authorization, absent * dependencies, `undefined`, and fail-closed errors can omit them. */ type SelectedPublicOutput> = string extends PositiveProjectionPaths ? PublicOutput : [PositiveProjectionPaths] extends [never] ? PublicOutput : DeepPick, Extract, string>>; type PopulateInput = Populate | string; type PopulateInputs = TPopulate extends readonly (infer U)[] ? U : TPopulate; type PopulatePath = TPopulateEntry extends string ? TPopulateEntry : TPopulateEntry extends { path: infer TPath extends string; } ? TPath : never; type PopulateSelect = TPopulateEntry extends { select?: infer TSelect; } ? TSelect : undefined; type PopulateLeafValue = TValue extends readonly (infer U)[] ? Array>> : SelectedPublicOutput>; type PopulateEntryShape = TPath extends `${infer Head}.${infer Tail}` ? Head extends keyof WithDefaultId ? Head extends string ? { [K in Head]: WithDefaultId[K] extends readonly (infer U)[] ? Array>> : Simplify[K], Tail, TValue>>; } : {} : {} : TPath extends keyof WithDefaultId ? Pick<{ [K in TPath]: TValue; }, TPath> : {}; type PopulateOutputShape = PopulatePath extends infer TPath extends string ? PopulateEntryShape, TPath>, TPopulateEntry>> : {}; type PopulateOutput = string extends PopulatePath> ? {} : [PopulatePath>] extends [never] ? {} : Simplify, PopulateInput>>>>; type SelectedPopulatedPublicOutput> = Simplify & PopulateOutput>; type Unwrap = NonNullable>; type DeepFieldPath = [Depth] extends [never] ? never : Unwrap extends NonTraversable ? never : { [K in Extract, string>]: K | (NonNullable[K]> extends NonTraversable ? never : NonNullable[K]> extends readonly (infer U)[] ? Unwrap extends NonTraversable ? never : `${K}.${DeepFieldPath}` : NonNullable[K]> extends object ? `${K}.${DeepFieldPath[K]>, PrevDepth[Depth]>}` : never); }[Extract, string>]; type PathValue = TPath extends `${infer Head}.${infer Tail}` ? Head extends Extract, string> ? PathValue[Head]>, Tail> : unknown : TPath extends Extract, string> ? Unwrap[TPath] : unknown; type FieldFilterOperators = { $eq?: T; $ne?: T; $in?: Array>; $nin?: Array>; $exists?: boolean; $gt?: ArrayValue extends ComparableValue ? ArrayValue : never; $gte?: ArrayValue extends ComparableValue ? ArrayValue : never; $lt?: ArrayValue extends ComparableValue ? ArrayValue : never; $lte?: ArrayValue extends ComparableValue ? ArrayValue : never; $regex?: ArrayValue extends string ? string | RegExp : never; $all?: T extends readonly unknown[] ? Array> : never; $size?: T extends readonly unknown[] ? number : never; $elemMatch?: ArrayValue extends object ? TypedFilter> : never; $$sq?: unknown; $$date?: unknown; [key: `$${string}`]: unknown; }; type FieldFilterValue = T | FieldFilterOperators>; type LooseFilter = false | Record; type TypedFilterObject = { [K in DeepFieldPath]?: FieldFilterValue>; } & { _id?: FieldFilterValue; $and?: TypedFilter[]; $or?: TypedFilter[]; $nor?: TypedFilter[]; [key: `$${string}`]: unknown; }; type TypedFilter = false | TypedFilterObject>; type Filter = IsUnknown extends true ? LooseFilter : TypedFilter; /** * Batched related-data join. Count uses one grouped aggregate per execution, * counting distinct authorized target documents independently of list pagination. * The target schema casts authorized filters/foreign operands; invalid casts return * BadRequest. Query casting does not execute countDocuments middleware/plugins. */ interface LegacyInclude { mode?: 'legacy'; model: string; op: 'list' | 'read' | 'count'; /** * Output path; must not overlap the receiving model's documentPermissionField * (equal, ancestor or descendant, including equivalent bracket paths). * Nested receiving-model settings apply. Rejected before target persistence. */ path: string; filter?: Filter; localField: string; foreignField: string; args?: unknown; options?: unknown; } /** * Explicit structural marker referencing a field of the immediate parent * document (ACI-01 D2.1). Recognized only in filter/id value positions of * correlated includes; magic `$field` strings are never markers. */ interface ParentRef { $parent: string; } /** Filter template for correlated includes; values may carry {@link ParentRef} markers. */ type CorrelatedFilter = Record; interface CorrelatedIncludeArgs { select?: Projection; sort?: Sort; skip?: number | string; limit?: number | string; page?: number | string; pageSize?: number | string; include?: Include | Include[]; } interface CorrelatedIncludeBase { mode: 'correlated'; model: string; op: 'list' | 'read' | 'count'; /** * Output-only field path. Must not equal, contain or descend from the receiving * model's documentPermissionField (default `_permissions`). The include tree is * preflighted before target persistence; violations return BadRequest. */ path: string; args?: CorrelatedIncludeArgs; options?: Record; } /** Identifier read: exactly one of `id` / `filter` must be present. */ interface CorrelatedReadByIdInclude extends CorrelatedIncludeBase { op: 'read'; id: string | ParentRef; filter?: never; } interface CorrelatedReadByFilterInclude extends CorrelatedIncludeBase { op: 'read'; filter: CorrelatedFilter; id?: never; } interface CorrelatedListInclude extends CorrelatedIncludeBase { op: 'list'; filter: CorrelatedFilter; id?: never; } interface CorrelatedCountInclude extends CorrelatedIncludeBase { op: 'count'; filter: CorrelatedFilter; id?: never; } type CorrelatedInclude = CorrelatedReadByIdInclude | CorrelatedReadByFilterInclude | CorrelatedListInclude | CorrelatedCountInclude; /** Related output cannot overwrite authorization metadata at any supported nested level. */ type Include = LegacyInclude | CorrelatedInclude; type FindAccess = 'list' | 'read'; type PopulateAccess = 'list' | 'read'; interface Populate { path: string; select?: Projection; match?: unknown; access?: PopulateAccess; } interface SubPopulate { path: string; select?: Projection; } type GuardHook = (this: TRequest, permissions: AccessRouterPermissions) => boolean | Promise; type Validation = boolean | string | string[] | GuardHook; type SelectAccess = 'list' | 'create' | 'read' | 'update' | string; /** Base operations with both basic and advanced generated model routes. */ type PairedRouteAccess = 'list' | 'read' | 'create' | 'update' | 'upsert' | 'count' | 'distinct'; /** Existing base route guards, including unpaired operations and subdocument access. */ type RouteBaseAccess = PairedRouteAccess | 'new' | 'delete' | 'subs'; /** Server-owned transport variant for a paired generated route. */ type RouteVariant = 'basic' | 'advanced'; /** The fourteen route-only keys, or the two keys for a specified paired base operation. */ type RouteVariantAccess = `${RouteVariant}${Capitalize}`; /** Known route guards with the existing trusted custom access-string compatibility. */ type RouteGuardAccess = RouteBaseAccess | RouteVariantAccess | (string & {}); type DocPermissionsAccess = 'list' | 'create' | 'read' | 'update' | string; type BaseFilterAccess = 'list' | 'read' | 'update' | 'delete' | string; type DecorateAccess = 'list' | 'create' | 'read' | 'update' | string; type DecorateAllAccess = 'list' | string; type ValidateAccess = 'create' | 'update' | string; type PrepareAccess = 'create' | 'update' | string; type TransformAccess = 'update' | string; type AfterPersistAccess = 'create' | 'update' | string; interface KeyValue { [key: string]: unknown; } interface Change { op: 'add' | 'replace' | 'remove'; path: Array; value?: unknown; } interface ModelHookContext { modelName: string; mongooseModel: mongoose.Model; operation?: string; originalDocumentSnapshot?: Record; finalDocumentSnapshot?: Record; currentDocument?: ModelDocument; originalData?: Record; allowedData?: Record; preparedData?: Record; allowedFields?: string[]; modifiedPaths?: string[]; changes?: Change[]; docPermissions?: KeyValue; resolvedQuery?: { filter?: Filter; select?: string[]; populate?: Populate[] | string; sort?: Sort; skip?: number; limit?: number; }; } interface DataHookContext { dataName?: string; operation?: string; resolvedQuery?: { filter?: Filter; select?: string[]; sort?: Sort; skip?: number; limit?: number; }; } type RootTarget = 'model' | 'data'; type RootSubOperation = 'subList' | 'subRead' | 'subCreate' | 'subUpdate' | 'subBulkUpdate' | 'subDelete'; type RootModelOperation = 'new' | 'list' | 'read' | 'create' | 'update' | 'upsert' | 'delete' | 'distinct' | 'count' | RootSubOperation; type RootDataOperation = 'list' | 'read'; interface RootQueryEntryBase { target: TTarget; name: string; op: TOp; order?: number; } interface RootModelNewQueryEntry extends RootQueryEntryBase<'model', 'new'> { args?: { select?: string[]; }; options?: { skim?: boolean; includePermissions?: boolean; includeFieldPermissions?: boolean; }; } interface RootModelListQueryEntry extends RootQueryEntryBase<'model', 'list'> { filter?: Filter; args?: PublicListArgs; options?: PublicListOptions; } interface RootModelReadByIdQueryEntry extends RootQueryEntryBase<'model', 'read'> { id: string; args?: PublicReadArgs; options?: PublicReadOptions; } interface RootModelReadByFilterQueryEntry extends RootQueryEntryBase<'model', 'read'> { filter: Filter; args?: PublicReadArgs & { sort?: Sort; }; options?: PublicReadOptions; } interface RootModelCreateQueryEntry extends RootQueryEntryBase<'model', 'create'> { data: unknown; args?: PublicCreateArgs; options?: PublicCreateOptions; } interface RootModelUpdateQueryEntry extends RootQueryEntryBase<'model', 'update'> { id: string; data: unknown; args?: PublicUpdateArgs; options?: PublicUpdateOptions; } interface RootModelUpsertQueryEntry extends RootQueryEntryBase<'model', 'upsert'> { data: Record; args?: PublicUpsertArgs; options?: PublicUpsertOptions; } interface RootModelDeleteQueryEntry extends RootQueryEntryBase<'model', 'delete'> { id: string; } interface RootModelSubListQueryEntry extends RootQueryEntryBase<'model', 'subList'> { id: string; sub: string; filter?: Filter; args?: { select?: string[]; }; } interface RootModelSubReadQueryEntry extends RootQueryEntryBase<'model', 'subRead'> { id: string; sub: string; subId: string; args?: { select?: string[]; populate?: SubPopulate | SubPopulate[] | string | string[]; }; } interface RootModelSubCreateQueryEntry extends RootQueryEntryBase<'model', 'subCreate'> { id: string; sub: string; data: unknown; } interface RootModelSubUpdateQueryEntry extends RootQueryEntryBase<'model', 'subUpdate'> { id: string; sub: string; subId: string; data: unknown; } interface RootModelSubBulkUpdateQueryEntry extends RootQueryEntryBase<'model', 'subBulkUpdate'> { id: string; sub: string; data: unknown; } interface RootModelSubDeleteQueryEntry extends RootQueryEntryBase<'model', 'subDelete'> { id: string; sub: string; subId: string; } interface RootModelDistinctQueryEntry extends RootQueryEntryBase<'model', 'distinct'> { field: string; filter?: Filter; } interface RootModelCountQueryEntry extends RootQueryEntryBase<'model', 'count'> { filter?: Filter; options?: Record; } interface RootDataListQueryEntry extends RootQueryEntryBase<'data', 'list'> { filter?: Filter; args?: DataFindArgs; options?: { includeCount?: boolean; [key: string]: unknown; }; } interface RootDataReadByIdQueryEntry extends RootQueryEntryBase<'data', 'read'> { id: string; args?: DataFindOneArgs; } interface RootDataReadByFilterQueryEntry extends RootQueryEntryBase<'data', 'read'> { filter: Filter; args?: DataFindOneArgs; } type RootQueryEntry = RootModelNewQueryEntry | RootModelListQueryEntry | RootModelReadByIdQueryEntry | RootModelReadByFilterQueryEntry | RootModelCreateQueryEntry | RootModelUpdateQueryEntry | RootModelUpsertQueryEntry | RootModelDeleteQueryEntry | RootModelSubListQueryEntry | RootModelSubReadQueryEntry | RootModelSubCreateQueryEntry | RootModelSubUpdateQueryEntry | RootModelSubBulkUpdateQueryEntry | RootModelSubDeleteQueryEntry | RootModelDistinctQueryEntry | RootModelCountQueryEntry | RootDataListQueryEntry | RootDataReadByIdQueryEntry | RootDataReadByFilterQueryEntry; interface RootOperationResult { index: number; target: RootTarget; name: string; op: RootModelOperation | RootDataOperation; result: PublicServiceResult; statusCode: number; message: string; } interface SubQueryEntry { model: string; op: 'list' | 'read' | string; id?: string; filter?: Filter; args?: Record; options?: Record; sqOptions?: { path?: string; compact?: boolean; }; } type TaskType = 'COPY_AND_DEPOPULATE' | 'COPY' | 'MOVE' | 'SLICE' | 'COUNT' | 'MASK' | string; interface Task { type: TaskType; args: unknown; options?: Record; } interface AccessRouterBaseRequest extends AccessRouterRequest { query: Record<'skip' | 'limit' | 'page' | 'page_size' | 'try_list' | 'skim' | 'include_permissions' | 'include_field_permissions' | 'include_count' | 'include_extra_headers' | 'returning_all', string>; } interface ModelRequest extends AccessRouterBaseRequest { macl: Core; } interface DataRequest extends AccessRouterBaseRequest { dacl: DataCore; } type Request = AccessRouterBaseRequest; interface ErrorResult { success: false; kind: 'error'; code: Codes.BadRequest | Codes.Unauthorized | Codes.Forbidden | Codes.NotFound; errors?: TError[]; query?: TQuery; } interface SingleResult { success: true; kind: 'single'; code: Codes.Success | Codes.Created; data: T; input?: TInput; query?: TQuery; context?: ModelHookContext; } interface ListResult { success: true; kind: 'list'; code: Codes.Success | Codes.Created; data: T[]; count: number; totalCount?: number | null; input?: TInput; query?: TQuery; contexts?: ModelHookContext[]; } type ServiceResult = SingleResult | ListResult | ErrorResult; /** * Type-only nominal brand that marks an object as a fully-formed public DTO * produced by one of the `toPublic*` serializers. It has no runtime value, so * the serialized JSON shape is unchanged; its only purpose is to make the * internal {@link SingleResult}/{@link ListResult}/{@link ErrorResult} shapes * structurally incompatible with the public DTOs at the type level. Direct * assignment from an internal service result to a public DTO fails to compile * because the internal result does not carry this brand, and crossing the * boundary requires the explicit serializer (or an explicit cast). */ declare const publicResultBrand: unique symbol; interface PublicErrorResult { success: false; code: Codes.BadRequest | Codes.Unauthorized | Codes.Forbidden | Codes.NotFound; errors?: TError[]; [publicResultBrand]: 'public-error'; } interface PublicSingleResult { success: true; kind: 'single'; code: Codes.Success | Codes.Created; data: T; [publicResultBrand]: 'public-single'; } interface PublicListResult { success: true; kind: 'list'; code: Codes.Success | Codes.Created; data: T[]; count: number; totalCount?: number | null; [publicResultBrand]: 'public-list'; } type PublicServiceResult = PublicSingleResult | PublicListResult | PublicErrorResult; interface FindArgs { select?: Projection; populate?: Populate[] | string; include?: Include | Include[]; sort?: Sort; skip?: string | number; limit?: string | number; page?: string | number; pageSize?: string | number; overrides?: { /** Trusted replacement: nullish generates row policy; false returns Forbidden without filter generation or persistence. */ filter?: Filter; select?: Projection; populate?: Populate[] | string; }; } interface FindOptions { skim?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; includeCount?: boolean; populateAccess?: PopulateAccess; lean?: boolean; } interface FindOneArgs { select?: Projection; sort?: Sort; populate?: Populate[] | string; include?: Include | Include[]; overrides?: { /** Trusted replacement: nullish generates row policy; false returns Forbidden without filter generation or persistence. */ filter?: Filter; select?: Projection; populate?: Populate[] | string; }; } interface FindOneOptions { access?: FindAccess; populateAccess?: PopulateAccess; skim?: boolean; lean?: boolean; includePermissions?: boolean; /** * Include field-level `_view`/`_edit` maps. Defaults to `includePermissions` * when omitted. `includePermissions: true` + `includeFieldPermissions: false` * keeps `docPermissions`-hook keys but omits `_view`/`_edit` entirely. * Enabling field maps also computes doc permissions as grant input (even * under skim or when `includePermissions` is false), so the maps match the * `includePermissions: true` output. */ includeFieldPermissions?: boolean; } interface FindByIdArgs { select?: Projection; populate?: Populate[] | string; include?: Include | Include[]; overrides?: { select?: Projection; populate?: Populate[] | string; /** Nullish resolves the identifier; false denies without ID/filter generation or persistence. Objects still receive row policy. */ idFilter?: Filter; }; } interface FindByIdOptions extends FindOneOptions { } /** Terminal false row policy returns Forbidden in either result mode, before adapter dispatch. */ interface ExistsOptions { /** Row-policy access, defaulting to read. */ access?: BaseFilterAccess; /** Return an `{ _id }` record or null instead of a match/miss boolean. Denial is an ErrorResult, never a miss. */ includeId?: boolean; } type MaybePromise = T | Promise; type AccessRouterFieldKey = [Extract] extends [never] ? string : Extract; type IdentifierHook = (this: TRequest, id: string) => MaybePromise>; type GlobalPermissionValue = Record | string[] | string | null | undefined; type BaseFilterHook = (this: TRequest, permissions: AccessRouterPermissions) => MaybePromise; type OverrideFilterHook = (this: TRequest, filter: Filter, permissions: AccessRouterPermissions) => MaybePromise; type Hook = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: TContext) => MaybePromise; type HookChain = Hook | Array>; type ValidateRule = boolean | unknown[]; type ValidateHook = (this: TRequest, allowedData: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise; type DocPermissionsHook = (this: TRequest, doc: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise>; type ChangeHook = (this: TRequest, previousValue: unknown, nextValue: unknown, changes: Change[], context: ModelHookContext) => MaybePromise; type DeleteHook = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise; type DocumentHook = (this: TRequest, value: ModelDocument, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise>; type DocumentHookChain = DocumentHook | Array>; type ModelBaseFilterHook = BaseFilterHook; type DataBaseFilterHook = BaseFilterHook; type ModelOverrideFilterHook = OverrideFilterHook; type DataOverrideFilterHook = OverrideFilterHook; type ModelIdentifierHook = IdentifierHook; type DataIdentifierHook = IdentifierHook; type ModelValidateHook = ValidateHook; type ModelDocPermissionsHook = DocPermissionsHook; type ModelChangeHook = ChangeHook; type ModelDocumentHook = DocumentHookChain; type ModelDeleteHook = DeleteHook, ModelRequest>; type ModelHook = HookChain; type ModelListHook = HookChain; type DataHook = HookChain; type DataListHook = HookChain; /** * Supported per-access virtual getter keys (VIRT-00A D1/D5). * `default` is a fallback access; `delete`/`distinct`/`count` are NOT * virtual accesses and must be rejected at configuration time. */ type ModelVirtualAccess = 'list' | 'create' | 'read' | 'update'; type ModelVirtualRecordAccess = 'default' | ModelVirtualAccess; /** * Scope-aware virtual getter context (VIRT-00A D2, frozen). * Extends {@link ModelHookContext} with the receiving model, definition/ * permission `scopePath` (e.g. `[]` at root, `['contacts','sub']` for * `contacts.sub.displayName`), and the three effective accesses. The * initiating operation stays in `context.operation` and is never * overwritten with fallback/effective visibility. * * Embedded outputs reuse the owning parent's internally computed grants with * the scope's `permissionSchema..sub` rules; dedicated subdocument * routes resolve grants from the raw parent lookup. Related (populate/ * include) targets finalize with the target model's own definitions, * permissions, selection, and effective access before parent getters run. */ interface ModelVirtualContext extends ModelHookContext { receivingModelName: string; scopePath: string[]; virtualAccess: ModelVirtualAccess; outputAccess: ModelVirtualAccess; docPermissionsAccess: ModelVirtualAccess; } type IsUnknownVirtual = unknown extends T ? ([keyof T] extends [never] ? true : false) : false; /** * Typed isolated persisted view for a virtual getter scope (VIRT-00A D3/D4). * Known models use a partial read-only persisted view (never * `Record`); unknown/untyped models keep an explicit * loose fallback. */ type ModelVirtualDoc = IsUnknownVirtual extends true ? Readonly>> : [Extract] extends [never] ? Readonly>> : Readonly>; /** * Virtual getter signature (VIRT-00A D3/D4, frozen). * Receives an isolated plain-object snapshot of persisted dependencies + * already-finalized related/embedded outputs for its scope. Sibling virtual * values are not visible. Mutating the input cannot change response data or * lifecycle snapshots. Only the returned field value is committed; * `undefined` omits the field; throws omit fail-closed. `this` is the * model request. */ type ModelVirtualGetter = (this: ModelRequest, doc: ModelVirtualDoc, permissions: AccessRouterPermissions, context: ModelVirtualContext) => MaybePromise; /** * Leaf virtual descriptor (VIRT-01): `{ get, dependsOn }`. * `dependsOn` lists top-level persisted field names relative to the * definition's scope (e.g. `['displayName']` inside `contacts.sub`, never * `['contacts.sub.displayName']`). Dotted paths and virtual-to-virtual * dependencies are rejected at configuration time. * * The getter runs after `toObject()`/lean normalization and after internal * document-permissions computation, with global permissions available and * before `decorate`/`decorateAll`/tasks. Only the returned field value is * committed via document-aware helpers: `undefined` (or a throw) omits the * field fail-closed and computed values never persist. Getters see an * isolated plain-object view for both lean and hydrated results; mutating the * input cannot change response data or lifecycle snapshots. * * @example * ```ts * import type { ModelVirtualDescriptor } from '@web-ts-toolkit/access-router'; * interface User { name: string; address: string; } * const descriptor: ModelVirtualDescriptor = { * dependsOn: ['address'], * get: async (doc) => (doc.address === undefined ? undefined : `addr:${doc.address}`), * }; * ``` */ interface ModelVirtualDescriptor { get: ModelVirtualGetter; dependsOn?: Array>; } /** * Per-access virtual record (VIRT-01): `default` + `list`/`create`/`read`/ * `update` (NOT `delete`/`distinct`/`count`). Each present access holds a * leaf descriptor or bare getter shorthand. An optional shared `dependsOn` * supports the documented `{ dependsOn, read: getter }` shape; a leaf's own * `dependsOn` wins over the shared list during resolution. */ type ModelVirtualAccessRecord = { default?: ModelVirtualDescriptor | ModelVirtualGetter; list?: ModelVirtualDescriptor | ModelVirtualGetter; create?: ModelVirtualDescriptor | ModelVirtualGetter; read?: ModelVirtualDescriptor | ModelVirtualGetter; update?: ModelVirtualDescriptor | ModelVirtualGetter; dependsOn?: Array>; }; /** Single virtual entry: bare getter, leaf descriptor, or per-access record. */ type ModelVirtualLeaf = ModelVirtualGetter | ModelVirtualDescriptor | ModelVirtualAccessRecord; type UnwrapVirtualArray = T extends readonly (infer U)[] ? U : T; /** * Persisted sub-scope model for an embedded container field (VIRT-01). * Unwraps arrays/nullable wrappers to the element/object type; unknown * fields fall back to a loose record. */ type VirtualSubModel = TField extends keyof TModel ? UnwrapVirtualArray> : Record; type LooseVirtualLeaf = ModelVirtualGetter, unknown> | ModelVirtualDescriptor, unknown> | ModelVirtualAccessRecord, unknown>; type LooseVirtualSubEntry = LooseVirtualLeaf | { sub?: Record; }>; }; /** * Virtual definitions for a model scope (VIRT-01). * Root leaves come from `TVirtuals` (strict: unknown persisted getter * fields are type errors). Embedded containers mirror * `permissionSchema..sub`: any persisted field may host a `sub` * record whose leaves use the container's persisted sub-model view. * Untyped models keep an explicit loose record fallback. * * Selected virtual outputs are optional (`Partial`): authorization, absent * dependencies, `undefined`, and fail-closed errors can omit them. Virtual * names are never added to persisted `Filter`, sort/distinct inputs, or * client write types. * * @example End-to-end typed virtual (persisted `User` has no `fullAddress`): * ```ts * import acl from '@web-ts-toolkit/access-router'; * import type { SelectedPublicOutput } from '@web-ts-toolkit/access-router/advanced'; * interface User { name: string; address: string; } * interface UserVirtuals { fullAddress: string; } * const router = acl.createRouter('User', { * permissionSchema: { * name: { read: true }, * address: { read: 'canViewAddress' }, * fullAddress: { read: 'canViewAddress' }, * }, * virtuals: { * fullAddress: { * dependsOn: ['address'], * read: async function (doc) { * if (doc.address === undefined) return undefined; * return `addr:${doc.address}`; * }, * }, * }, * }); * type Out = SelectedPublicOutput; * const out: Out = { name: 'Ada', fullAddress: 'addr:x' }; * const omitted: Out = { name: 'Ada' }; // virtual is optional when denied/missing * void [router, out, omitted]; * ``` */ type ModelVirtuals> = IsUnknownVirtual extends true ? Record : { [K in Extract]?: ModelVirtualLeaf; } & { [K in Extract]?: { sub?: Record, unknown> | { sub?: Record; }>; }; }; interface DefaultFindOneArgs extends Omit, 'overrides'> { } interface DefaultFindByIdArgs extends Omit, 'overrides'> { } interface DefaultFindArgs extends Omit, 'overrides'> { } type RequestSchema = RequestSchemaLike; type NestedRequestSchema = { default?: RequestSchema; data?: RequestSchema; }; /** * One field's subdocument route guards: exact variant → field base → field scalar * or closed object. Only an absent field rule falls back to top-level variant/base * guards. A defined object with no applicable key denies; nested `.default` is not * a fallback. Dynamic legacy operation names remain supported. */ interface SubOperationAccess { /** Base list guard for this field; fallback for both subdocument list variants. */ list?: Validation; /** Base read guard for this field; fallback for both subdocument read variants. */ read?: Validation; create?: Validation; update?: Validation; delete?: Validation; /** GET /:id/; undefined inherits this field's `list` rule. */ basicList?: Validation; /** POST /:id//__query; undefined inherits this field's `list` rule. */ advancedList?: Validation; /** GET /:id//:subId (and HEAD); undefined inherits this field's `read` rule. */ basicRead?: Validation; /** POST /:id//:subId/__query; undefined inherits this field's `read` rule. */ advancedRead?: Validation; [access: string]: Validation | undefined; } /** Per-field subdocument route objects or scalar guards; field names remain dynamic. */ type SubRouteGuardOptions = Record; type SubFieldPermissionOptions = Record>; interface RequestSchemas { create?: RequestSchema; update?: RequestSchema; upsert?: RequestSchema; count?: RequestSchema; distinct?: RequestSchema; advancedList?: RequestSchema; advancedReadFilter?: RequestSchema; advancedRead?: RequestSchema; advancedCreate?: RequestSchema | NestedRequestSchema; advancedCreateData?: RequestSchema; advancedUpdate?: RequestSchema | NestedRequestSchema; advancedUpdateData?: RequestSchema; advancedUpsert?: RequestSchema | NestedRequestSchema; advancedUpsertData?: RequestSchema; subList?: RequestSchema; subRead?: RequestSchema; subCreate?: RequestSchema; subUpdate?: RequestSchema; subBulkUpdate?: RequestSchema; } interface DataRequestSchemas { advancedList?: RequestSchema; advancedReadFilter?: RequestSchema; advancedRead?: RequestSchema; } interface Defaults { findOneArgs?: DefaultFindOneArgs; findOneOptions?: FindOneOptions; findByIdArgs?: DefaultFindByIdArgs; findByIdOptions?: FindByIdOptions; findArgs?: DefaultFindArgs; findOptions?: FindOptions; createArgs?: CreateArgs; createOptions?: CreateOptions; updateOneArgs?: UpdateOneArgs; updateOneOptions?: UpdateOneOptions; upsertOptions?: UpsertOptions; updateByIdArgs?: UpdateByIdArgs; upsertArgs?: UpsertArgs; updateByIdOptions?: UpdateByIdOptions; existsOptions?: ExistsOptions; publicListArgs?: PublicListArgs; publicListOptions?: PublicListOptions; publicCreateArgs?: PublicCreateArgs; publicCreateOptions?: PublicCreateOptions; publicReadArgs?: PublicReadArgs; publicReadOptions?: PublicReadOptions; publicUpdateArgs?: PublicUpdateArgs; publicUpdateOptions?: PublicUpdateOptions; } interface AccessRouterLogger { debug?: (...args: unknown[]) => unknown; info?: (...args: unknown[]) => unknown; warn?: (...args: unknown[]) => unknown; error?: (...args: unknown[]) => unknown; isLevelEnabled?: (level: string) => boolean; } interface GlobalOptions { requestPermissionField?: string; globalPermissions?: (this: AccessRouterRequest, req: AccessRouterRequest) => MaybePromise; /** Require populate target models to be registered in the active runtime. Defaults to `true`. */ requireRegisteredPopulateModels?: boolean; logger?: AccessRouterLogger; requestComplexity?: RequestComplexityOptions; } interface RootRouterOptions { basePath: string; /** Guard for the batch endpoint. Entries use target base operations, never basic/advanced variant names. */ operationAccess?: Validation; maxBatchEntries?: number; maxOrderGroups?: number; /** Whole root operations per order group; separate from requestComplexity.maxBulkConcurrency persistence admission. */ maxConcurrentOperations?: number; } /** Base-operation field grants used by PermissionSchema. Basic/advanced route keys do not grant fields. */ interface FieldOperationAccess { new?: Validation; list?: Validation; create?: Validation; read?: Validation; update?: Validation; upsert?: Validation; delete?: Validation; distinct?: Validation; count?: Validation; subs?: Validation | SubFieldPermissionOptions; } /** * Live generated-route guards, separate from PermissionSchema field grants. * An exact basic/advanced rule replaces its base guard; only undefined inherits * the base operation's exact → `.default` → scalar-shorthand resolution. Selected * false/empty-array/false-returning guards are terminal, not ANDed with the base. * Model exact lookup includes runtime-default variants; data has no model defaults. * Denial is HTTP 401; routes remain registered in getEndpoints() and OpenAPI. * Field/row/hook, root-entry, related-target and mutation-response policies keep * their existing base operations. Paths below use the default route segments. */ interface OperationAccess extends Omit { /** GET /new guard; unpaired, with no basic/advanced override. */ new?: Validation; /** Base list guard and fallback for both list routes; also governs model read-to-list retries. */ list?: Validation; /** Base create guard and fallback for both model create routes. */ create?: Validation; /** Base read guard and fallback for both read variants; advancedRead covers both POST read routes. */ read?: Validation; /** Base update guard and fallback for both model update routes. */ update?: Validation; /** Base upsert guard and fallback for both upserts; create/update route variants do not gate its branches. */ upsert?: Validation; /** DELETE /:id guard; unpaired, with no basic/advanced override. */ delete?: Validation; /** Base distinct guard and fallback for GET and POST /distinct/:field. */ distinct?: Validation; /** Base count guard and fallback for GET and POST /count. */ count?: Validation; /** Fallback for a base operation without an exact rule; explicit base/variant guards take precedence. */ default?: Validation; /** GET / (and HEAD) override for model/data list; undefined inherits `list`. */ basicList?: Validation; /** POST /__query override for model/data list; undefined inherits `list`. */ advancedList?: Validation; /** GET /:id (and HEAD) override for model/data read; undefined inherits `read`. */ basicRead?: Validation; /** POST /__query/:id and /__query/__filter override for model/data read; undefined inherits `read`. */ advancedRead?: Validation; /** POST / model create override; undefined inherits `create`. */ basicCreate?: Validation; /** POST /__mutation model create override; undefined inherits `create`. */ advancedCreate?: Validation; /** PATCH /:id model update override; undefined inherits `update`. */ basicUpdate?: Validation; /** PATCH /__mutation/:id model update override; undefined inherits `update`. */ advancedUpdate?: Validation; /** PUT / model upsert override; undefined inherits `upsert`. */ basicUpsert?: Validation; /** PUT /__mutation model upsert override; undefined inherits `upsert`. */ advancedUpsert?: Validation; /** GET /count model count override; undefined inherits `count`. */ basicCount?: Validation; /** POST /count model filtered-count override; undefined inherits `count`. */ advancedCount?: Validation; /** GET /distinct/:field model distinct override; undefined inherits `distinct`. */ basicDistinct?: Validation; /** POST /distinct/:field model filtered-distinct override; undefined inherits `distinct`. */ advancedDistinct?: Validation; /** * Per-field scalar or closed-object guards, with list/read variants. Legacy scalar * `subs` is accepted but is not an umbrella guard for absent fields; they use top-level rules. */ subs?: Validation | SubRouteGuardOptions; } type PermissionRule = Validation | FieldOperationAccess; /** Field grants by base operation. Put basic/advanced route guards in operationAccess, not field-rule objects. */ type PermissionSchema = Partial>; interface DocPermissions { list?: ModelDocPermissionsHook; create?: ModelDocPermissionsHook; read?: ModelDocPermissionsHook; update?: ModelDocPermissionsHook; } interface DefaultModelRouterOptions { listHardLimit?: number; /** Authorization metadata path (default `_permissions`); include output cannot equal, contain or descend from it. */ documentPermissionField?: string; idParam?: string; idField?: string; resolveIdFilter?: ModelIdentifierHook; parentPath?: string; queryRouteSegment?: string; mutationRouteSegment?: string; /** * Live model route guards: scalar Validation shorthand or base/variant rules. * `{ list: true, read: true, basicRead: false }` denies only GET /:id (and HEAD) * among top-level routes, retaining both advanced read POSTs subject to ordinary * field/row policy. Subdocument reads follow their field-rule precedence. * Exact runtime-default variants precede base fallback; option objects replace * top-level keys shallowly, rather than recursively merging operationAccess. */ operationAccess?: Validation | OperationAccess; modelPermissionPrefix?: string; /** * When `true`, list/read/update requests without an explicit `select` return * field-less rows (only `_id`, plus the permission field when requested) * instead of all allowed fields. Applies to plain and advanced PATCH * (returned updated record honors `select`, including `?select=` on plain * PATCH and body `select` on advanced PATCH). Only an omitted (`undefined`) * select triggers this; explicit selects — including backend `defaults` and * an explicitly empty string/array (which keep their current meaning) — are * unaffected. Counts, ordering, includes and permission enforcement are * unchanged. Defaults to `false`. */ requireExplicitSelect?: boolean; /** * Extra fields allowed in `sort` regardless of `list`/`read` field policy. * Merged as a union with permission-derived allowed fields (`id`/`_id` are * always allowed). Does not grant output visibility, only sortability. */ sortableFields?: string[]; /** * When `true`, disallowed `sort` keys are omitted from the sort sent upstream * (Mongoose) instead of returning `BadRequest`. Malformed sort syntax still * returns `BadRequest`. If every key is stripped, no sort is applied. * Defaults to `false` (strict: disallowed sort returns `BadRequest`). */ stripDisallowedSort?: boolean; /** * When `true`, the document permissions field (`documentPermissionField`, * default `_permissions`) is removed from all outputs — including its empty * placeholder — and client `includePermissions`/`includeFieldPermissions` * input (query params, body/root-batch options and service-direct options) * as well as per-operation `defaults` are ignored (both resolve to `false`). * Output matches `includePermissions: false` with the permissions field * removed entirely. Enforcement still runs; only output metadata is * affected. Defaults to `false`. */ stripPermissionsField?: boolean; /** * When `true`, client `includeFieldPermissions` input (query params, * body/root-batch options and service-direct options) as well as * per-operation `defaults` are ignored and field maps (`_view`/`_edit`) * are never computed. `docPermissions`-hook keys still follow * `includePermissions`. Defaults to `false`. */ disableFieldPermissions?: boolean; } interface ExtendedDefaultModelRouterOptions extends DefaultModelRouterOptions { 'operationAccess.default'?: Validation; 'operationAccess.new'?: Validation; 'operationAccess.list'?: Validation; 'operationAccess.read'?: Validation; 'operationAccess.update'?: Validation; 'operationAccess.upsert'?: Validation; 'operationAccess.delete'?: Validation; 'operationAccess.create'?: Validation; 'operationAccess.distinct'?: Validation; 'operationAccess.count'?: Validation; 'operationAccess.basicList'?: Validation; 'operationAccess.advancedList'?: Validation; 'operationAccess.basicRead'?: Validation; 'operationAccess.advancedRead'?: Validation; 'operationAccess.basicCreate'?: Validation; 'operationAccess.advancedCreate'?: Validation; 'operationAccess.basicUpdate'?: Validation; 'operationAccess.advancedUpdate'?: Validation; 'operationAccess.basicUpsert'?: Validation; 'operationAccess.advancedUpsert'?: Validation; 'operationAccess.basicCount'?: Validation; 'operationAccess.advancedCount'?: Validation; 'operationAccess.basicDistinct'?: Validation; 'operationAccess.advancedDistinct'?: Validation; 'operationAccess.subs'?: Validation | SubRouteGuardOptions; } interface ModelRouterOptions> extends DefaultModelRouterOptions { modelName?: string; basePath?: string; /** * Update grants define assignment boundaries: authorized leaves preserve omitted * siblings; a whole-object/array grant permits replacement (no generic deep merge). * Send nested JSON; literal dotted client keys are not update operators. * Basic/advanced keys are route guards in operationAccess, not field grants here. */ permissionSchema?: PermissionSchema | Extract>; /** * Package-level computed fields ("virtuals", VIRT-01). * Typed getter/descriptor/output generics per VIRT-00A D3: typed getters * use a partial read-only persisted view for their scope, never * `Record` for a known model; untyped consumers keep an * explicit loose fallback. Per-access records use `default` + * `list`/`create`/`read`/`update` (NOT `delete`/`distinct`/`count`) and * recursive embedded `sub` scopes mirroring * `permissionSchema..sub`. Inapplicable registered names stay * virtual (never persisted). Mutable post-construction (not a build-time * key); replacement swaps the frozen snapshot so in-flight planning keeps * a coherent version. * * Getters run after `toObject()`/lean normalization (both lean and * hydrated results compute), after internal document-permissions * computation, and before `decorate`/`decorateAll`/tasks and final * trimming. `dependsOn` fields are fetched internally even when the caller * may not receive them, then stripped unless independently selected and * authorized. Virtuals are output-only: registered names (including * embedded `sub` leaves) are excluded from persisted projections, client * write admission, and database sort/filter/distinct handling. * * @example Shortest typed happy-path (persisted model lacks `fullAddress`): * ```ts * import acl from '@web-ts-toolkit/access-router'; * interface User { name: string; address: string; } * interface UserVirtuals { fullAddress: string; } * const router = acl.createRouter('User', { * permissionSchema: { * name: { read: true }, * address: { read: 'canViewAddress' }, * fullAddress: { read: 'canViewAddress' }, * }, * virtuals: { * fullAddress: { * dependsOn: ['address'], * read: async function (doc) { * if (doc.address === undefined) return undefined; * return `addr:${doc.address}`; * }, * }, * }, * }); * // Selected output: { name: string; fullAddress?: string } — virtual is optional. * void router; * ``` */ virtuals?: ModelVirtuals; alwaysSelectFields?: string[]; docPermissions?: DocPermissions | ModelDocPermissionsHook; /** * Allowlist of `docPermissions`-hook keys exposed in API responses. * Enforcement always uses the full hook map; only serialization is reduced, * after all grant computations and decorate hooks have run. `_view`/`_edit` * are always kept. `undefined` (default) exposes everything (current * behavior); any array (including `[]`) exposes `_view`/`_edit` plus the * listed keys. Note `guard()` conditions on stripped doc-level keys fail * closed — allowlist any key a custom route guard evaluates. */ exposedDocPermissionKeys?: string[]; baseFilter?: ModelBaseFilterHook | Record; overrideFilter?: ModelOverrideFilterHook | Record; decorate?: ModelHook | Record>; decorateAll?: ModelListHook | Record>; validate?: ValidateRule | ModelValidateHook | Record; /** * Trusted model hook after validation of selected client data; output is not * re-filtered and may add protected/server-only fields. During update, supplied * children at partial policy ancestors apply without deleting omitted siblings. * Explicit null/undefined can clear/unset a parent; no output means no assignments. * Use transform document setters for deliberate whole-parent replacement. */ prepare?: ModelHook | Record>; /** * Trusted live-document hook after update assignment and before save. May replace * protected state explicitly with document setters; do not blindly reintroduce * unfiltered context.originalData. Model hooks are not added to subdocument writes. */ transform?: ModelDocumentHook | Record>; afterPersist?: ModelDocumentHook | Record>; onChange?: Record; beforeDelete?: ModelDeleteHook; afterDelete?: ModelDeleteHook; requestSchemas?: RequestSchemas; defaults?: Defaults; } interface DataRouterOptions { /** * In-memory records are owned by the router as an immutable configured snapshot. * Mutating this array or its records after configuration does not change served data; * use `setDataOption(name, 'data', nextRecords)` or `router.data(nextRecords)` to replace it. */ data?: TData[]; listHardLimit?: number; idParam?: string; idField?: string; resolveIdFilter?: DataIdentifierHook; parentPath?: string; queryRouteSegment?: string; /** * Live data-route guards: list/read plus basicList/advancedList/basicRead/advancedRead. * Only undefined variants inherit their base; false denies with HTTP 401 while * routes remain registered. Data never inherits model defaults. The broader * legacy operation-rule object and scalar Validation shorthand remain accepted. */ operationAccess?: Validation | OperationAccess; dataName?: string; basePath?: string; /** Base-operation field grants, independent of operationAccess's basic/advanced route guards. */ permissionSchema?: PermissionSchema>; baseFilter?: DataBaseFilterHook | Record; overrideFilter?: DataOverrideFilterHook | Record; decorate?: DataHook | Record>; decorateAll?: DataListHook | Record>; requestSchemas?: DataRequestSchemas; } interface ExtendedModelRouterOptions> extends ModelRouterOptions, ExtendedDefaultModelRouterOptions { 'alwaysSelectFields.default'?: string[]; 'alwaysSelectFields.list'?: string[]; 'alwaysSelectFields.create'?: string[]; 'alwaysSelectFields.read'?: string[]; 'alwaysSelectFields.update'?: string[]; 'docPermissions.default'?: ModelDocPermissionsHook; 'docPermissions.list'?: ModelDocPermissionsHook; 'docPermissions.create'?: ModelDocPermissionsHook; 'docPermissions.read'?: ModelDocPermissionsHook; 'docPermissions.update'?: ModelDocPermissionsHook; 'baseFilter.default'?: ModelBaseFilterHook; 'baseFilter.list'?: ModelBaseFilterHook; 'baseFilter.read'?: ModelBaseFilterHook; 'baseFilter.update'?: ModelBaseFilterHook; 'baseFilter.delete'?: ModelBaseFilterHook; 'overrideFilter.default'?: ModelOverrideFilterHook; 'overrideFilter.list'?: ModelOverrideFilterHook; 'overrideFilter.read'?: ModelOverrideFilterHook; 'overrideFilter.update'?: ModelOverrideFilterHook; 'overrideFilter.delete'?: ModelOverrideFilterHook; 'decorate.default'?: ModelHook; 'decorate.list'?: ModelHook; 'decorate.create'?: ModelHook; 'decorate.read'?: ModelHook; 'decorate.update'?: ModelHook; 'decorateAll.default'?: ModelListHook; 'decorateAll.list'?: ModelListHook; 'validate.default'?: ValidateRule | ModelValidateHook; 'validate.create'?: ValidateRule | ModelValidateHook; 'validate.update'?: ValidateRule | ModelValidateHook; 'prepare.default'?: ModelHook; 'prepare.create'?: ModelHook; 'prepare.update'?: ModelHook; 'transform.default'?: ModelDocumentHook; 'transform.update'?: ModelDocumentHook; 'afterPersist.default'?: ModelDocumentHook; 'afterPersist.create'?: ModelDocumentHook; 'afterPersist.update'?: ModelDocumentHook; onChange?: Record; 'requestSchemas.create'?: RequestSchema; 'requestSchemas.update'?: RequestSchema; 'requestSchemas.upsert'?: RequestSchema; 'requestSchemas.count'?: RequestSchema; 'requestSchemas.distinct'?: RequestSchema; 'requestSchemas.advancedList'?: RequestSchema; 'requestSchemas.advancedReadFilter'?: RequestSchema; 'requestSchemas.advancedRead'?: RequestSchema; 'requestSchemas.advancedCreate.default'?: RequestSchema; 'requestSchemas.advancedCreate.data'?: RequestSchema; 'requestSchemas.advancedUpdate'?: RequestSchema; 'requestSchemas.advancedUpdate.default'?: RequestSchema; 'requestSchemas.advancedUpdate.data'?: RequestSchema; 'requestSchemas.advancedUpsert.default'?: RequestSchema; 'requestSchemas.advancedUpsert.data'?: RequestSchema; 'requestSchemas.subList'?: RequestSchema; 'requestSchemas.subRead'?: RequestSchema; 'requestSchemas.subCreate'?: RequestSchema; 'requestSchemas.subUpdate'?: RequestSchema; 'requestSchemas.subBulkUpdate'?: RequestSchema; 'defaults.findOneArgs'?: DefaultFindOneArgs; 'defaults.findOneOptions'?: FindOneOptions; 'defaults.findByIdArgs'?: DefaultFindByIdArgs; 'defaults.findByIdOptions'?: FindByIdOptions; 'defaults.findArgs'?: DefaultFindArgs; 'defaults.findOptions'?: FindOptions; 'defaults.createArgs'?: CreateArgs; 'defaults.createOptions'?: CreateOptions; 'defaults.updateOneArgs'?: UpdateOneArgs; 'defaults.updateOneOptions'?: UpdateOneOptions; 'defaults.updateByIdArgs'?: UpdateByIdArgs; 'defaults.updateByIdOptions'?: UpdateByIdOptions; 'defaults.upsertArgs'?: UpsertArgs; 'defaults.existsOptions'?: ExistsOptions; 'defaults.publicListArgs'?: PublicListArgs; 'defaults.publicListOptions'?: PublicListOptions; 'defaults.publicCreateArgs'?: PublicCreateArgs; 'defaults.publicCreateOptions'?: PublicCreateOptions; 'defaults.publicReadArgs'?: PublicReadArgs; 'defaults.publicReadOptions'?: PublicReadOptions; 'defaults.publicUpdateArgs'?: PublicUpdateArgs; 'defaults.publicUpdateOptions'?: PublicUpdateOptions; /** * Virtual dotted keys (VIRT-01): `virtuals`, `virtuals.`, * `virtuals..`, `virtuals..sub...` (recursive). * The template fallback preserves typed getter doc views for known * models so unknown persisted getter fields fail (including dotted * setters); embedded `sub` records stay structurally loose here and are * fully validated at runtime against the receiving Mongoose schema + * child scopes. */ [key: `virtuals.${string}`]: ModelVirtuals[Extract] | ModelVirtualDescriptor | ModelVirtualGetter | { sub?: Record; } | Record, unknown> | ModelVirtualGetter, unknown> | { sub?: unknown; }>; } interface ExtendedDataRouterOptions extends DataRouterOptions { 'operationAccess.default'?: Validation; 'operationAccess.list'?: Validation; 'operationAccess.read'?: Validation; 'operationAccess.basicList'?: Validation; 'operationAccess.advancedList'?: Validation; 'operationAccess.basicRead'?: Validation; 'operationAccess.advancedRead'?: Validation; 'requestSchemas.advancedList'?: RequestSchema; 'requestSchemas.advancedReadFilter'?: RequestSchema; 'requestSchemas.advancedRead'?: RequestSchema; } interface DistinctArgs { filter?: Filter; } type SubdocumentId = string; type SubdocumentName = string; type SubdocumentRecord = Record; type SubdocumentBulkRecord = SubdocumentRecord & { _id?: unknown; }; type SubdocumentCreateInput = SubdocumentRecord | SubdocumentRecord[]; type SubdocumentBulkUpdateInput = SubdocumentBulkRecord[]; interface SubdocumentListOptions { filter?: Filter; select?: string[]; } interface SubdocumentReadOptions { select?: string[]; populate?: SubPopulate | SubPopulate[]; } interface SubdocumentCreateOptions { addFirst?: boolean; } interface SubdocumentParentArgs { populate?: SubPopulate | SubPopulate[]; } interface SubdocumentParentOptions { access?: BaseFilterAccess; lean?: boolean; } declare function parsePathParam(value: string | string[] | undefined, parameter: string): string; declare function parseQuery(schema: TSchema, value: unknown): z.output; declare function parseBody(schema: TSchema, value: unknown): z.output; declare function parseBodyWithSchema(schema: TSchema, value: unknown, userSchema?: RequestSchemaLike): Promise>; declare function parseNestedBodyWithSchema(schema: z.ZodTypeAny, value: unknown, nestedKey: string, userSchema?: RequestSchemaLike): Promise>; /** * Wraps a request validator into a request schema adapter used by `access-router`. * * @example * const schema = defineRequestSchema(fromZod(z.object({ name: z.string() }))); */ declare function defineRequestSchema(validator: RequestSchemaValidator, options?: RequestSchemaOptions): RequestSchemaAdapter; /** * Adapts a Zod schema into a `RequestSchemaValidator` for `access-router`. * * Supports both synchronous schemas and schemas with async refinements or * transforms via `safeParseAsync` when available. Synchronous schemas keep * their existing output values and issue paths. Operational exceptions thrown * by `safeParseAsync` itself propagate unchanged. * * @example * const validator = fromZod(z.object({ name: z.string() })); */ declare function fromZod(schema: TSchema): RequestSchemaValidator>; declare function fromStandardSchema(schema: TSchema): RequestSchemaValidator>; declare function fromYup(schema: TSchema): RequestSchemaValidator; declare function fromJoi(schema: TSchema): RequestSchemaValidator; /** * Adapts an AJV validator into a `RequestSchemaValidator`. * * Contract: synchronous validators return a boolean and expose input-local * diagnostics via mutable `validator.errors`; those errors are snapshotted * synchronously before any suspension so concurrent same-turn validations do * not observe another input's diagnostics. Asynchronous AJV schemas * (`$async: true`) resolve with validated data, including false/true/null. * AJV `ValidationError` rejections (`ajv: true`, `validation: true`, `errors` * array) are normalized; other throws/rejections preserve operational identity. * Async paths never read shared `validator.errors`. * * Untagged promises/thenables throw TypeError and are observed to prevent * unhandled rejection. Migrate async boolean-verdict wrappers to a custom * RequestSchemaValidator, or retain `$async: true` and return data/reject AJV * validation errors. Sync validators must return boolean verdicts. Real AJV * sync/async validators are accepted without casts; no runtime AJV import is used. * Output is inferred from genuine AJV's type-guard overload (including its async * subtype); structural async output is inferred from the promise. Pass TValue * explicitly for a structural sync boolean validator without a type guard. */ declare function fromAjv(validator: AjvValidatorLike & ((value: unknown) => value is TValue)): RequestSchemaValidator; /** Adapts structural AJV validators with the same sync-verdict / tagged-async-data contract. */ declare function fromAjv(validator: AjvValidatorLike): RequestSchemaValidator; declare function fromValibot(schema: TSchema, safeParse: ValibotSafeParseLike): RequestSchemaValidator; /** * Adapts an ArkType type into a `RequestSchemaValidator`. * * Success/failure is discriminated via the supported ArkErrors brand key * (`hasArkKind(result, "errors")`, stored at runtime as `' arkKind'` with a * leading space; the unspaced `arkKind` alias is also accepted) or, when the * type exposes a Standard Schema `~standard.validate` contract, via that * contract. ArkType's Standard Schema `validate` returns `{ value }` on * success but returns raw `ArkErrors` on failure instead of `{ issues }`, so * both shapes are handled. Array shape and `message`/`path`-like record * fields are never used as discriminators, so valid empty/scalar/record/ * nullable arrays pass through unchanged. Unexpected exceptions thrown by the * type propagate unchanged. */ declare function fromArkType(type: ArkTypeLike): RequestSchemaValidator; declare function fromIoTs(decoder: IoTsDecoderLike): RequestSchemaValidator; declare function fromSuperstruct(struct: TStruct, validate: SuperstructValidateLike): RequestSchemaValidator; declare function fromVine(validator: VineValidatorLike): RequestSchemaValidator; 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 };