@web-ts-toolkit/access-router 0.54.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +417 -9
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +623 -156
- package/advanced.mjs +597 -130
- package/index.d.mts +5 -2
- package/index.d.ts +5 -2
- package/index.js +3033 -1831
- package/index.mjs +2780 -1578
- package/llms.txt +3 -0
- package/package.json +3 -3
- package/{parsers-ZSrF7SNu.d.mts → parsers-BXKoUKMa.d.mts} +633 -444
- package/{parsers-ZSrF7SNu.d.ts → parsers-BXKoUKMa.d.ts} +633 -444
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import mongoose from 'mongoose';
|
|
2
|
+
import mongoose, { Model, Schema } from 'mongoose';
|
|
3
3
|
import express from 'express';
|
|
4
4
|
|
|
5
5
|
interface BooleanObject {
|
|
@@ -24,6 +24,13 @@ declare class Permission {
|
|
|
24
24
|
type Permissions = Permission;
|
|
25
25
|
type AccessRouterPermissions = Permission;
|
|
26
26
|
|
|
27
|
+
/** Package-only, per-call output intent. Resolve defaults before any async admission. */
|
|
28
|
+
|
|
29
|
+
interface ModelServiceIntent {
|
|
30
|
+
readonly args: FindArgs;
|
|
31
|
+
readonly options: Omit<FindOptions & FindOneOptions, 'authorization'>;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
type OpenApiMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
28
35
|
type OpenApiSchema = Record<string, unknown>;
|
|
29
36
|
type OpenApiSchemaSource = unknown;
|
|
@@ -104,224 +111,6 @@ type OpenApiRouterOptions = Partial<OpenApiDocumentOptions> & {
|
|
|
104
111
|
swaggerUiBundleUrl?: string;
|
|
105
112
|
};
|
|
106
113
|
|
|
107
|
-
type ModelReferenceMap = {
|
|
108
|
-
[key: string]: string | ModelReferenceMap;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Owner of global/model/data options, registered Mongoose models and OpenAPI routes.
|
|
112
|
-
* Prefer createAccessRuntime() for a callable API with factories and bound setters;
|
|
113
|
-
* its `.runtime` exposes this owner. A bare instance has no createRouter method.
|
|
114
|
-
* Registries/configuration are isolated; the Mongoose jsonSchema extension is initialized
|
|
115
|
-
* idempotently. The defaultRuntime additionally adopts process-global named models.
|
|
116
|
-
*/
|
|
117
|
-
declare class AccessRuntime {
|
|
118
|
-
private readonly allowGlobalModelLookup;
|
|
119
|
-
constructor(options?: {
|
|
120
|
-
allowGlobalModelLookup?: boolean;
|
|
121
|
-
});
|
|
122
|
-
private readonly globalOptions;
|
|
123
|
-
private readonly defaultModelOptions;
|
|
124
|
-
private readonly modelOptions;
|
|
125
|
-
private readonly modelJsonSchemas;
|
|
126
|
-
private readonly dataOptions;
|
|
127
|
-
private readonly modelRefs;
|
|
128
|
-
private readonly modelSubs;
|
|
129
|
-
private readonly modelAtts;
|
|
130
|
-
private readonly modelInstances;
|
|
131
|
-
private readonly openApiRegistry;
|
|
132
|
-
private openApiPathPrefix;
|
|
133
|
-
registerModelInstance<TModel>(modelName: string, model: mongoose.Model<TModel>): void;
|
|
134
|
-
hasModelInstance(modelName: string): boolean;
|
|
135
|
-
getModelInstance<TModel = unknown>(modelName: string): mongoose.Model<TModel> | null;
|
|
136
|
-
registerOpenApiRoute(route: OpenApiRouteDescriptor): void;
|
|
137
|
-
/**
|
|
138
|
-
* Compose OpenAPI paths while synchronously constructing mounted routers.
|
|
139
|
-
* Prefixes apply before collision checks; prior descriptors and Express paths
|
|
140
|
-
* are unchanged. Nested calls compose and the prior scope is restored even on
|
|
141
|
-
* failure. The callback must finish registration synchronously (no async work).
|
|
142
|
-
*/
|
|
143
|
-
withOpenApiPathPrefix<T>(prefix: string, build: () => T): T;
|
|
144
|
-
/**
|
|
145
|
-
* Enable strict OpenAPI collision detection for the remainder of this
|
|
146
|
-
* runtime's lifetime. After this call, conflicting method/path
|
|
147
|
-
* registrations and duplicate operationIds throw {@link OpenApiCollisionError}
|
|
148
|
-
* instead of silently replacing. Existing already-registered routes are
|
|
149
|
-
* not retroactively re-validated.
|
|
150
|
-
*/
|
|
151
|
-
enableOpenApiCollisionDetection(): void;
|
|
152
|
-
getOpenApiRoutes(): OpenApiRouteDescriptor[];
|
|
153
|
-
clearOpenApiRoutes(): void;
|
|
154
|
-
getOpenApiSpec(info: OpenApiDocumentOptions): {
|
|
155
|
-
openapi: string;
|
|
156
|
-
info: Omit<OpenApiDocumentOptions, "servers">;
|
|
157
|
-
servers?: OpenApiDocumentOptions["servers"];
|
|
158
|
-
paths: Record<string, Record<string, {
|
|
159
|
-
[key: `x-${string}`]: unknown;
|
|
160
|
-
operationId?: string;
|
|
161
|
-
summary?: string;
|
|
162
|
-
description?: string;
|
|
163
|
-
tags?: string[];
|
|
164
|
-
deprecated?: boolean;
|
|
165
|
-
parameters?: OpenApiParameter[];
|
|
166
|
-
requestBody?: {
|
|
167
|
-
required?: boolean;
|
|
168
|
-
content: Record<string, {
|
|
169
|
-
schema: OpenApiSchema;
|
|
170
|
-
}>;
|
|
171
|
-
};
|
|
172
|
-
responses: OpenApiResponses;
|
|
173
|
-
}>>;
|
|
174
|
-
};
|
|
175
|
-
createBootstrapSnapshot(): {
|
|
176
|
-
globalOptions: GlobalOptions;
|
|
177
|
-
defaultModelOptions: DefaultModelRouterOptions;
|
|
178
|
-
modelOptions: Record<string, ModelRouterOptions>;
|
|
179
|
-
dataOptions: Record<string, DataRouterOptions>;
|
|
180
|
-
modelInstances: Record<string, mongoose.Model<unknown>>;
|
|
181
|
-
modelJsonSchemas: Record<string, Record<string, unknown>>;
|
|
182
|
-
modelRefs: Record<string, ModelReferenceMap>;
|
|
183
|
-
modelSubs: Record<string, string[]>;
|
|
184
|
-
modelAtts: Record<string, string[]>;
|
|
185
|
-
openApi: {
|
|
186
|
-
routes: OpenApiRouteDescriptor[];
|
|
187
|
-
options: Required<OpenApiRegistryOptions>;
|
|
188
|
-
};
|
|
189
|
-
};
|
|
190
|
-
restoreBootstrapSnapshot(snapshot: ReturnType<AccessRuntime['createBootstrapSnapshot']>): void;
|
|
191
|
-
/** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
|
|
192
|
-
setGlobalOptions(options: GlobalOptions): void;
|
|
193
|
-
setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]): void;
|
|
194
|
-
getGlobalOptions(): GlobalOptions;
|
|
195
|
-
getGlobalOption<K extends keyof GlobalOptions>(key: K, defaultValue?: GlobalOptions[K]): GlobalOptions[K];
|
|
196
|
-
/** Update model starting defaults shallowly; already-stored model values are not broadcast-replaced. */
|
|
197
|
-
setDefaultModelOptions(options: DefaultModelRouterOptions): void;
|
|
198
|
-
setDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, value: ExtendedDefaultModelRouterOptions[K]): void;
|
|
199
|
-
getDefaultModelOptions(): DefaultModelRouterOptions<unknown>;
|
|
200
|
-
getDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions<unknown>[K];
|
|
201
|
-
private createModelOptions;
|
|
202
|
-
private getOrCreateModelOptions;
|
|
203
|
-
/**
|
|
204
|
-
* Assign `{ ...modelDefaults, ...storedOptions, ...options }` shallowly. A supplied
|
|
205
|
-
* operationAccess/defaults object replaces that whole value. Defaults accept only
|
|
206
|
-
* query/presentation settings; virtual configuration is validated before assignment.
|
|
207
|
-
* Existing router.options remains its construction-time snapshot. Fresh model-service
|
|
208
|
-
* calls capture the owner's current options/defaults; in-flight calls retain their snapshot.
|
|
209
|
-
*/
|
|
210
|
-
setModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, options: ModelRouterOptions<TModel, TVirtuals>): void;
|
|
211
|
-
setModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K, value: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): void;
|
|
212
|
-
/** Frozen stored snapshot, not fully resolved per-path defaults. Read again for current configuration. */
|
|
213
|
-
getModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): ModelRouterOptions<TModel, TVirtuals>;
|
|
214
|
-
/**
|
|
215
|
-
* Generic stored exact → parent.default → parent lookup; only undefined falls through.
|
|
216
|
-
* The exact runtime-model-default path is the final fallback if the stored parent is
|
|
217
|
-
* absent. This is not the route-specific variant-to-base resolver; use
|
|
218
|
-
* getExactModelOption to inspect a variant including its exact model-default fallback.
|
|
219
|
-
*/
|
|
220
|
-
getModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
221
|
-
/** Exact stored path, then the same runtime-model-default path; no parent/default or variant-to-base lookup. */
|
|
222
|
-
getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
223
|
-
/**
|
|
224
|
-
* List registered virtual leaf names for a scope (VIRT-01).
|
|
225
|
-
* Scope containers (`{ sub }`) are not leaves. Inapplicable names are
|
|
226
|
-
* still listed here; applicability is decided per-access by
|
|
227
|
-
* {@link resolveVirtualDescriptor}. Request-controlled include collisions
|
|
228
|
-
* are validated by VIRT-05, not guessed here.
|
|
229
|
-
*/
|
|
230
|
-
getVirtualNames(modelName: string, scopePath?: string[]): string[];
|
|
231
|
-
/**
|
|
232
|
-
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
233
|
-
* Inapplicable registered names stay virtual (never persisted), even when
|
|
234
|
-
* no getter applies for the current access. Containers and persisted
|
|
235
|
-
* fields return false.
|
|
236
|
-
*/
|
|
237
|
-
isVirtualField(modelName: string, fieldName: string, scopePath?: string[]): boolean;
|
|
238
|
-
/**
|
|
239
|
-
* Resolve an applicable virtual descriptor with exact → `.default` → bare
|
|
240
|
-
* semantics (VIRT-01, VIRT-00A D1/D5). Returns `undefined` when no getter
|
|
241
|
-
* applies for `access`. Crucially, an access-record object is NEVER
|
|
242
|
-
* interpreted as a getter descriptor when the access is absent: an
|
|
243
|
-
* inapplicable registered name stays virtual (excluded from persisted
|
|
244
|
-
* projections, write admission, and DB sort/filter/distinct).
|
|
245
|
-
* Shared record-level `dependsOn` is inherited when a leaf lacks its own.
|
|
246
|
-
*/
|
|
247
|
-
resolveVirtualDescriptor(modelName: string, virtualName: string, access: string, scopePath?: string[]): {
|
|
248
|
-
get: (...args: never[]) => unknown;
|
|
249
|
-
dependsOn: string[];
|
|
250
|
-
} | undefined;
|
|
251
|
-
getModelNames(): string[];
|
|
252
|
-
hasModel(modelName: string): boolean;
|
|
253
|
-
getModelJsonSchema(modelName: string): Record<string, unknown>;
|
|
254
|
-
private createDataOptions;
|
|
255
|
-
private getOrCreateDataOptions;
|
|
256
|
-
setDataOptions<TData = unknown>(dataName: string, options: DataRouterOptions<TData>): void;
|
|
257
|
-
setDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: ExtendedDataRouterOptions<TData>[K]): void;
|
|
258
|
-
getDataOptions<TData = unknown>(dataName: string): DataRouterOptions<TData>;
|
|
259
|
-
/**
|
|
260
|
-
* Reusable configured data view: plain containers are frozen; native properties
|
|
261
|
-
* return detached mutable copies on access. Native identity is not stable.
|
|
262
|
-
* Replacement leaves previously captured views on their original data version.
|
|
263
|
-
*/
|
|
264
|
-
getDataSnapshot<TData = unknown>(dataName: string): readonly TData[];
|
|
265
|
-
getDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: ExtendedDataRouterOptions<TData>[K]): ExtendedDataRouterOptions<TData>[K];
|
|
266
|
-
getExactDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions<TData>[K];
|
|
267
|
-
getDataNames(): string[];
|
|
268
|
-
ensureModelMeta(modelName: string): void;
|
|
269
|
-
getModelRef(modelName: string, refPath: string): string | null;
|
|
270
|
-
getModelSub(modelName: string): string[];
|
|
271
|
-
getModelAtt(modelName: string): string[];
|
|
272
|
-
}
|
|
273
|
-
declare const defaultRuntime: AccessRuntime;
|
|
274
|
-
|
|
275
|
-
declare class DataCore {
|
|
276
|
-
private req;
|
|
277
|
-
private readonly requestRuntime;
|
|
278
|
-
private caches;
|
|
279
|
-
private cachedPermissions;
|
|
280
|
-
constructor(req: AccessRouterBaseRequest);
|
|
281
|
-
genIDFilter<TData = unknown>(dataName: string, id: string): Promise<Filter<TData>>;
|
|
282
|
-
genFilter<TData = unknown>(dataName: string, access?: BaseFilterAccess, _filter?: Filter<TData>): Promise<Filter<TData>>;
|
|
283
|
-
/** Request-schema allowlist, independent of doc values. No model hooks or backend elevation. */
|
|
284
|
-
genAllowedFields(dataName: string, _doc: unknown, access: SelectAccess, options?: DataFieldOptions): Promise<string[]>;
|
|
285
|
-
/** Request-only existing-value pick. No automatic identity/metadata retention; options default to {}. */
|
|
286
|
-
pickAllowedFields<TDoc>(dataName: string, doc: TDoc, access: SelectAccess, options?: DataFieldOptions): Promise<TDoc>;
|
|
287
|
-
/**
|
|
288
|
-
* Request-only requested-token intersection. Omitted/null/empty/exclusion-only select
|
|
289
|
-
* yields []; DataService then applies no extra projection after request trimming.
|
|
290
|
-
* Named options default to {}; legacy elevation and positional arguments are removed.
|
|
291
|
-
*/
|
|
292
|
-
genSelect(dataName: string, access: SelectAccess, targetFields?: Projection, options?: DataFieldOptions): Promise<string[]>;
|
|
293
|
-
private fieldPermissionSchema;
|
|
294
|
-
decorate<TDoc>(dataName: string, doc: TDoc, access: DecorateAccess, context?: DataHookContext): Promise<TDoc>;
|
|
295
|
-
decorateAll<TDoc>(dataName: string, docs: TDoc[], access: DecorateAllAccess, context?: DataHookContext): Promise<TDoc[]>;
|
|
296
|
-
getPermissions(): Permission;
|
|
297
|
-
setPermissions(): Promise<void>;
|
|
298
|
-
canActivate(routeGuard: Validation): Promise<boolean>;
|
|
299
|
-
isAllowed(dataName: string, access: RouteGuardAccess | string): Promise<boolean>;
|
|
300
|
-
/** Authorize route entry using explicit server-owned basic/advanced metadata. */
|
|
301
|
-
isAllowedRoute(dataName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise<boolean>;
|
|
302
|
-
getService<TData = unknown>(dataName: string): DataService<TData>;
|
|
303
|
-
service<TData = unknown>(dataName: string): DataService<TData>;
|
|
304
|
-
svc<TData = unknown>(dataName: string): DataService<TData>;
|
|
305
|
-
private getGlobalPermissions;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
declare const MIDDLEWARE: unique symbol;
|
|
309
|
-
declare const DATA_MIDDLEWARE: unique symbol;
|
|
310
|
-
declare const PERMISSIONS: unique symbol;
|
|
311
|
-
declare const PERMISSION_KEYS: unique symbol;
|
|
312
|
-
|
|
313
|
-
interface AccessRouterRequestExtensions {
|
|
314
|
-
macl?: Core;
|
|
315
|
-
dacl?: DataCore;
|
|
316
|
-
[PERMISSIONS]?: AccessRouterPermissions;
|
|
317
|
-
[PERMISSION_KEYS]?: string[];
|
|
318
|
-
[MIDDLEWARE]?: unknown[] | unknown;
|
|
319
|
-
[DATA_MIDDLEWARE]?: unknown[] | unknown;
|
|
320
|
-
[key: string]: unknown;
|
|
321
|
-
[key: symbol]: unknown;
|
|
322
|
-
}
|
|
323
|
-
type AccessRouterRequest = express.Request & AccessRouterRequestExtensions;
|
|
324
|
-
|
|
325
114
|
/** Request-only list arguments: advanced body fields, or root-entry args; no per-call authorization. */
|
|
326
115
|
interface PublicListArgs {
|
|
327
116
|
select?: Projection;
|
|
@@ -530,21 +319,525 @@ interface CorrelatedCountInclude extends CorrelatedIncludeBase {
|
|
|
530
319
|
filter: CorrelatedFilter;
|
|
531
320
|
id?: never;
|
|
532
321
|
}
|
|
533
|
-
type CorrelatedInclude = CorrelatedReadByIdInclude | CorrelatedReadByFilterInclude | CorrelatedListInclude | CorrelatedCountInclude;
|
|
534
|
-
/** Related output cannot overwrite authorization metadata at any supported nested level. */
|
|
535
|
-
type Include = LegacyInclude | CorrelatedInclude;
|
|
536
|
-
type FindAccess = 'list' | 'read';
|
|
537
|
-
type PopulateAccess = 'list' | 'read';
|
|
538
|
-
interface Populate {
|
|
539
|
-
path: string;
|
|
540
|
-
select?: Projection;
|
|
541
|
-
match?: unknown;
|
|
542
|
-
access?: PopulateAccess;
|
|
322
|
+
type CorrelatedInclude = CorrelatedReadByIdInclude | CorrelatedReadByFilterInclude | CorrelatedListInclude | CorrelatedCountInclude;
|
|
323
|
+
/** Related output cannot overwrite authorization metadata at any supported nested level. */
|
|
324
|
+
type Include = LegacyInclude | CorrelatedInclude;
|
|
325
|
+
type FindAccess = 'list' | 'read';
|
|
326
|
+
type PopulateAccess = 'list' | 'read';
|
|
327
|
+
interface Populate {
|
|
328
|
+
path: string;
|
|
329
|
+
select?: Projection;
|
|
330
|
+
match?: unknown;
|
|
331
|
+
access?: PopulateAccess;
|
|
332
|
+
}
|
|
333
|
+
interface SubPopulate {
|
|
334
|
+
path: string;
|
|
335
|
+
select?: Projection;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
339
|
+
type AccessRouterFieldKey<T> = [Extract<keyof T, string>] extends [never] ? string : Extract<keyof T, string>;
|
|
340
|
+
type IdentifierHook<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, id: string) => MaybePromise<Filter<TValue>>;
|
|
341
|
+
type GlobalPermissionValue = Record<string, boolean> | string[] | string | null | undefined;
|
|
342
|
+
type BaseFilterHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, permissions: AccessRouterPermissions) => MaybePromise<Filter | true | null | undefined>;
|
|
343
|
+
type OverrideFilterHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, filter: Filter, permissions: AccessRouterPermissions) => MaybePromise<Filter>;
|
|
344
|
+
type Hook<TValue, TContext, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: TContext) => MaybePromise<TValue>;
|
|
345
|
+
type HookChain<TValue, TContext, TRequest extends AccessRouterRequest = AccessRouterRequest> = Hook<TValue, TContext, TRequest> | Array<Hook<TValue, TContext, TRequest>>;
|
|
346
|
+
type ValidateRule = boolean | unknown[];
|
|
347
|
+
type ValidateHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, allowedData: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<ValidateRule>;
|
|
348
|
+
type DocPermissionsHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, doc: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<Record<string, unknown>>;
|
|
349
|
+
type ChangeHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, previousValue: unknown, nextValue: unknown, changes: Change[], context: ModelHookContext) => MaybePromise<void>;
|
|
350
|
+
type DeleteHook<TValue = unknown, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<void>;
|
|
351
|
+
type DocumentHook<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: ModelDocument<TValue>, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<ModelDocument<TValue>>;
|
|
352
|
+
type DocumentHookChain<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = DocumentHook<TValue, TRequest> | Array<DocumentHook<TValue, TRequest>>;
|
|
353
|
+
type ModelBaseFilterHook = BaseFilterHook<ModelRequest>;
|
|
354
|
+
type DataBaseFilterHook = BaseFilterHook<DataRequest>;
|
|
355
|
+
type ModelOverrideFilterHook = OverrideFilterHook<ModelRequest>;
|
|
356
|
+
type DataOverrideFilterHook = OverrideFilterHook<DataRequest>;
|
|
357
|
+
type ModelIdentifierHook<TValue = unknown> = IdentifierHook<TValue, ModelRequest>;
|
|
358
|
+
type DataIdentifierHook<TValue = unknown> = IdentifierHook<TValue, DataRequest>;
|
|
359
|
+
type ModelValidateHook = ValidateHook<ModelRequest>;
|
|
360
|
+
/**
|
|
361
|
+
* Request-user grants from untrimmed projected input. Declare private persisted inputs
|
|
362
|
+
* with alwaysSelectFields. Integrated output resolves once per document/access stage,
|
|
363
|
+
* independently of skim or metadata serialization; use the result's document handle
|
|
364
|
+
* for later picks after input/metadata trim. Backend grants do not forge user metadata.
|
|
365
|
+
*/
|
|
366
|
+
type ModelDocPermissionsHook = DocPermissionsHook<ModelRequest>;
|
|
367
|
+
type ModelChangeHook = ChangeHook<ModelRequest>;
|
|
368
|
+
type ModelDocumentHook<TValue = unknown> = DocumentHookChain<TValue, ModelRequest>;
|
|
369
|
+
type ModelDeleteHook<TValue = unknown> = DeleteHook<ModelDocument<TValue>, ModelRequest>;
|
|
370
|
+
type ModelHook<TValue = unknown> = HookChain<TValue, ModelHookContext, ModelRequest>;
|
|
371
|
+
type ModelListHook<TValue = unknown> = HookChain<TValue[], ModelHookContext, ModelRequest>;
|
|
372
|
+
type DataHook<TValue = unknown> = HookChain<TValue, DataHookContext, DataRequest>;
|
|
373
|
+
type DataListHook<TValue = unknown> = HookChain<TValue[], DataHookContext, DataRequest>;
|
|
374
|
+
/**
|
|
375
|
+
* Supported per-access virtual getter keys (VIRT-00A D1/D5).
|
|
376
|
+
* `default` is a fallback access; `delete`/`distinct`/`count` are NOT
|
|
377
|
+
* virtual accesses and must be rejected at configuration time.
|
|
378
|
+
*/
|
|
379
|
+
type ModelVirtualAccess = 'list' | 'create' | 'read' | 'update';
|
|
380
|
+
type ModelVirtualRecordAccess = 'default' | ModelVirtualAccess;
|
|
381
|
+
/**
|
|
382
|
+
* Scope-aware virtual getter context (VIRT-00A D2, frozen).
|
|
383
|
+
* Extends {@link ModelHookContext} with the receiving model, definition/
|
|
384
|
+
* permission `scopePath` (e.g. `[]` at root, `['contacts','sub']` for
|
|
385
|
+
* `contacts.sub.displayName`), and the three effective accesses. The
|
|
386
|
+
* initiating operation stays in `context.operation` and is never
|
|
387
|
+
* overwritten with fallback/effective visibility.
|
|
388
|
+
*
|
|
389
|
+
* Embedded outputs reuse the owning parent's internally computed grants with
|
|
390
|
+
* the scope's `permissionSchema.<field>.sub` rules; dedicated subdocument
|
|
391
|
+
* routes resolve grants from the raw parent lookup. Related (populate/
|
|
392
|
+
* include) targets finalize with the target model's own definitions,
|
|
393
|
+
* permissions, selection, and effective access before parent getters run.
|
|
394
|
+
* Declaration-scoped private inputs use the getter's fourth argument, never
|
|
395
|
+
* this context or its document-authorization metadata.
|
|
396
|
+
*/
|
|
397
|
+
interface ModelVirtualContext extends ModelHookContext {
|
|
398
|
+
receivingModelName: string;
|
|
399
|
+
scopePath: string[];
|
|
400
|
+
virtualAccess: ModelVirtualAccess;
|
|
401
|
+
outputAccess: ModelVirtualAccess;
|
|
402
|
+
docPermissionsAccess: ModelVirtualAccess;
|
|
403
|
+
}
|
|
404
|
+
type IsUnknownVirtual<T> = unknown extends T ? ([keyof T] extends [never] ? true : false) : false;
|
|
405
|
+
/**
|
|
406
|
+
* Typed isolated persisted view for a virtual getter scope (VIRT-00A D3/D4).
|
|
407
|
+
* Known models use a partial read-only persisted view (never
|
|
408
|
+
* `Record<string, unknown>`); unknown/untyped models keep an explicit
|
|
409
|
+
* loose fallback. This first argument retains the ordinary persisted/local
|
|
410
|
+
* dependency and finalized-public-related view. `dependsOnPopulate` does not
|
|
411
|
+
* retype persisted ref IDs or add its automatically retained refs here.
|
|
412
|
+
*/
|
|
413
|
+
type ModelVirtualDoc<TModel = unknown> = IsUnknownVirtual<TModel> extends true ? Readonly<Partial<Record<string, unknown>>> : [Extract<keyof TModel, string>] extends [never] ? Readonly<Partial<Record<string, unknown>>> : Readonly<Partial<TModel>>;
|
|
414
|
+
/**
|
|
415
|
+
* Server-owned, one-hop persisted input declaration. `path` is a scope-local
|
|
416
|
+
* ObjectId ref (scalar or 1D array) to a model already configured in this runtime.
|
|
417
|
+
* Embedded virtuals use their `.sub` scope's field, never a dotted source path.
|
|
418
|
+
* `select` uses Populate's string/array/1-or-minus-1 grammar; omitted/empty means
|
|
419
|
+
* all eligible static persisted fields. Target virtuals, metadata, dynamic paths
|
|
420
|
+
* and traversal through target refs are rejected. `_id` is included unless excluded.
|
|
421
|
+
* Static string refs must bind the actual same-connection ObjectId model; register
|
|
422
|
+
* its model instance AND options first. Validation is eager/atomic, including setters.
|
|
423
|
+
* This bypasses source/target input-field permissions, not the virtual's own output
|
|
424
|
+
* authorization. GlobalOptions.requireReadOperationAccessForPopulateDependencies defaults
|
|
425
|
+
* to true, requiring the captured caller's target base operationAccess.read for private
|
|
426
|
+
* loading. Only literal false bypasses that gate; private-only loading then adds no
|
|
427
|
+
* operation check solely for the fetch. Target overrideFilter.read/baseFilter.read and
|
|
428
|
+
* retained source IDs always use request authority/tenant scope, independently of ordinary
|
|
429
|
+
* populate match/access and authorization.related. Private target virtuals never run.
|
|
430
|
+
* In both modes, active selected/applicable dependency refs denied caller-context base
|
|
431
|
+
* read visibility are omitted entirely, including IDs; public list/custom access or a
|
|
432
|
+
* related-operation grant cannot override this. Allowed selected/permitted refs keep the
|
|
433
|
+
* original authorized populate mask or exact original refs. No active plan keeps ordinary
|
|
434
|
+
* behavior. The mode and source/target policy are captured before awaits; registration
|
|
435
|
+
* remains mandatory independently of requireRegisteredPopulateModels.
|
|
436
|
+
*/
|
|
437
|
+
interface ModelVirtualPopulateDependency<TModel = unknown> {
|
|
438
|
+
/** Top-level ref relative to this virtual's scope; no dotted source paths. Retained automatically. */
|
|
439
|
+
path: AccessRouterFieldKey<TModel>;
|
|
440
|
+
/** Persisted target mask: string, string[], or 1/-1 object (not 0). Empty/omitted selects all eligible fields. */
|
|
441
|
+
select?: Projection;
|
|
442
|
+
}
|
|
443
|
+
type VirtualPopulateMap<TModel, TPopulated> = {
|
|
444
|
+
[K in keyof TPopulated]: K extends AccessRouterFieldKey<TModel> ? unknown : never;
|
|
445
|
+
};
|
|
446
|
+
type VirtualPopulatePartial<T> = T extends Date | RegExp | ArrayBuffer | ArrayBufferView | {
|
|
447
|
+
readonly _bsontype: string;
|
|
448
|
+
} ? T : T extends readonly (infer U)[] ? readonly VirtualPopulatePartial<U>[] : T extends object ? {
|
|
449
|
+
readonly [K in keyof T]?: VirtualPopulatePartial<T[K]>;
|
|
450
|
+
} : T;
|
|
451
|
+
type VirtualPopulateKnownValue<TSource, TTarget> = TSource extends readonly unknown[] ? readonly (VirtualPopulatePartial<TTarget> | null)[] : VirtualPopulatePartial<TTarget>;
|
|
452
|
+
type VirtualPopulateValue<TSource, TTarget> = unknown extends TSource ? VirtualPopulatePartial<TTarget> | readonly (VirtualPopulatePartial<TTarget> | null)[] | null : VirtualPopulateKnownValue<NonNullable<TSource>, TTarget> | null;
|
|
453
|
+
/**
|
|
454
|
+
* Declaration-scoped fourth getter input. Explicitly map source keys to target
|
|
455
|
+
* persisted shapes; IDs cannot infer a target model. Each key and target field
|
|
456
|
+
* may be missing, scalar misses are null, and arrays preserve null slots. Source
|
|
457
|
+
* cardinality determines the view (unknown sources permit scalar or array).
|
|
458
|
+
* Containers are deeply readonly/partial; native scalar types are preserved.
|
|
459
|
+
* Each getter/row/array slot receives isolated copies, including native mutable
|
|
460
|
+
* leaves. Native methods are not made immutable by this type. Hydrated and lean
|
|
461
|
+
* materialization can differ; this is not a JSON-value conversion promise.
|
|
462
|
+
* This annotation does not select fields or confer authority. No implicit transport
|
|
463
|
+
* to DTOs, hooks, handles or metadata; an authorized getter's deliberate return is output.
|
|
464
|
+
*/
|
|
465
|
+
type ModelVirtualPopulateInputs<TModel = unknown, TPopulated extends VirtualPopulateMap<TModel, TPopulated> = Record<never, never>> = {
|
|
466
|
+
readonly [K in keyof TPopulated]?: VirtualPopulateValue<K extends keyof TModel ? TModel[K] : unknown, TPopulated[K]>;
|
|
467
|
+
};
|
|
468
|
+
/**
|
|
469
|
+
* Virtual getter signature (VIRT-00A D3/D4, frozen).
|
|
470
|
+
* Its first argument is an isolated plain-object snapshot of ordinary persisted/
|
|
471
|
+
* local-dependency inputs + finalized public related/embedded outputs for its scope.
|
|
472
|
+
* Sibling virtual values are not visible. Input mutations have no implicit effect
|
|
473
|
+
* on response data or lifecycle snapshots. Only the returned field value is committed;
|
|
474
|
+
* `undefined` omits the field; throws omit fail-closed. `this` is the
|
|
475
|
+
* model request. The optional fourth argument contains only this getter's
|
|
476
|
+
* declared private populated inputs; default it to `{}` or guard undefined.
|
|
477
|
+
* It is separate from `doc`, whose persisted/public-related typing is unchanged.
|
|
478
|
+
* The runtime supplies it (empty without a declaration); the empty default generic
|
|
479
|
+
* guesses no target shape. Optionality preserves legacy three-argument direct calls
|
|
480
|
+
* and callbacks. Absent source refs or unavailable private inputs skip invocation;
|
|
481
|
+
* present null/empty refs and missing target rows yield null/[]/null array slots.
|
|
482
|
+
* Private values are not implicitly transported to output or authorization metadata;
|
|
483
|
+
* an authorized getter can deliberately return a computed value using them.
|
|
484
|
+
*/
|
|
485
|
+
type ModelVirtualGetter<TModel = unknown, TValue = unknown, TPopulated extends VirtualPopulateMap<TModel, TPopulated> = Record<never, never>> = (this: ModelRequest, doc: ModelVirtualDoc<TModel>, permissions: AccessRouterPermissions, context: ModelVirtualContext, populated?: ModelVirtualPopulateInputs<TModel, TPopulated>) => MaybePromise<TValue | undefined>;
|
|
486
|
+
/**
|
|
487
|
+
* Leaf virtual descriptor: `{ get, dependsOn?, dependsOnPopulate? }`.
|
|
488
|
+
* `dependsOn` lists top-level persisted field names relative to the
|
|
489
|
+
* definition's scope (e.g. `['displayName']` inside `contacts.sub`, never
|
|
490
|
+
* `['contacts.sub.displayName']`). Dotted paths and virtual-to-virtual
|
|
491
|
+
* dependencies are rejected at configuration time.
|
|
492
|
+
*
|
|
493
|
+
* The getter runs after `toObject()`/lean normalization and after internal
|
|
494
|
+
* document-permissions computation, with global permissions available and
|
|
495
|
+
* before `decorate`/`decorateAll`/tasks. Only the returned field value is
|
|
496
|
+
* committed via document-aware helpers: `undefined` (or a throw) omits the
|
|
497
|
+
* field fail-closed and computed values never persist. Getters see an
|
|
498
|
+
* isolated plain-object view for both lean and hydrated results; mutating the
|
|
499
|
+
* input has no implicit effect on response data or lifecycle snapshots.
|
|
500
|
+
*
|
|
501
|
+
* Annotate a descriptor separately with `TPopulated` to type its private fourth
|
|
502
|
+
* getter argument, then assign it to ordinary `ModelVirtuals`. The optional
|
|
503
|
+
* generic never changes the persisted model, write types or public output.
|
|
504
|
+
* Use `populated = {}` or guard undefined to retain three-argument direct calls.
|
|
505
|
+
* See the shipped README's "Private populated dependencies" for registration,
|
|
506
|
+
* reference omission/population/ID shapes, captured private-read mode, mandatory row
|
|
507
|
+
* gates and absence/error rules. Private computation does not imply reference visibility.
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* import type { ModelVirtualDescriptor } from '@web-ts-toolkit/access-router';
|
|
512
|
+
* import type { Types } from 'mongoose';
|
|
513
|
+
* interface Order { total: number; account: Types.ObjectId | null; }
|
|
514
|
+
* interface Account { balance: number; creditLimit: number; }
|
|
515
|
+
* const descriptor: ModelVirtualDescriptor<Order, boolean, { account: Account }> = {
|
|
516
|
+
* dependsOn: ['total'],
|
|
517
|
+
* dependsOnPopulate: [{ path: 'account', select: ['balance', 'creditLimit'] }],
|
|
518
|
+
* get(doc, _permissions, _context, populated = {}) {
|
|
519
|
+
* const account = populated.account;
|
|
520
|
+
* if (doc.total === undefined || account?.balance === undefined || account.creditLimit === undefined)
|
|
521
|
+
* return undefined;
|
|
522
|
+
* return doc.total <= account.balance + account.creditLimit;
|
|
523
|
+
* },
|
|
524
|
+
* };
|
|
525
|
+
* ```
|
|
526
|
+
*/
|
|
527
|
+
interface ModelVirtualDescriptor<TModel = unknown, TValue = unknown, TPopulated extends VirtualPopulateMap<TModel, TPopulated> = Record<never, never>> {
|
|
528
|
+
/** Only this return becomes virtual output; private inputs are not implicitly transported. */
|
|
529
|
+
get: ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
530
|
+
/** Local top-level persisted input names relative to the definition's scope. */
|
|
531
|
+
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
532
|
+
/** Server-only private fourth-argument inputs; source refs retained automatically. `[]` clears a shared list. */
|
|
533
|
+
dependsOnPopulate?: ModelVirtualPopulateDependency<TModel>[];
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Per-access virtual record (VIRT-01): `default` + `list`/`create`/`read`/
|
|
537
|
+
* `update` (NOT `delete`/`distinct`/`count`). Each present access holds a
|
|
538
|
+
* leaf descriptor or bare getter shorthand. An optional shared `dependsOn`
|
|
539
|
+
* supports the documented `{ dependsOn, read: getter }` shape; a leaf's own
|
|
540
|
+
* `dependsOn` wins over the shared list during resolution. `dependsOnPopulate`
|
|
541
|
+
* inherits independently by the same rule: undefined inherits, a defined leaf
|
|
542
|
+
* list replaces the shared list wholesale (including `[]`).
|
|
543
|
+
*/
|
|
544
|
+
type ModelVirtualAccessRecord<TModel = unknown, TValue = unknown, TPopulated extends VirtualPopulateMap<TModel, TPopulated> = Record<never, never>> = {
|
|
545
|
+
default?: ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
546
|
+
list?: ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
547
|
+
create?: ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
548
|
+
read?: ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
549
|
+
update?: ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualGetter<TModel, TValue, TPopulated>;
|
|
550
|
+
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
551
|
+
/** Shared private input list; each resolved getter gets only its own mask. A leaf's defined list replaces it. */
|
|
552
|
+
dependsOnPopulate?: ModelVirtualPopulateDependency<TModel>[];
|
|
553
|
+
};
|
|
554
|
+
/** Single virtual entry: bare getter, leaf descriptor, or per-access record. */
|
|
555
|
+
type ModelVirtualLeaf<TModel = unknown, TValue = unknown, TPopulated extends VirtualPopulateMap<TModel, TPopulated> = Record<never, never>> = ModelVirtualGetter<TModel, TValue, TPopulated> | ModelVirtualDescriptor<TModel, TValue, TPopulated> | ModelVirtualAccessRecord<TModel, TValue, TPopulated>;
|
|
556
|
+
type UnwrapVirtualArray<T> = T extends readonly (infer U)[] ? U : T;
|
|
557
|
+
/**
|
|
558
|
+
* Persisted sub-scope model for an embedded container field (VIRT-01).
|
|
559
|
+
* Unwraps arrays/nullable wrappers to the element/object type; unknown
|
|
560
|
+
* fields fall back to a loose record.
|
|
561
|
+
*/
|
|
562
|
+
type VirtualSubModel<TModel, TField extends string> = TField extends keyof TModel ? UnwrapVirtualArray<NonNullable<TModel[TField]>> : Record<string, unknown>;
|
|
563
|
+
type LooseVirtualLeaf = ModelVirtualGetter<Record<string, unknown>, unknown> | ModelVirtualDescriptor<Record<string, unknown>, unknown> | ModelVirtualAccessRecord<Record<string, unknown>, unknown>;
|
|
564
|
+
type LooseVirtualSubEntry = LooseVirtualLeaf | {
|
|
565
|
+
sub?: Record<string, LooseVirtualLeaf | {
|
|
566
|
+
sub?: Record<string, LooseVirtualLeaf>;
|
|
567
|
+
}>;
|
|
568
|
+
};
|
|
569
|
+
/**
|
|
570
|
+
* Virtual definitions for a model scope (VIRT-01).
|
|
571
|
+
* Root leaves come from `TVirtuals` (strict: unknown persisted getter
|
|
572
|
+
* fields are type errors). Embedded containers mirror
|
|
573
|
+
* `permissionSchema.<field>.sub`: any persisted field may host a `sub`
|
|
574
|
+
* record whose leaves use the container's persisted sub-model view.
|
|
575
|
+
* Untyped models keep an explicit loose record fallback.
|
|
576
|
+
*
|
|
577
|
+
* Selected virtual outputs are optional (`Partial`): authorization, absent
|
|
578
|
+
* dependencies, `undefined`, and fail-closed errors can omit them. Virtual
|
|
579
|
+
* names are never added to persisted `Filter`, sort/distinct inputs, or
|
|
580
|
+
* client write types.
|
|
581
|
+
*
|
|
582
|
+
* @example End-to-end typed virtual (persisted `User` has no `fullAddress`):
|
|
583
|
+
* ```ts
|
|
584
|
+
* import acl from '@web-ts-toolkit/access-router';
|
|
585
|
+
* import type { SelectedPublicOutput } from '@web-ts-toolkit/access-router/advanced';
|
|
586
|
+
* interface User { name: string; address: string; }
|
|
587
|
+
* interface UserVirtuals { fullAddress: string; }
|
|
588
|
+
* const router = acl.createRouter<User, UserVirtuals>('User', {
|
|
589
|
+
* permissionSchema: {
|
|
590
|
+
* name: { read: true },
|
|
591
|
+
* address: { read: 'canViewAddress' },
|
|
592
|
+
* fullAddress: { read: 'canViewAddress' },
|
|
593
|
+
* },
|
|
594
|
+
* virtuals: {
|
|
595
|
+
* fullAddress: {
|
|
596
|
+
* dependsOn: ['address'],
|
|
597
|
+
* read: async function (doc) {
|
|
598
|
+
* if (doc.address === undefined) return undefined;
|
|
599
|
+
* return `addr:${doc.address}`;
|
|
600
|
+
* },
|
|
601
|
+
* },
|
|
602
|
+
* },
|
|
603
|
+
* });
|
|
604
|
+
* type Out = SelectedPublicOutput<User, ['name', 'fullAddress'], UserVirtuals>;
|
|
605
|
+
* const out: Out = { name: 'Ada', fullAddress: 'addr:x' };
|
|
606
|
+
* const omitted: Out = { name: 'Ada' }; // virtual is optional when denied/missing
|
|
607
|
+
* void [router, out, omitted];
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
type ModelVirtuals<TModel = unknown, TVirtuals extends object = Record<never, never>> = IsUnknownVirtual<TModel> extends true ? Record<string, LooseVirtualSubEntry> : {
|
|
611
|
+
[K in Extract<keyof TVirtuals, string>]?: ModelVirtualLeaf<TModel, TVirtuals[K]>;
|
|
612
|
+
} & {
|
|
613
|
+
[K in Extract<keyof TModel, string>]?: {
|
|
614
|
+
sub?: Record<string, ModelVirtualLeaf<VirtualSubModel<TModel, K>, unknown> | {
|
|
615
|
+
sub?: Record<string, LooseVirtualLeaf>;
|
|
616
|
+
}>;
|
|
617
|
+
};
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
interface ResolvedVirtualDescriptor {
|
|
621
|
+
get: (...args: never[]) => unknown;
|
|
622
|
+
dependsOn: string[];
|
|
623
|
+
dependsOnPopulate?: ModelVirtualPopulateDependency[];
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
type ModelReferenceMap = {
|
|
627
|
+
[key: string]: string | ModelReferenceMap;
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* Owner of global/model/data options, registered Mongoose models and OpenAPI routes.
|
|
631
|
+
* Prefer createAccessRuntime() for a callable API with factories and bound setters;
|
|
632
|
+
* its `.runtime` exposes this owner. A bare instance has no createRouter method.
|
|
633
|
+
* Registries/configuration are isolated; the Mongoose jsonSchema extension is initialized
|
|
634
|
+
* idempotently. The defaultRuntime additionally adopts process-global named models.
|
|
635
|
+
*/
|
|
636
|
+
declare class AccessRuntime {
|
|
637
|
+
private readonly allowGlobalModelLookup;
|
|
638
|
+
constructor(options?: {
|
|
639
|
+
allowGlobalModelLookup?: boolean;
|
|
640
|
+
});
|
|
641
|
+
private readonly globalOptions;
|
|
642
|
+
private readonly defaultModelOptions;
|
|
643
|
+
private readonly modelOptions;
|
|
644
|
+
private readonly modelJsonSchemas;
|
|
645
|
+
private readonly dataOptions;
|
|
646
|
+
private readonly modelRefs;
|
|
647
|
+
private readonly modelSubs;
|
|
648
|
+
private readonly modelAtts;
|
|
649
|
+
private readonly modelInstances;
|
|
650
|
+
private readonly openApiRegistry;
|
|
651
|
+
private openApiPathPrefix;
|
|
652
|
+
registerModelInstance<TModel>(modelName: string, model: mongoose.Model<TModel>): void;
|
|
653
|
+
hasModelInstance(modelName: string): boolean;
|
|
654
|
+
getModelInstance<TModel = unknown>(modelName: string): mongoose.Model<TModel> | null;
|
|
655
|
+
registerOpenApiRoute(route: OpenApiRouteDescriptor): void;
|
|
656
|
+
/**
|
|
657
|
+
* Compose OpenAPI paths while synchronously constructing mounted routers.
|
|
658
|
+
* Prefixes apply before collision checks; prior descriptors and Express paths
|
|
659
|
+
* are unchanged. Nested calls compose and the prior scope is restored even on
|
|
660
|
+
* failure. The callback must finish registration synchronously (no async work).
|
|
661
|
+
*/
|
|
662
|
+
withOpenApiPathPrefix<T>(prefix: string, build: () => T): T;
|
|
663
|
+
/**
|
|
664
|
+
* Enable strict OpenAPI collision detection for the remainder of this
|
|
665
|
+
* runtime's lifetime. After this call, conflicting method/path
|
|
666
|
+
* registrations and duplicate operationIds throw {@link OpenApiCollisionError}
|
|
667
|
+
* instead of silently replacing. Existing already-registered routes are
|
|
668
|
+
* not retroactively re-validated.
|
|
669
|
+
*/
|
|
670
|
+
enableOpenApiCollisionDetection(): void;
|
|
671
|
+
getOpenApiRoutes(): OpenApiRouteDescriptor[];
|
|
672
|
+
clearOpenApiRoutes(): void;
|
|
673
|
+
getOpenApiSpec(info: OpenApiDocumentOptions): {
|
|
674
|
+
openapi: string;
|
|
675
|
+
info: Omit<OpenApiDocumentOptions, "servers">;
|
|
676
|
+
servers?: OpenApiDocumentOptions["servers"];
|
|
677
|
+
paths: Record<string, Record<string, {
|
|
678
|
+
[key: `x-${string}`]: unknown;
|
|
679
|
+
operationId?: string;
|
|
680
|
+
summary?: string;
|
|
681
|
+
description?: string;
|
|
682
|
+
tags?: string[];
|
|
683
|
+
deprecated?: boolean;
|
|
684
|
+
parameters?: OpenApiParameter[];
|
|
685
|
+
requestBody?: {
|
|
686
|
+
required?: boolean;
|
|
687
|
+
content: Record<string, {
|
|
688
|
+
schema: OpenApiSchema;
|
|
689
|
+
}>;
|
|
690
|
+
};
|
|
691
|
+
responses: OpenApiResponses;
|
|
692
|
+
}>>;
|
|
693
|
+
};
|
|
694
|
+
createBootstrapSnapshot(): {
|
|
695
|
+
globalOptions: GlobalOptions;
|
|
696
|
+
defaultModelOptions: DefaultModelRouterOptions;
|
|
697
|
+
modelOptions: Record<string, ModelRouterOptions>;
|
|
698
|
+
dataOptions: Record<string, DataRouterOptions>;
|
|
699
|
+
modelInstances: Record<string, mongoose.Model<unknown>>;
|
|
700
|
+
modelJsonSchemas: Record<string, Record<string, unknown>>;
|
|
701
|
+
modelRefs: Record<string, ModelReferenceMap>;
|
|
702
|
+
modelSubs: Record<string, string[]>;
|
|
703
|
+
modelAtts: Record<string, string[]>;
|
|
704
|
+
openApi: {
|
|
705
|
+
routes: OpenApiRouteDescriptor[];
|
|
706
|
+
options: Required<OpenApiRegistryOptions>;
|
|
707
|
+
};
|
|
708
|
+
};
|
|
709
|
+
restoreBootstrapSnapshot(snapshot: ReturnType<AccessRuntime['createBootstrapSnapshot']>): void;
|
|
710
|
+
/** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
|
|
711
|
+
setGlobalOptions(options: GlobalOptions): void;
|
|
712
|
+
setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]): void;
|
|
713
|
+
getGlobalOptions(): GlobalOptions;
|
|
714
|
+
getGlobalOption<K extends keyof GlobalOptions>(key: K, defaultValue?: GlobalOptions[K]): GlobalOptions[K];
|
|
715
|
+
/** Update model starting defaults shallowly; already-stored model values are not broadcast-replaced. */
|
|
716
|
+
setDefaultModelOptions(options: DefaultModelRouterOptions): void;
|
|
717
|
+
setDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, value: ExtendedDefaultModelRouterOptions[K]): void;
|
|
718
|
+
getDefaultModelOptions(): DefaultModelRouterOptions<unknown>;
|
|
719
|
+
getDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions<unknown>[K];
|
|
720
|
+
private createModelOptions;
|
|
721
|
+
private getOrCreateModelOptions;
|
|
722
|
+
/**
|
|
723
|
+
* Assign `{ ...modelDefaults, ...storedOptions, ...options }` shallowly. A supplied
|
|
724
|
+
* operationAccess/defaults object replaces that whole value. Defaults accept only
|
|
725
|
+
* query/presentation settings; virtual configuration is validated before assignment.
|
|
726
|
+
* Existing router.options remains its construction-time snapshot. Fresh model-service
|
|
727
|
+
* calls capture the owner's current options/defaults; in-flight calls retain their snapshot.
|
|
728
|
+
*/
|
|
729
|
+
setModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, options: ModelRouterOptions<TModel, TVirtuals>): void;
|
|
730
|
+
setModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K, value: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): void;
|
|
731
|
+
/** Frozen stored snapshot, not fully resolved per-path defaults. Read again for current configuration. */
|
|
732
|
+
getModelOptions<TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string): ModelRouterOptions<TModel, TVirtuals>;
|
|
733
|
+
/**
|
|
734
|
+
* Generic stored exact → parent.default → parent lookup; only undefined falls through.
|
|
735
|
+
* The exact runtime-model-default path is the final fallback if the stored parent is
|
|
736
|
+
* absent. This is not the route-specific variant-to-base resolver; use
|
|
737
|
+
* getExactModelOption to inspect a variant including its exact model-default fallback.
|
|
738
|
+
*/
|
|
739
|
+
getModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions<TModel, TVirtuals>[K]): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
740
|
+
/** Exact stored path, then the same runtime-model-default path; no parent/default or variant-to-base lookup. */
|
|
741
|
+
getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel, TVirtuals>, TModel = unknown, TVirtuals extends object = Record<never, never>>(modelName: string, key: K | string): ExtendedModelRouterOptions<TModel, TVirtuals>[K];
|
|
742
|
+
/**
|
|
743
|
+
* List registered virtual leaf names for a scope (VIRT-01).
|
|
744
|
+
* Scope containers (`{ sub }`) are not leaves. Inapplicable names are
|
|
745
|
+
* still listed here; applicability is decided per-access by
|
|
746
|
+
* {@link resolveVirtualDescriptor}. Request-controlled include collisions
|
|
747
|
+
* are validated by VIRT-05, not guessed here.
|
|
748
|
+
*/
|
|
749
|
+
getVirtualNames(modelName: string, scopePath?: string[]): string[];
|
|
750
|
+
/**
|
|
751
|
+
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
752
|
+
* Inapplicable registered names stay virtual (never persisted), even when
|
|
753
|
+
* no getter applies for the current access. Containers and persisted
|
|
754
|
+
* fields return false.
|
|
755
|
+
*/
|
|
756
|
+
isVirtualField(modelName: string, fieldName: string, scopePath?: string[]): boolean;
|
|
757
|
+
/**
|
|
758
|
+
* Resolve an applicable virtual descriptor with exact → `.default` → bare
|
|
759
|
+
* semantics (VIRT-01, VIRT-00A D1/D5). Returns `undefined` when no getter
|
|
760
|
+
* applies for `access`. Crucially, an access-record object is NEVER
|
|
761
|
+
* interpreted as a getter descriptor when the access is absent: an
|
|
762
|
+
* inapplicable registered name stays virtual (excluded from persisted
|
|
763
|
+
* projections, write admission, and DB sort/filter/distinct).
|
|
764
|
+
* Shared `dependsOn` and `dependsOnPopulate` lists inherit independently;
|
|
765
|
+
* each defined leaf list replaces its shared list wholesale.
|
|
766
|
+
*/
|
|
767
|
+
resolveVirtualDescriptor(modelName: string, virtualName: string, access: string, scopePath?: string[]): ResolvedVirtualDescriptor | undefined;
|
|
768
|
+
getModelNames(): string[];
|
|
769
|
+
hasModel(modelName: string): boolean;
|
|
770
|
+
getModelJsonSchema(modelName: string): Record<string, unknown>;
|
|
771
|
+
private createDataOptions;
|
|
772
|
+
private getOrCreateDataOptions;
|
|
773
|
+
setDataOptions<TData = unknown>(dataName: string, options: DataRouterOptions<TData>): void;
|
|
774
|
+
setDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: ExtendedDataRouterOptions<TData>[K]): void;
|
|
775
|
+
getDataOptions<TData = unknown>(dataName: string): DataRouterOptions<TData>;
|
|
776
|
+
/**
|
|
777
|
+
* Reusable configured data view: plain containers are frozen; native properties
|
|
778
|
+
* return detached mutable copies on access. Native identity is not stable.
|
|
779
|
+
* Replacement leaves previously captured views on their original data version.
|
|
780
|
+
*/
|
|
781
|
+
getDataSnapshot<TData = unknown>(dataName: string): readonly TData[];
|
|
782
|
+
getDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: ExtendedDataRouterOptions<TData>[K]): ExtendedDataRouterOptions<TData>[K];
|
|
783
|
+
getExactDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions<TData>[K];
|
|
784
|
+
getDataNames(): string[];
|
|
785
|
+
ensureModelMeta(modelName: string): void;
|
|
786
|
+
getModelRef(modelName: string, refPath: string): string | null;
|
|
787
|
+
getModelSub(modelName: string): string[];
|
|
788
|
+
getModelAtt(modelName: string): string[];
|
|
789
|
+
}
|
|
790
|
+
declare const defaultRuntime: AccessRuntime;
|
|
791
|
+
|
|
792
|
+
declare class DataCore {
|
|
793
|
+
private req;
|
|
794
|
+
private readonly requestRuntime;
|
|
795
|
+
private caches;
|
|
796
|
+
private cachedPermissions;
|
|
797
|
+
constructor(req: AccessRouterBaseRequest);
|
|
798
|
+
genIDFilter<TData = unknown>(dataName: string, id: string): Promise<Filter<TData>>;
|
|
799
|
+
genFilter<TData = unknown>(dataName: string, access?: BaseFilterAccess, _filter?: Filter<TData>): Promise<Filter<TData>>;
|
|
800
|
+
/** Request-schema allowlist, independent of doc values. No model hooks or backend elevation. */
|
|
801
|
+
genAllowedFields(dataName: string, _doc: unknown, access: SelectAccess, options?: DataFieldOptions): Promise<string[]>;
|
|
802
|
+
/** Request-only existing-value pick. No automatic identity/metadata retention; options default to {}. */
|
|
803
|
+
pickAllowedFields<TDoc>(dataName: string, doc: TDoc, access: SelectAccess, options?: DataFieldOptions): Promise<TDoc>;
|
|
804
|
+
/**
|
|
805
|
+
* Request-only requested-token intersection. Omitted/null/empty/exclusion-only select
|
|
806
|
+
* yields []; DataService then applies no extra projection after request trimming.
|
|
807
|
+
* Named options default to {}; legacy elevation and positional arguments are removed.
|
|
808
|
+
*/
|
|
809
|
+
genSelect(dataName: string, access: SelectAccess, targetFields?: Projection, options?: DataFieldOptions): Promise<string[]>;
|
|
810
|
+
private fieldPermissionSchema;
|
|
811
|
+
decorate<TDoc>(dataName: string, doc: TDoc, access: DecorateAccess, context?: DataHookContext): Promise<TDoc>;
|
|
812
|
+
decorateAll<TDoc>(dataName: string, docs: TDoc[], access: DecorateAllAccess, context?: DataHookContext): Promise<TDoc[]>;
|
|
813
|
+
getPermissions(): Permission;
|
|
814
|
+
setPermissions(): Promise<void>;
|
|
815
|
+
canActivate(routeGuard: Validation): Promise<boolean>;
|
|
816
|
+
isAllowed(dataName: string, access: RouteGuardAccess | string): Promise<boolean>;
|
|
817
|
+
/** Authorize route entry using explicit server-owned basic/advanced metadata. */
|
|
818
|
+
isAllowedRoute(dataName: string, baseAccess: RouteGuardAccess | string, variant: RouteVariant): Promise<boolean>;
|
|
819
|
+
getService<TData = unknown>(dataName: string): DataService<TData>;
|
|
820
|
+
service<TData = unknown>(dataName: string): DataService<TData>;
|
|
821
|
+
svc<TData = unknown>(dataName: string): DataService<TData>;
|
|
822
|
+
private getGlobalPermissions;
|
|
543
823
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
824
|
+
|
|
825
|
+
declare const MIDDLEWARE: unique symbol;
|
|
826
|
+
declare const DATA_MIDDLEWARE: unique symbol;
|
|
827
|
+
declare const PERMISSIONS: unique symbol;
|
|
828
|
+
declare const PERMISSION_KEYS: unique symbol;
|
|
829
|
+
|
|
830
|
+
interface AccessRouterRequestExtensions {
|
|
831
|
+
macl?: Core;
|
|
832
|
+
dacl?: DataCore;
|
|
833
|
+
[PERMISSIONS]?: AccessRouterPermissions;
|
|
834
|
+
[PERMISSION_KEYS]?: string[];
|
|
835
|
+
[MIDDLEWARE]?: unknown[] | unknown;
|
|
836
|
+
[DATA_MIDDLEWARE]?: unknown[] | unknown;
|
|
837
|
+
[key: string]: unknown;
|
|
838
|
+
[key: symbol]: unknown;
|
|
547
839
|
}
|
|
840
|
+
type AccessRouterRequest = express.Request & AccessRouterRequestExtensions;
|
|
548
841
|
|
|
549
842
|
/**
|
|
550
843
|
* Bounded persisted/registered-virtual response paths, including embedded array children.
|
|
@@ -798,6 +1091,37 @@ type AllowedOutputFieldOptions<TModel = unknown, TVirtuals extends object = Reco
|
|
|
798
1091
|
readonly purpose: 'output';
|
|
799
1092
|
};
|
|
800
1093
|
|
|
1094
|
+
/** Package-private D2 validation/capture. Terminal persisted inputs, never output grants or target I/O. */
|
|
1095
|
+
|
|
1096
|
+
interface VirtualPopulateTarget {
|
|
1097
|
+
/** Stable per-capture identity for grouping even when native policy values are copied on read. */
|
|
1098
|
+
readonly identity: object;
|
|
1099
|
+
readonly runtime: AccessRuntime;
|
|
1100
|
+
readonly modelName: string;
|
|
1101
|
+
readonly model: Model<unknown>;
|
|
1102
|
+
/** Binding identity only. Execution uses the owned catalog/selections, not live schema inspection. */
|
|
1103
|
+
readonly schema: Schema;
|
|
1104
|
+
/** Terminal read policy: no target virtual registry or private dependency graph. */
|
|
1105
|
+
readonly configuration: AuthorizationConfiguration;
|
|
1106
|
+
}
|
|
1107
|
+
interface CapturedVirtualPopulateDependency {
|
|
1108
|
+
/** Scope-local path and absolute persisted source path (no `.sub` markers). */
|
|
1109
|
+
readonly path: string;
|
|
1110
|
+
readonly sourcePath: string;
|
|
1111
|
+
readonly cardinality: 'scalar' | 'array';
|
|
1112
|
+
readonly target: VirtualPopulateTarget;
|
|
1113
|
+
/** Eligible leaf mask for this getter only; never a public output selection. */
|
|
1114
|
+
readonly selection: readonly string[];
|
|
1115
|
+
/** Positive persisted paths, including association identity even for `-_id`. */
|
|
1116
|
+
readonly fetchSelection: readonly string[];
|
|
1117
|
+
readonly includeId: boolean;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* An opaque function identity survives configuration copying without copying a
|
|
1121
|
+
* Mongoose schema/runtime graph. Bindings live only here, not on DTOs or handles.
|
|
1122
|
+
*/
|
|
1123
|
+
type VirtualPopulateSnapshotToken = () => void;
|
|
1124
|
+
|
|
801
1125
|
interface AuthorizationBinding {
|
|
802
1126
|
readonly request: AccessRouterRequest;
|
|
803
1127
|
readonly runtime: object;
|
|
@@ -827,6 +1151,12 @@ interface AuthorizationFieldCatalog {
|
|
|
827
1151
|
}
|
|
828
1152
|
interface AuthorizationConfiguration {
|
|
829
1153
|
readonly catalog: AuthorizationFieldCatalog;
|
|
1154
|
+
/** Opaque terminal private-input capture. Never related authority or public document metadata. */
|
|
1155
|
+
readonly virtualPopulateSnapshot?: VirtualPopulateSnapshotToken;
|
|
1156
|
+
/** Runtime-owned private input admission mode, not public output/related authority. */
|
|
1157
|
+
readonly requireReadOperationAccessForPopulateDependencies?: boolean;
|
|
1158
|
+
/** Independent ordinary-populate registration posture from the same global snapshot. */
|
|
1159
|
+
readonly requireRegisteredPopulateModels?: boolean;
|
|
830
1160
|
readonly documentPermissionField?: string;
|
|
831
1161
|
/** Captured permission schema, virtual descriptors, always-select and metadata posture. */
|
|
832
1162
|
readonly [key: string]: unknown;
|
|
@@ -836,9 +1166,15 @@ interface AuthorizationRelatedTarget {
|
|
|
836
1166
|
readonly path: string;
|
|
837
1167
|
readonly modelName: string;
|
|
838
1168
|
readonly configuration: AuthorizationConfiguration;
|
|
1169
|
+
/** Effective include args/options, resolved with that target's captured defaults. */
|
|
1170
|
+
readonly queryIntent?: ModelServiceIntent;
|
|
1171
|
+
/** Existing include operation gate, resolved synchronously with its target. */
|
|
1172
|
+
readonly operationGuard?: unknown;
|
|
839
1173
|
/** Passthrough is the existing global unregistered-populate opt-out, not a caller policy. */
|
|
840
1174
|
readonly kind?: 'output' | 'count' | 'passthrough';
|
|
841
1175
|
readonly relatedTargets?: readonly AuthorizationRelatedTarget[];
|
|
1176
|
+
/** Captured supported raw-ref transport shape; exotic ordinary populates keep their adapter path. */
|
|
1177
|
+
readonly staticObjectIdReference?: boolean;
|
|
842
1178
|
}
|
|
843
1179
|
declare const resolvedAuthorizationBrand: unique symbol;
|
|
844
1180
|
interface ResolvedServiceAuthorization {
|
|
@@ -883,6 +1219,7 @@ type FieldAuthorizationState = 'granted' | 'deferred' | 'denied';
|
|
|
883
1219
|
type VirtualProjectionAccess = 'list' | 'create' | 'read' | 'update' | (string & {});
|
|
884
1220
|
type VirtualCandidateAuthState = 'granted' | 'deferred';
|
|
885
1221
|
interface VirtualProjectionSnapshot {
|
|
1222
|
+
virtualPopulateSnapshot?: VirtualPopulateSnapshotToken;
|
|
886
1223
|
catalog?: AuthorizationFieldCatalog;
|
|
887
1224
|
permissionSchema?: Record<string, unknown> | null;
|
|
888
1225
|
virtuals?: Record<string, unknown> | null;
|
|
@@ -894,12 +1231,11 @@ interface VirtualProjectionSnapshot {
|
|
|
894
1231
|
}
|
|
895
1232
|
interface PlanVirtualCandidate {
|
|
896
1233
|
name: string;
|
|
897
|
-
descriptor:
|
|
898
|
-
get: (...args: never[]) => unknown;
|
|
899
|
-
dependsOn: string[];
|
|
900
|
-
};
|
|
1234
|
+
descriptor: ResolvedVirtualDescriptor;
|
|
901
1235
|
authState: VirtualCandidateAuthState;
|
|
902
1236
|
dependsOn: string[];
|
|
1237
|
+
/** Terminal private reads for this active getter; not first-argument dependencies. */
|
|
1238
|
+
populateDependencies: readonly CapturedVirtualPopulateDependency[];
|
|
903
1239
|
virtualAccess: VirtualProjectionAccess;
|
|
904
1240
|
outputAccess: VirtualProjectionAccess;
|
|
905
1241
|
}
|
|
@@ -925,6 +1261,10 @@ interface VirtualProjectionPlan {
|
|
|
925
1261
|
outputVirtualNames: string[];
|
|
926
1262
|
/** Safe Mongoose projection: no virtuals, no `.sub` paths, no `-_id`. */
|
|
927
1263
|
persistedFetchSelection: string[];
|
|
1264
|
+
/** Pre-expansion persisted/hook input mask. Fetching new refs confers no hook-input authority. */
|
|
1265
|
+
publicPersistedFetchSelection: string[];
|
|
1266
|
+
/** Empty embedded hook views retain container shape, never a private fetch's contents. */
|
|
1267
|
+
publicStructurePaths?: string[];
|
|
928
1268
|
candidates: PlanVirtualCandidate[];
|
|
929
1269
|
virtualOnlyDeps: string[];
|
|
930
1270
|
internalOnlyFields: string[];
|
|
@@ -1013,6 +1353,13 @@ declare class Service<TModel = unknown, TVirtuals extends object = Record<never,
|
|
|
1013
1353
|
/** Presence, not truthiness: never read a legacy slot or treat it as payload data. */
|
|
1014
1354
|
private admitServiceCall;
|
|
1015
1355
|
private executionOutputPlan;
|
|
1356
|
+
private preparedOutputIntent;
|
|
1357
|
+
/** Raw result -> exact ref capture -> detached ordinary source/hook rows.
|
|
1358
|
+
* Private inputs travel separately through finalization, never through snapshots.
|
|
1359
|
+
*/
|
|
1360
|
+
private loadOutputPopulateDependencies;
|
|
1361
|
+
/** Dependency-bearing post-lifecycle/template work uses detached output copies. */
|
|
1362
|
+
private prepareDetachedOutputRows;
|
|
1016
1363
|
private requestSortFields;
|
|
1017
1364
|
/** One output stage per row, independent of metadata and of pre-write admission. */
|
|
1018
1365
|
private finalizeDirectOutput;
|
|
@@ -1062,7 +1409,8 @@ declare class Service<TModel = unknown, TVirtuals extends object = Record<never,
|
|
|
1062
1409
|
* Finalizes already-filtered (visible-only) sub rows through the shared
|
|
1063
1410
|
* boundary with scope ` [sub,'sub']`, owning-parent `read` grants, and
|
|
1064
1411
|
* scoped rules. Children (deeper nested + related via `related`) finalize
|
|
1065
|
-
* before sub getters;
|
|
1412
|
+
* before sub getters; argument one has finalized public children. Declared
|
|
1413
|
+
* private populated inputs travel separately in argument four.
|
|
1066
1414
|
* Uses a bounded row map + request/runtime hook-chain admission (stable
|
|
1067
1415
|
* order, limit-one reentry, no persistence permits held). Waiting ancestor
|
|
1068
1416
|
* callback frames do not consume extra permits. Callers must filter rows BEFORE
|
|
@@ -1247,18 +1595,6 @@ declare class Service<TModel = unknown, TVirtuals extends object = Record<never,
|
|
|
1247
1595
|
countByFieldValues(foreignField: string, values: readonly unknown[], filter?: Filter<TModel>, access?: BaseFilterAccess): Promise<SingleResult<Map<string, number>> | ErrorResult>;
|
|
1248
1596
|
private countPreparedByFieldValueGroups;
|
|
1249
1597
|
getDocPermissions(doc: unknown): Record<string, unknown>;
|
|
1250
|
-
private resolveFindOneArgs;
|
|
1251
|
-
private resolveFindOneOptions;
|
|
1252
|
-
private resolveFindByIdArgs;
|
|
1253
|
-
private resolveFindByIdOptions;
|
|
1254
|
-
private resolveFindArgs;
|
|
1255
|
-
private resolveFindOptions;
|
|
1256
|
-
private resolveCreateArgs;
|
|
1257
|
-
private resolveCreateOptions;
|
|
1258
|
-
private resolveUpdateOneArgs;
|
|
1259
|
-
private resolveUpdateOneOptions;
|
|
1260
|
-
private resolveUpdateByIdArgs;
|
|
1261
|
-
private resolveUpdateByIdOptions;
|
|
1262
1598
|
private resolveUpsertArgs;
|
|
1263
1599
|
private resolveUpsertOptions;
|
|
1264
1600
|
private resolveExistsOptions;
|
|
@@ -1356,6 +1692,8 @@ declare class PublicService<TModel = unknown, TVirtuals extends object = Record<
|
|
|
1356
1692
|
_delete(id: string): Promise<SingleResult<unknown> | ErrorResult>;
|
|
1357
1693
|
_distinct(field: string, options?: DistinctArgs<TModel>): Promise<ListResult<unknown> | ErrorResult>;
|
|
1358
1694
|
_count(filter: Filter<TModel>, access?: BaseFilterAccess): Promise<SingleResult<number> | ErrorResult>;
|
|
1695
|
+
/** Own the conditional gate with the read; execute only after a nonterminal miss. */
|
|
1696
|
+
private captureListFallbackAdmission;
|
|
1359
1697
|
private admitPublicCall;
|
|
1360
1698
|
private resolvePublicListArgs;
|
|
1361
1699
|
private resolvePublicListOptions;
|
|
@@ -1635,7 +1973,7 @@ declare class Base<TModel = unknown, TVirtuals extends object = Record<never, ne
|
|
|
1635
1973
|
protected getAuthorizedTargetService(modelName: string, op: CrossResourceModelOperation, path?: string, parentAuthorization?: ResolvedServiceAuthorization): Promise<PublicService<unknown, Record<never, never>>>;
|
|
1636
1974
|
private relatedBinding;
|
|
1637
1975
|
private relatedExecution;
|
|
1638
|
-
protected processInclude(include: Include | Include[]): {
|
|
1976
|
+
protected processInclude(include: Include | Include[], authorization?: ResolvedServiceAuthorization): {
|
|
1639
1977
|
includes: LegacyInclude[];
|
|
1640
1978
|
correlatedIncludes: CorrelatedInclude[];
|
|
1641
1979
|
includeLocalFields: string[];
|
|
@@ -2688,198 +3026,6 @@ interface ExistsOptions {
|
|
|
2688
3026
|
includeId?: boolean;
|
|
2689
3027
|
}
|
|
2690
3028
|
|
|
2691
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
2692
|
-
type AccessRouterFieldKey<T> = [Extract<keyof T, string>] extends [never] ? string : Extract<keyof T, string>;
|
|
2693
|
-
type IdentifierHook<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, id: string) => MaybePromise<Filter<TValue>>;
|
|
2694
|
-
type GlobalPermissionValue = Record<string, boolean> | string[] | string | null | undefined;
|
|
2695
|
-
type BaseFilterHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, permissions: AccessRouterPermissions) => MaybePromise<Filter | true | null | undefined>;
|
|
2696
|
-
type OverrideFilterHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, filter: Filter, permissions: AccessRouterPermissions) => MaybePromise<Filter>;
|
|
2697
|
-
type Hook<TValue, TContext, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: TContext) => MaybePromise<TValue>;
|
|
2698
|
-
type HookChain<TValue, TContext, TRequest extends AccessRouterRequest = AccessRouterRequest> = Hook<TValue, TContext, TRequest> | Array<Hook<TValue, TContext, TRequest>>;
|
|
2699
|
-
type ValidateRule = boolean | unknown[];
|
|
2700
|
-
type ValidateHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, allowedData: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<ValidateRule>;
|
|
2701
|
-
type DocPermissionsHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, doc: unknown, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<Record<string, unknown>>;
|
|
2702
|
-
type ChangeHook<TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, previousValue: unknown, nextValue: unknown, changes: Change[], context: ModelHookContext) => MaybePromise<void>;
|
|
2703
|
-
type DeleteHook<TValue = unknown, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: TValue, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<void>;
|
|
2704
|
-
type DocumentHook<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = (this: TRequest, value: ModelDocument<TValue>, permissions: AccessRouterPermissions, context: ModelHookContext) => MaybePromise<ModelDocument<TValue>>;
|
|
2705
|
-
type DocumentHookChain<TValue, TRequest extends AccessRouterRequest = AccessRouterRequest> = DocumentHook<TValue, TRequest> | Array<DocumentHook<TValue, TRequest>>;
|
|
2706
|
-
type ModelBaseFilterHook = BaseFilterHook<ModelRequest>;
|
|
2707
|
-
type DataBaseFilterHook = BaseFilterHook<DataRequest>;
|
|
2708
|
-
type ModelOverrideFilterHook = OverrideFilterHook<ModelRequest>;
|
|
2709
|
-
type DataOverrideFilterHook = OverrideFilterHook<DataRequest>;
|
|
2710
|
-
type ModelIdentifierHook<TValue = unknown> = IdentifierHook<TValue, ModelRequest>;
|
|
2711
|
-
type DataIdentifierHook<TValue = unknown> = IdentifierHook<TValue, DataRequest>;
|
|
2712
|
-
type ModelValidateHook = ValidateHook<ModelRequest>;
|
|
2713
|
-
/**
|
|
2714
|
-
* Request-user grants from untrimmed projected input. Declare private persisted inputs
|
|
2715
|
-
* with alwaysSelectFields. Integrated output resolves once per document/access stage,
|
|
2716
|
-
* independently of skim or metadata serialization; use the result's document handle
|
|
2717
|
-
* for later picks after input/metadata trim. Backend grants do not forge user metadata.
|
|
2718
|
-
*/
|
|
2719
|
-
type ModelDocPermissionsHook = DocPermissionsHook<ModelRequest>;
|
|
2720
|
-
type ModelChangeHook = ChangeHook<ModelRequest>;
|
|
2721
|
-
type ModelDocumentHook<TValue = unknown> = DocumentHookChain<TValue, ModelRequest>;
|
|
2722
|
-
type ModelDeleteHook<TValue = unknown> = DeleteHook<ModelDocument<TValue>, ModelRequest>;
|
|
2723
|
-
type ModelHook<TValue = unknown> = HookChain<TValue, ModelHookContext, ModelRequest>;
|
|
2724
|
-
type ModelListHook<TValue = unknown> = HookChain<TValue[], ModelHookContext, ModelRequest>;
|
|
2725
|
-
type DataHook<TValue = unknown> = HookChain<TValue, DataHookContext, DataRequest>;
|
|
2726
|
-
type DataListHook<TValue = unknown> = HookChain<TValue[], DataHookContext, DataRequest>;
|
|
2727
|
-
/**
|
|
2728
|
-
* Supported per-access virtual getter keys (VIRT-00A D1/D5).
|
|
2729
|
-
* `default` is a fallback access; `delete`/`distinct`/`count` are NOT
|
|
2730
|
-
* virtual accesses and must be rejected at configuration time.
|
|
2731
|
-
*/
|
|
2732
|
-
type ModelVirtualAccess = 'list' | 'create' | 'read' | 'update';
|
|
2733
|
-
type ModelVirtualRecordAccess = 'default' | ModelVirtualAccess;
|
|
2734
|
-
/**
|
|
2735
|
-
* Scope-aware virtual getter context (VIRT-00A D2, frozen).
|
|
2736
|
-
* Extends {@link ModelHookContext} with the receiving model, definition/
|
|
2737
|
-
* permission `scopePath` (e.g. `[]` at root, `['contacts','sub']` for
|
|
2738
|
-
* `contacts.sub.displayName`), and the three effective accesses. The
|
|
2739
|
-
* initiating operation stays in `context.operation` and is never
|
|
2740
|
-
* overwritten with fallback/effective visibility.
|
|
2741
|
-
*
|
|
2742
|
-
* Embedded outputs reuse the owning parent's internally computed grants with
|
|
2743
|
-
* the scope's `permissionSchema.<field>.sub` rules; dedicated subdocument
|
|
2744
|
-
* routes resolve grants from the raw parent lookup. Related (populate/
|
|
2745
|
-
* include) targets finalize with the target model's own definitions,
|
|
2746
|
-
* permissions, selection, and effective access before parent getters run.
|
|
2747
|
-
*/
|
|
2748
|
-
interface ModelVirtualContext extends ModelHookContext {
|
|
2749
|
-
receivingModelName: string;
|
|
2750
|
-
scopePath: string[];
|
|
2751
|
-
virtualAccess: ModelVirtualAccess;
|
|
2752
|
-
outputAccess: ModelVirtualAccess;
|
|
2753
|
-
docPermissionsAccess: ModelVirtualAccess;
|
|
2754
|
-
}
|
|
2755
|
-
type IsUnknownVirtual<T> = unknown extends T ? ([keyof T] extends [never] ? true : false) : false;
|
|
2756
|
-
/**
|
|
2757
|
-
* Typed isolated persisted view for a virtual getter scope (VIRT-00A D3/D4).
|
|
2758
|
-
* Known models use a partial read-only persisted view (never
|
|
2759
|
-
* `Record<string, unknown>`); unknown/untyped models keep an explicit
|
|
2760
|
-
* loose fallback.
|
|
2761
|
-
*/
|
|
2762
|
-
type ModelVirtualDoc<TModel = unknown> = IsUnknownVirtual<TModel> extends true ? Readonly<Partial<Record<string, unknown>>> : [Extract<keyof TModel, string>] extends [never] ? Readonly<Partial<Record<string, unknown>>> : Readonly<Partial<TModel>>;
|
|
2763
|
-
/**
|
|
2764
|
-
* Virtual getter signature (VIRT-00A D3/D4, frozen).
|
|
2765
|
-
* Receives an isolated plain-object snapshot of persisted dependencies +
|
|
2766
|
-
* already-finalized related/embedded outputs for its scope. Sibling virtual
|
|
2767
|
-
* values are not visible. Mutating the input cannot change response data or
|
|
2768
|
-
* lifecycle snapshots. Only the returned field value is committed;
|
|
2769
|
-
* `undefined` omits the field; throws omit fail-closed. `this` is the
|
|
2770
|
-
* model request.
|
|
2771
|
-
*/
|
|
2772
|
-
type ModelVirtualGetter<TModel = unknown, TValue = unknown> = (this: ModelRequest, doc: ModelVirtualDoc<TModel>, permissions: AccessRouterPermissions, context: ModelVirtualContext) => MaybePromise<TValue | undefined>;
|
|
2773
|
-
/**
|
|
2774
|
-
* Leaf virtual descriptor (VIRT-01): `{ get, dependsOn }`.
|
|
2775
|
-
* `dependsOn` lists top-level persisted field names relative to the
|
|
2776
|
-
* definition's scope (e.g. `['displayName']` inside `contacts.sub`, never
|
|
2777
|
-
* `['contacts.sub.displayName']`). Dotted paths and virtual-to-virtual
|
|
2778
|
-
* dependencies are rejected at configuration time.
|
|
2779
|
-
*
|
|
2780
|
-
* The getter runs after `toObject()`/lean normalization and after internal
|
|
2781
|
-
* document-permissions computation, with global permissions available and
|
|
2782
|
-
* before `decorate`/`decorateAll`/tasks. Only the returned field value is
|
|
2783
|
-
* committed via document-aware helpers: `undefined` (or a throw) omits the
|
|
2784
|
-
* field fail-closed and computed values never persist. Getters see an
|
|
2785
|
-
* isolated plain-object view for both lean and hydrated results; mutating the
|
|
2786
|
-
* input cannot change response data or lifecycle snapshots.
|
|
2787
|
-
*
|
|
2788
|
-
* @example
|
|
2789
|
-
* ```ts
|
|
2790
|
-
* import type { ModelVirtualDescriptor } from '@web-ts-toolkit/access-router';
|
|
2791
|
-
* interface User { name: string; address: string; }
|
|
2792
|
-
* const descriptor: ModelVirtualDescriptor<User, string> = {
|
|
2793
|
-
* dependsOn: ['address'],
|
|
2794
|
-
* get: async (doc) => (doc.address === undefined ? undefined : `addr:${doc.address}`),
|
|
2795
|
-
* };
|
|
2796
|
-
* ```
|
|
2797
|
-
*/
|
|
2798
|
-
interface ModelVirtualDescriptor<TModel = unknown, TValue = unknown> {
|
|
2799
|
-
get: ModelVirtualGetter<TModel, TValue>;
|
|
2800
|
-
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
2801
|
-
}
|
|
2802
|
-
/**
|
|
2803
|
-
* Per-access virtual record (VIRT-01): `default` + `list`/`create`/`read`/
|
|
2804
|
-
* `update` (NOT `delete`/`distinct`/`count`). Each present access holds a
|
|
2805
|
-
* leaf descriptor or bare getter shorthand. An optional shared `dependsOn`
|
|
2806
|
-
* supports the documented `{ dependsOn, read: getter }` shape; a leaf's own
|
|
2807
|
-
* `dependsOn` wins over the shared list during resolution.
|
|
2808
|
-
*/
|
|
2809
|
-
type ModelVirtualAccessRecord<TModel = unknown, TValue = unknown> = {
|
|
2810
|
-
default?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2811
|
-
list?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2812
|
-
create?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2813
|
-
read?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2814
|
-
update?: ModelVirtualDescriptor<TModel, TValue> | ModelVirtualGetter<TModel, TValue>;
|
|
2815
|
-
dependsOn?: Array<AccessRouterFieldKey<TModel>>;
|
|
2816
|
-
};
|
|
2817
|
-
/** Single virtual entry: bare getter, leaf descriptor, or per-access record. */
|
|
2818
|
-
type ModelVirtualLeaf<TModel = unknown, TValue = unknown> = ModelVirtualGetter<TModel, TValue> | ModelVirtualDescriptor<TModel, TValue> | ModelVirtualAccessRecord<TModel, TValue>;
|
|
2819
|
-
type UnwrapVirtualArray<T> = T extends readonly (infer U)[] ? U : T;
|
|
2820
|
-
/**
|
|
2821
|
-
* Persisted sub-scope model for an embedded container field (VIRT-01).
|
|
2822
|
-
* Unwraps arrays/nullable wrappers to the element/object type; unknown
|
|
2823
|
-
* fields fall back to a loose record.
|
|
2824
|
-
*/
|
|
2825
|
-
type VirtualSubModel<TModel, TField extends string> = TField extends keyof TModel ? UnwrapVirtualArray<NonNullable<TModel[TField]>> : Record<string, unknown>;
|
|
2826
|
-
type LooseVirtualLeaf = ModelVirtualGetter<Record<string, unknown>, unknown> | ModelVirtualDescriptor<Record<string, unknown>, unknown> | ModelVirtualAccessRecord<Record<string, unknown>, unknown>;
|
|
2827
|
-
type LooseVirtualSubEntry = LooseVirtualLeaf | {
|
|
2828
|
-
sub?: Record<string, LooseVirtualLeaf | {
|
|
2829
|
-
sub?: Record<string, LooseVirtualLeaf>;
|
|
2830
|
-
}>;
|
|
2831
|
-
};
|
|
2832
|
-
/**
|
|
2833
|
-
* Virtual definitions for a model scope (VIRT-01).
|
|
2834
|
-
* Root leaves come from `TVirtuals` (strict: unknown persisted getter
|
|
2835
|
-
* fields are type errors). Embedded containers mirror
|
|
2836
|
-
* `permissionSchema.<field>.sub`: any persisted field may host a `sub`
|
|
2837
|
-
* record whose leaves use the container's persisted sub-model view.
|
|
2838
|
-
* Untyped models keep an explicit loose record fallback.
|
|
2839
|
-
*
|
|
2840
|
-
* Selected virtual outputs are optional (`Partial`): authorization, absent
|
|
2841
|
-
* dependencies, `undefined`, and fail-closed errors can omit them. Virtual
|
|
2842
|
-
* names are never added to persisted `Filter`, sort/distinct inputs, or
|
|
2843
|
-
* client write types.
|
|
2844
|
-
*
|
|
2845
|
-
* @example End-to-end typed virtual (persisted `User` has no `fullAddress`):
|
|
2846
|
-
* ```ts
|
|
2847
|
-
* import acl from '@web-ts-toolkit/access-router';
|
|
2848
|
-
* import type { SelectedPublicOutput } from '@web-ts-toolkit/access-router/advanced';
|
|
2849
|
-
* interface User { name: string; address: string; }
|
|
2850
|
-
* interface UserVirtuals { fullAddress: string; }
|
|
2851
|
-
* const router = acl.createRouter<User, UserVirtuals>('User', {
|
|
2852
|
-
* permissionSchema: {
|
|
2853
|
-
* name: { read: true },
|
|
2854
|
-
* address: { read: 'canViewAddress' },
|
|
2855
|
-
* fullAddress: { read: 'canViewAddress' },
|
|
2856
|
-
* },
|
|
2857
|
-
* virtuals: {
|
|
2858
|
-
* fullAddress: {
|
|
2859
|
-
* dependsOn: ['address'],
|
|
2860
|
-
* read: async function (doc) {
|
|
2861
|
-
* if (doc.address === undefined) return undefined;
|
|
2862
|
-
* return `addr:${doc.address}`;
|
|
2863
|
-
* },
|
|
2864
|
-
* },
|
|
2865
|
-
* },
|
|
2866
|
-
* });
|
|
2867
|
-
* type Out = SelectedPublicOutput<User, ['name', 'fullAddress'], UserVirtuals>;
|
|
2868
|
-
* const out: Out = { name: 'Ada', fullAddress: 'addr:x' };
|
|
2869
|
-
* const omitted: Out = { name: 'Ada' }; // virtual is optional when denied/missing
|
|
2870
|
-
* void [router, out, omitted];
|
|
2871
|
-
* ```
|
|
2872
|
-
*/
|
|
2873
|
-
type ModelVirtuals<TModel = unknown, TVirtuals extends object = Record<never, never>> = IsUnknownVirtual<TModel> extends true ? Record<string, LooseVirtualSubEntry> : {
|
|
2874
|
-
[K in Extract<keyof TVirtuals, string>]?: ModelVirtualLeaf<TModel, TVirtuals[K]>;
|
|
2875
|
-
} & {
|
|
2876
|
-
[K in Extract<keyof TModel, string>]?: {
|
|
2877
|
-
sub?: Record<string, ModelVirtualLeaf<VirtualSubModel<TModel, K>, unknown> | {
|
|
2878
|
-
sub?: Record<string, LooseVirtualLeaf>;
|
|
2879
|
-
}>;
|
|
2880
|
-
};
|
|
2881
|
-
};
|
|
2882
|
-
|
|
2883
3029
|
type RequestSchema = RequestSchemaLike;
|
|
2884
3030
|
type NestedRequestSchema = {
|
|
2885
3031
|
default?: RequestSchema;
|
|
@@ -2984,7 +3130,7 @@ interface AccessRouterLogger {
|
|
|
2984
3130
|
isLevelEnabled?: (level: string) => boolean;
|
|
2985
3131
|
}
|
|
2986
3132
|
/**
|
|
2987
|
-
* Runtime-wide request initialization and resource settings, configured with
|
|
3133
|
+
* Runtime-wide request initialization, private-input policy and resource settings, configured with
|
|
2988
3134
|
* api.setGlobalOptions / api.setGlobalOption. These are not model options or wire
|
|
2989
3135
|
* options. Object assignment is shallow; nested requestComplexity replaces as a unit.
|
|
2990
3136
|
*/
|
|
@@ -3002,6 +3148,39 @@ interface GlobalOptions {
|
|
|
3002
3148
|
globalPermissions?: (this: AccessRouterRequest, req: AccessRouterRequest) => MaybePromise<GlobalPermissionValue>;
|
|
3003
3149
|
/** Require populate target models to be registered in the active runtime. Defaults to `true`. */
|
|
3004
3150
|
requireRegisteredPopulateModels?: boolean;
|
|
3151
|
+
/**
|
|
3152
|
+
* Require the original caller's target base `operationAccess.read` before loading
|
|
3153
|
+
* declared private `dependsOnPopulate` inputs (default `true`). Only literal boolean
|
|
3154
|
+
* `false` bypasses that operation gate; undefined/malformed JS values normalize to
|
|
3155
|
+
* `true`. Source/target input-field permissions are already bypassed by the server
|
|
3156
|
+
* declaration; the virtual's own output authorization still applies. Target request-mode
|
|
3157
|
+
* `baseFilter.read`, `overrideFilter.read` and retained reference IDs always constrain rows.
|
|
3158
|
+
* Captured with source/target policy and query intent before the first await, including
|
|
3159
|
+
* nested output scopes. In-flight calls retain their mode; later calls see updates.
|
|
3160
|
+
* Runtime-isolated server configuration only: no caller/query or per-model override.
|
|
3161
|
+
*
|
|
3162
|
+
* In BOTH modes, references in an active selected/applicable dependency plan require
|
|
3163
|
+
* source selection/field permission AND the captured caller-context target base read
|
|
3164
|
+
* grant. Denied/failed read visibility omits the entire reference, including its ID,
|
|
3165
|
+
* even with ordinary list/custom populate access or a public related-operation grant.
|
|
3166
|
+
* Allowed read keeps the original authorized public populate mask, or exact original
|
|
3167
|
+
* refs without population. No active dependency plan leaves ordinary behavior intact.
|
|
3168
|
+
* False-mode private-only loading adds no operation check solely for that fetch.
|
|
3169
|
+
* This never grants public fields, row/write authority or query controls. Registration
|
|
3170
|
+
* and static input declarations remain required, independently of requireRegisteredPopulateModels.
|
|
3171
|
+
*
|
|
3172
|
+
* @example
|
|
3173
|
+
* ```ts
|
|
3174
|
+
* import { createAccessRuntime, type GlobalOptions } from '@web-ts-toolkit/access-router';
|
|
3175
|
+
* const api = createAccessRuntime();
|
|
3176
|
+
* const policy = { requireReadOperationAccessForPopulateDependencies: false } satisfies GlobalOptions;
|
|
3177
|
+
* api.setGlobalOptions(policy); // privileged computation inputs; no public target-read grant
|
|
3178
|
+
* const snapshot: GlobalOptions = api.getGlobalOptions();
|
|
3179
|
+
* api.setGlobalOption('requireReadOperationAccessForPopulateDependencies', true);
|
|
3180
|
+
* void snapshot; // stored snapshot; read again to observe the setter
|
|
3181
|
+
* ```
|
|
3182
|
+
*/
|
|
3183
|
+
requireReadOperationAccessForPopulateDependencies?: boolean;
|
|
3005
3184
|
logger?: AccessRouterLogger;
|
|
3006
3185
|
/** Request budgets and separate hook/persistence pools; concurrency limits are captured on first owner use. */
|
|
3007
3186
|
requestComplexity?: RequestComplexityOptions;
|
|
@@ -3305,6 +3484,16 @@ interface ModelRouterOptions<TModel = unknown, TVirtuals extends object = Record
|
|
|
3305
3484
|
* authorized. Virtuals are output-only: registered names (including
|
|
3306
3485
|
* embedded `sub` leaves) are excluded from persisted projections, client
|
|
3307
3486
|
* write admission, and database sort/filter/distinct handling.
|
|
3487
|
+
* `dependsOnPopulate` declares one-hop private persisted target inputs only in
|
|
3488
|
+
* that getter's fourth argument. Annotate ModelVirtualDescriptor<Source, Value,
|
|
3489
|
+
* { ref: Target }> separately, then assign here; persisted/write/public types
|
|
3490
|
+
* stay unchanged. Register target models/options first in this runtime. Source
|
|
3491
|
+
* refs are retained automatically. GlobalOptions.requireReadOperationAccessForPopulateDependencies
|
|
3492
|
+
* defaults to true; false bypasses only private target-operation admission, never
|
|
3493
|
+
* request read-row filters/retained IDs. In both modes an active dependency ref's
|
|
3494
|
+
* denied caller-context target base read omits the entire ref, including its ID.
|
|
3495
|
+
* Allowed refs keep the original authorized populate mask or exact original IDs;
|
|
3496
|
+
* unselected/denied source fields remain hidden. No active plan keeps ordinary behavior.
|
|
3308
3497
|
*
|
|
3309
3498
|
* @example Shortest typed happy-path (persisted model lacks `fullAddress`):
|
|
3310
3499
|
* ```ts
|
|
@@ -3611,4 +3800,4 @@ declare function fromIoTs<TValue = unknown>(decoder: IoTsDecoderLike<TValue>): R
|
|
|
3611
3800
|
declare function fromSuperstruct<TStruct, TOutput = unknown>(struct: TStruct, validate: SuperstructValidateLike): RequestSchemaValidator<TOutput>;
|
|
3612
3801
|
declare function fromVine<TValue = unknown>(validator: VineValidatorLike<TValue>): RequestSchemaValidator<TValue>;
|
|
3613
3802
|
|
|
3614
|
-
export { type DecorateAccess as $, type AccessRouterBaseRequest as A, type BaseFilterAccess as B, type Change as C, type CorrelatedReadByFilterInclude as D, type CorrelatedReadByIdInclude as E, type CountBody as F, type CreateArgs as G, type CreateAuthorization as H, type CreateOptions as I, type CreateQueryInput as J, CustomHeaders as K, DATA_MIDDLEWARE as L, type DataBaseFilterHook as M, type DataFieldOptions as N, type DataFilter as O, type DataFindArgs as P, type DataFindOneArgs as Q, type DataFindOneOptions as R, type DataFindOptions as S, type DataHook as T, type DataHookContext as U, type DataIdentifierHook as V, type DataListHook as W, type DataOverrideFilterHook as X, type DataRequest as Y, type DataRequestSchemas as Z, type DataRouterOptions as _, type AccessRouterFieldKey as a, type ModelVirtualGetter as a$, type DecorateAllAccess as a0, type DeepFieldPath as a1, type DefaultModelRouterOptions as a2, type Defaults as a3, type DistinctArgs as a4, type DistinctBody as a5, type DocPermissionsAccess as a6, type DocumentAuthorization as a7, type ErrorResult as a8, type ExistsOptions as a9, type JoiValidationErrorDetailLike as aA, type KeyValueProjection as aB, type LegacyInclude as aC, type ListQueryInput as aD, type ListResult as aE, MIDDLEWARE as aF, type MaybePromise as aG, type ModelBaseFilterHook as aH, type ModelChangeHook as aI, type ModelDeleteHook as aJ, type ModelDocPermissionsHook as aK, type ModelDocument as aL, type ModelDocumentHook as aM, type ModelHook as aN, type ModelHookContext as aO, type ModelHydrationOptions as aP, type ModelIdentifierHook as aQ, type ModelListHook as aR, type ModelOverrideFilterHook as aS, type ModelRequest as aT, type ModelRouterOptions as aU, type ModelValidateHook as aV, type ModelVirtualAccess as aW, type ModelVirtualAccessRecord as aX, type ModelVirtualContext as aY, type ModelVirtualDescriptor as aZ, type ModelVirtualDoc as a_, type ExtendedDataRouterOptions as aa, type ExtendedDefaultModelRouterOptions as ab, type ExtendedModelRouterOptions as ac, type FieldAuthorization as ad, type FieldOperationAccess as ae, type Filter as af, FilterOperator as ag, type FindAccess as ah, type FindArgs as ai, type FindByIdArgs as aj, type FindByIdOptions as ak, type FindOneArgs as al, type FindOneOptions as am, type FindOptions as an, type GlobalOptions as ao, type GlobalPermissionValue as ap, type GuardHook as aq, type HydrationMode as ar, type IdentifierHook as as, type Include as at, type InputFieldAuthorization as au, type InputFieldPath as av, type IoTsContextEntryLike as aw, type IoTsDecodeErrorLike as ax, type IoTsDecoderLike as ay, type JoiSchemaLike as az, type AccessRouterLogger as b, type
|
|
3803
|
+
export { type DecorateAccess as $, type AccessRouterBaseRequest as A, type BaseFilterAccess as B, type Change as C, type CorrelatedReadByFilterInclude as D, type CorrelatedReadByIdInclude as E, type CountBody as F, type CreateArgs as G, type CreateAuthorization as H, type CreateOptions as I, type CreateQueryInput as J, CustomHeaders as K, DATA_MIDDLEWARE as L, type DataBaseFilterHook as M, type DataFieldOptions as N, type DataFilter as O, type DataFindArgs as P, type DataFindOneArgs as Q, type DataFindOneOptions as R, type DataFindOptions as S, type DataHook as T, type DataHookContext as U, type DataIdentifierHook as V, type DataListHook as W, type DataOverrideFilterHook as X, type DataRequest as Y, type DataRequestSchemas as Z, type DataRouterOptions as _, type AccessRouterFieldKey as a, type ModelVirtualGetter as a$, type DecorateAllAccess as a0, type DeepFieldPath as a1, type DefaultModelRouterOptions as a2, type Defaults as a3, type DistinctArgs as a4, type DistinctBody as a5, type DocPermissionsAccess as a6, type DocumentAuthorization as a7, type ErrorResult as a8, type ExistsOptions as a9, type JoiValidationErrorDetailLike as aA, type KeyValueProjection as aB, type LegacyInclude as aC, type ListQueryInput as aD, type ListResult as aE, MIDDLEWARE as aF, type MaybePromise as aG, type ModelBaseFilterHook as aH, type ModelChangeHook as aI, type ModelDeleteHook as aJ, type ModelDocPermissionsHook as aK, type ModelDocument as aL, type ModelDocumentHook as aM, type ModelHook as aN, type ModelHookContext as aO, type ModelHydrationOptions as aP, type ModelIdentifierHook as aQ, type ModelListHook as aR, type ModelOverrideFilterHook as aS, type ModelRequest as aT, type ModelRouterOptions as aU, type ModelValidateHook as aV, type ModelVirtualAccess as aW, type ModelVirtualAccessRecord as aX, type ModelVirtualContext as aY, type ModelVirtualDescriptor as aZ, type ModelVirtualDoc as a_, type ExtendedDataRouterOptions as aa, type ExtendedDefaultModelRouterOptions as ab, type ExtendedModelRouterOptions as ac, type FieldAuthorization as ad, type FieldOperationAccess as ae, type Filter as af, FilterOperator as ag, type FindAccess as ah, type FindArgs as ai, type FindByIdArgs as aj, type FindByIdOptions as ak, type FindOneArgs as al, type FindOneOptions as am, type FindOptions as an, type GlobalOptions as ao, type GlobalPermissionValue as ap, type GuardHook as aq, type HydrationMode as ar, type IdentifierHook as as, type Include as at, type InputFieldAuthorization as au, type InputFieldPath as av, type IoTsContextEntryLike as aw, type IoTsDecodeErrorLike as ax, type IoTsDecoderLike as ay, type JoiSchemaLike as az, type AccessRouterLogger as b, type RootModelDistinctQueryEntry as b$, type ModelVirtualLeaf as b0, type ModelVirtualPopulateDependency as b1, type ModelVirtualPopulateInputs as b2, type ModelVirtualRecordAccess as b3, type ModelVirtuals as b4, type MutationAuthorization as b5, type NewArgs as b6, type NewOptions as b7, type OperationAccess as b8, type OperationAuthorization as b9, type PublicSubdocumentCreateOptions as bA, type PublicSubdocumentListOptions as bB, type PublicSubdocumentMutationOptions as bC, type PublicSubdocumentReadOptions as bD, type PublicUpdateArgs as bE, type PublicUpdateOptions as bF, type PublicUpsertArgs as bG, type PublicUpsertOptions as bH, type ReadQueryInput as bI, type RelatedAuthorization as bJ, type Request as bK, type RequestSchemaAdapter as bL, type RequestSchemaFailure as bM, type RequestSchemaIssue as bN, type RequestSchemaLike as bO, type RequestSchemaOptions as bP, type RequestSchemaResult as bQ, type RequestSchemaSuccess as bR, type RequestSchemaValidator as bS, type RequestSchemas as bT, type RootDataListQueryEntry as bU, type RootDataOperation as bV, type RootDataReadByFilterQueryEntry as bW, type RootDataReadByIdQueryEntry as bX, type RootModelCountQueryEntry as bY, type RootModelCreateQueryEntry as bZ, type RootModelDeleteQueryEntry as b_, type OutputAuthorization as ba, type OutputFieldOptions as bb, type OutputFieldPath as bc, type OutputPlanningOptions as bd, PERMISSIONS as be, PERMISSION_KEYS as bf, type PairedRouteAccess as bg, type ParentRef as bh, type PathValue as bi, type PermissionSchema as bj, type Populate as bk, type PopulateAccess as bl, type PopulatePlanningOptions as bm, type PrepareAccess as bn, type Projection as bo, type PublicCreateArgs as bp, type PublicCreateOptions as bq, type PublicErrorResult as br, type PublicListArgs as bs, type PublicListOptions as bt, type PublicListResult as bu, type PublicOutput as bv, type PublicReadArgs as bw, type PublicReadOptions as bx, type PublicServiceResult as by, type PublicSingleResult as bz, type AccessRouterRequest as c, type SubdocumentParentArgs as c$, type RootModelListQueryEntry as c0, type RootModelNewQueryEntry as c1, type RootModelOperation as c2, type RootModelReadByFilterQueryEntry as c3, type RootModelReadByIdQueryEntry as c4, type RootModelSubBulkUpdateQueryEntry as c5, type RootModelSubCreateQueryEntry as c6, type RootModelSubDeleteQueryEntry as c7, type RootModelSubListQueryEntry as c8, type RootModelSubReadQueryEntry as c9, type StandardSchemaPathSegment as cA, type StandardSchemaResult as cB, type StandardSchemaSuccess as cC, type StandardSchemaV1 as cD, StatusCodes as cE, type SubListBody as cF, type SubOperationAccess as cG, type SubPopulate as cH, type SubQueryEntry as cI, type SubReadBody as cJ, type SubRouteGuardOptions as cK, type SubdocumentBulkRecord as cL, type SubdocumentBulkUpdateInput as cM, type SubdocumentCreateAuthorization as cN, type SubdocumentCreateInput as cO, type SubdocumentCreateOptions as cP, type SubdocumentCreateVisibilityAuthorization as cQ, type SubdocumentDeleteAuthorization as cR, type SubdocumentDeleteOptions as cS, type SubdocumentGateAuthorization as cT, type SubdocumentId as cU, type SubdocumentInputAuthorization as cV, type SubdocumentListOptions as cW, type SubdocumentModel as cX, type SubdocumentMutationAuthorization as cY, type SubdocumentName as cZ, type SubdocumentOutputAuthorization as c_, type RootModelSubUpdateQueryEntry as ca, type RootModelUpdateQueryEntry as cb, type RootModelUpsertQueryEntry as cc, type RootOperationResult as cd, type RootQueryEntry as ce, type RootRouterOptions as cf, type RootSubOperation as cg, type RootTarget as ch, type RouteBaseAccess as ci, type RouteGuardAccess as cj, type RouteVariant as ck, type RouteVariantAccess as cl, type RowAuthorization as cm, type SelectAccess as cn, type SelectedPopulatedPublicOutput as co, type SelectedPublicOutput as cp, type ServiceAuthorization as cq, type ServiceOutputContext as cr, type ServiceOutputDecorator as cs, type ServiceResult as ct, type SingleResult as cu, type Sort as cv, type SortOrder as cw, type StandardSchemaFailure as cx, type StandardSchemaInferOutput as cy, type StandardSchemaIssue as cz, type AccessRouterRequestExtensions as d, type OpenApiResponses as d$, type SubdocumentParentOptions as d0, type SubdocumentReadAuthorization as d1, type SubdocumentReadOptions as d2, type SubdocumentRecord as d3, type SubdocumentUpdateOptions as d4, type SubdocumentVirtuals as d5, type SubdocumentVisibilityAuthorization as d6, type SuperstructFailureLike as d7, type SuperstructValidateLike as d8, type Task as d9, type YupValidationErrorLike as dA, defineRequestSchema as dB, fromAjv as dC, fromArkType as dD, fromIoTs as dE, fromJoi as dF, fromStandardSchema as dG, fromSuperstruct as dH, fromValibot as dI, fromVine as dJ, fromYup as dK, fromZod as dL, parseBody as dM, parseBodyWithSchema as dN, parseNestedBodyWithSchema as dO, parsePathParam as dP, parseQuery as dQ, AccessRuntime as dR, DataService as dS, Service as dT, PublicService as dU, type OpenApiRouterOptions as dV, type OpenApiRegistryOptions as dW, type OpenApiRouteDescriptor as dX, type OpenApiDocumentOptions as dY, type OpenApiParameter as dZ, type OpenApiSchema as d_, type TaskType as da, type TransformAccess as db, type TypedFilter as dc, type UpdateByIdArgs as dd, type UpdateByIdOptions as de, type UpdateOneArgs as df, type UpdateOneOptions as dg, type UpdateQueryInput as dh, type UpsertArgs as di, type UpsertOptions as dj, type UpsertQueryInput as dk, type ValibotIssueLike as dl, type ValibotPathItemLike as dm, type ValibotSafeParseLike as dn, type ValibotSafeParseResult as dp, type ValidateAccess as dq, type ValidateRule as dr, type Validation as ds, type ValidationError as dt, type VineValidationErrorLike as du, type VineValidationMessageLike as dv, type VineValidatorLike as dw, type VirtualSubModel as dx, type WithVirtuals as dy, type YupSchemaLike as dz, type AdvancedCreateBody as e, type AccessRouterPermissionMap as e0, type AccessRouterPermissions as e1, type OpenApiMethod as e2, type Permissions as e3, defaultRuntime as e4, 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 AllowedOutputFieldOptions as o, type ArkTypeErrorsLike as p, type ArkTypeLike as q, type ArkTypeProblemLike as r, type AuthorizedListResult as s, type AuthorizedSingleResult as t, Codes as u, type CorrelatedCountInclude as v, type CorrelatedFilter as w, type CorrelatedInclude as x, type CorrelatedIncludeArgs as y, type CorrelatedListInclude as z };
|