@atscript/moost-db 0.1.142 → 0.1.143
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/dist/index.cjs +1258 -474
- package/dist/index.d.cts +518 -59
- package/dist/index.d.mts +518 -59
- package/dist/index.mjs +1256 -475
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
import { i as resolveDbSpace, n as clearDbSpaces, r as provideDbSpace, t as DEFAULT_DB_SPACE } from "./db-space-registry-CWpYwZ4R.cjs";
|
|
2
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
|
|
3
|
-
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, TCrudOp, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbFieldMeta, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
3
|
+
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, TCrudOp, TCrudOp as TCrudOp$1, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbFieldMeta, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteCheckContext, TDbWriteCheckContext as TDbWriteCheckContext$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
4
4
|
import { HttpError } from "@moostjs/event-http";
|
|
5
5
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
6
6
|
import { parseUrl } from "@uniqu/url";
|
|
7
7
|
//#region src/as-readable.controller.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Endpoint a {@link AsReadableController.prepareRequest} call serves. `/one`
|
|
10
|
+
* and `/one/:id` both report `"one"`; `DELETE /:id` and `DELETE /?…` both
|
|
11
|
+
* report `"remove"`; value-help controllers report `query` / `pages` / `one`
|
|
12
|
+
* like DB readables do; every `@DbAction` handler (row, rows and table
|
|
13
|
+
* level) reports `"action"` with the action's name in
|
|
14
|
+
* {@link TDbRequestContext.action}.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.1.143
|
|
17
|
+
*/
|
|
18
|
+
type TDbRequestEndpoint = "query" | "pages" | "geo" | "one" | "meta" | "metaForm" | "insert" | "replace" | "update" | "remove" | "action";
|
|
19
|
+
/**
|
|
20
|
+
* Context passed to {@link AsReadableController.prepareRequest}.
|
|
21
|
+
*
|
|
22
|
+
* @since 0.1.143
|
|
23
|
+
*/
|
|
24
|
+
interface TDbRequestContext {
|
|
25
|
+
/** The endpoint being served. */
|
|
26
|
+
readonly endpoint: TDbRequestEndpoint;
|
|
27
|
+
/**
|
|
28
|
+
* The parsed Uniquery controls (`$select`, `$with`, `$search`, …) on read
|
|
29
|
+
* endpoints (`query`, `pages`, `geo`, `one`) — the object the rest of
|
|
30
|
+
* the pipeline validates and reads with. `undefined` on `meta`,
|
|
31
|
+
* `metaForm`, writes and actions.
|
|
32
|
+
*/
|
|
33
|
+
readonly controls?: Record<string, unknown>;
|
|
34
|
+
/** `"action"` endpoint only: the `@DbAction` name being run. */
|
|
35
|
+
readonly action?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Control DTO a {@link AsReadableController.validateControls} call checks against. @since 0.1.143 (`"geo"`) */
|
|
38
|
+
type TDbControlsType = "query" | "pages" | "getOne" | "geo";
|
|
39
|
+
/**
|
|
40
|
+
* A read request as {@link AsReadableController.parseRequest} hands it back.
|
|
41
|
+
*
|
|
42
|
+
* @since 0.1.143
|
|
43
|
+
*/
|
|
44
|
+
interface TDbParsedRequest {
|
|
45
|
+
/** The parsed URL (filter, controls, insights). */
|
|
46
|
+
parsed: ReturnType<typeof parseUrl>;
|
|
47
|
+
/** `parsed.controls` — the object `prepareRequest` saw and the pipeline validates. */
|
|
48
|
+
controls: Record<string, unknown>;
|
|
49
|
+
/** `"one"` only: the URL carried non-control (filter) parts, which `/one/:id` rejects. */
|
|
50
|
+
hasNonControl: boolean;
|
|
51
|
+
}
|
|
8
52
|
/**
|
|
9
53
|
* Abstract base class for read-only HTTP controllers over an Atscript interface.
|
|
10
54
|
*
|
|
@@ -93,12 +137,71 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
93
137
|
private _queryControlsValidator?;
|
|
94
138
|
private _pagesControlsValidator?;
|
|
95
139
|
private _getOneControlsValidator?;
|
|
140
|
+
private _geoControlsValidator?;
|
|
96
141
|
protected get queryControlsValidator(): Validator<any, unknown>;
|
|
97
142
|
protected get pagesControlsValidator(): Validator<any, unknown>;
|
|
98
143
|
protected get getOneControlsValidator(): Validator<any, unknown>;
|
|
99
|
-
|
|
144
|
+
/** `/geo` controls validator (since 0.1.143 — `/geo` runs {@link validateParsed} like `/query`). */
|
|
145
|
+
protected get geoControlsValidator(): Validator<any, unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* Per-request preparation hook — THE entry point for a permission layer to
|
|
148
|
+
* resolve per-request policy asynchronously (load the principal's scopes,
|
|
149
|
+
* evaluate grants) so the synchronous hooks that follow (`hasField`,
|
|
150
|
+
* `validateControls`, `checkCapabilities`) can consult it. Not
|
|
151
|
+
* implemented by default — defining it in a subclass switches it on (it is
|
|
152
|
+
* awaited only then, so unmodified controllers pay nothing).
|
|
153
|
+
*
|
|
154
|
+
* Runs once per request, before anything else consults the request:
|
|
155
|
+
* - read endpoints (`query`, `pages`, `geo`, `one`) — right after the
|
|
156
|
+
* URL is parsed, BEFORE validation, `hasField`, `validateControls`,
|
|
157
|
+
* `transformFilter` / `transformProjection`; `ctx.controls` are the
|
|
158
|
+
* parsed controls (mutating them is allowed and is what the pipeline
|
|
159
|
+
* then validates);
|
|
160
|
+
* - writes (`insert`, `replace`, `update`, `remove`) — at handler start,
|
|
161
|
+
* before the shape gate, `onWrite` / `onRemove` and any guard;
|
|
162
|
+
* - `meta` / `metaForm` — first;
|
|
163
|
+
* - `@DbAction` handlers of every level (`action`, with `ctx.action` = the
|
|
164
|
+
* action name) — from the action's interceptor (after the guards),
|
|
165
|
+
* before its ids are validated, its rows loaded, its row overlay built
|
|
166
|
+
* and the handler runs. A permission layer needs no separate action
|
|
167
|
+
* guard.
|
|
168
|
+
*
|
|
169
|
+
* A throw aborts the request with the thrown error (throw an `HttpError`
|
|
170
|
+
* for a specific status). Value-help controllers get it too.
|
|
171
|
+
*
|
|
172
|
+
* ```ts
|
|
173
|
+
* protected async prepareRequest(ctx: TDbRequestContext) {
|
|
174
|
+
* const scopes = await loadScopes(useAuthorization(), ctx.endpoint)
|
|
175
|
+
* if (!scopes) throw new HttpError(403)
|
|
176
|
+
* requestScopes.set(scopes) // read back by hasField / transformFilter
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @since 0.1.143
|
|
181
|
+
*/
|
|
182
|
+
protected prepareRequest?(ctx: TDbRequestContext): void | Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* The ONE request entry of every built-in route: with a `url` (read
|
|
185
|
+
* endpoints) it parses the query string — `/one` keeps only the `$`
|
|
186
|
+
* controls ({@link parseControlsOnlyFromUrl}), every other endpoint the
|
|
187
|
+
* whole query ({@link parseQueryString}) — and coerces boolean controls the
|
|
188
|
+
* URL grammar leaves as strings (`$actions=true`); then it awaits
|
|
189
|
+
* {@link prepareRequest} (when implemented) with the parsed controls.
|
|
190
|
+
* Without a `url` (writes, `meta`, `metaForm`) only the hook runs. Custom
|
|
191
|
+
* routes on a subclass should call it too.
|
|
192
|
+
*
|
|
193
|
+
* @since 0.1.143
|
|
194
|
+
*/
|
|
195
|
+
protected parseRequest(endpoint: TDbRequestEndpoint): Promise<undefined>;
|
|
196
|
+
protected parseRequest(endpoint: TDbRequestEndpoint, url: string): Promise<TDbParsedRequest>;
|
|
197
|
+
/**
|
|
198
|
+
* Validates the parsed controls against the endpoint's DTO — the hook for
|
|
199
|
+
* per-control authorization (override, call `super`, add rules). `"geo"`
|
|
200
|
+
* since 0.1.143 (`/geo` used to skip it).
|
|
201
|
+
*/
|
|
202
|
+
protected validateControls(controls: Record<string, unknown>, type: TDbControlsType): string | undefined;
|
|
100
203
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
101
|
-
protected validateParsed(parsed: Uniquery, type:
|
|
204
|
+
protected validateParsed(parsed: Uniquery, type: TDbControlsType): HttpError | undefined;
|
|
102
205
|
/**
|
|
103
206
|
* Per-request gate hook, invoked by the DB readable controller right after
|
|
104
207
|
* its capability gate (`checkCapabilities`) with the parsed query. The
|
|
@@ -143,6 +246,16 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
143
246
|
* response by principal.
|
|
144
247
|
*/
|
|
145
248
|
meta(): Promise<TMetaResponse>;
|
|
249
|
+
/**
|
|
250
|
+
* The `/meta` payload for the current request — the cached envelope
|
|
251
|
+
* through {@link applyMetaOverlay} — WITHOUT the `/meta` route's
|
|
252
|
+
* {@link prepareRequest} call. Internal consumers (e.g. `$actions`
|
|
253
|
+
* filtering on a read) use this, so the hook runs once per request with
|
|
254
|
+
* the endpoint actually being served.
|
|
255
|
+
*
|
|
256
|
+
* @since 0.1.143
|
|
257
|
+
*/
|
|
258
|
+
protected resolveMeta(): TMetaResponse | Promise<TMetaResponse>;
|
|
146
259
|
/**
|
|
147
260
|
* Identity of the inputs the cached `/meta` envelope is built from — a new
|
|
148
261
|
* value rebuilds it. Default: constant (built once). The DB readable
|
|
@@ -156,9 +269,20 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
156
269
|
* compiled `.as` class's `.name`, registered when an action's parameter is
|
|
157
270
|
* decorated with `@InputForm(FormType)`. Schemas are serialized once and
|
|
158
271
|
* cached per controller; the response uses the same annotation-allowlist
|
|
159
|
-
* policy as {@link getSerializeOptions}.
|
|
272
|
+
* policy as {@link getSerializeOptions}. Since 0.1.143 the form must pass
|
|
273
|
+
* {@link authorizeForm} — a refused form answers exactly like an unknown one.
|
|
160
274
|
*/
|
|
161
275
|
metaForm(name: string): Promise<TSerializedAnnotatedType>;
|
|
276
|
+
/**
|
|
277
|
+
* Per-request gate for `GET /meta/form/:name`: return `false` to refuse the
|
|
278
|
+
* form — the response is then the same 404 an unknown form gets, so a
|
|
279
|
+
* refused form's existence does not leak. `actionNames` are the discovered
|
|
280
|
+
* actions whose input form is `name` (a permission layer typically allows
|
|
281
|
+
* the form iff the caller may run at least one of them). Default: `true`.
|
|
282
|
+
*
|
|
283
|
+
* @since 0.1.143
|
|
284
|
+
*/
|
|
285
|
+
protected authorizeForm(_name: string, _actionNames: readonly string[]): boolean | Promise<boolean>;
|
|
162
286
|
/**
|
|
163
287
|
* Builds the `/meta` payload. Override in subclasses to populate source-specific
|
|
164
288
|
* fields. Subclasses that fully replace the envelope must call
|
|
@@ -350,6 +474,48 @@ interface TDbDecorateContext {
|
|
|
350
474
|
/** The request's parsed controls (`$select`, `$with`, `$actions`, …). Read-only by convention. */
|
|
351
475
|
controls: Record<string, unknown>;
|
|
352
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* One text / vector / geo index of the bound readable with the LOGICAL field
|
|
479
|
+
* paths it reads — see {@link AsDbReadableController.indexFieldPaths}.
|
|
480
|
+
*
|
|
481
|
+
* @since 0.1.143
|
|
482
|
+
*/
|
|
483
|
+
interface TDbIndexFieldPaths {
|
|
484
|
+
/** The name a request addresses the index by (`$index` for text and geo, `$vector` for vector). */
|
|
485
|
+
name: string;
|
|
486
|
+
type: "text" | "vector" | "geo";
|
|
487
|
+
/**
|
|
488
|
+
* Logical field paths the index reads. An index whose coverage cannot be
|
|
489
|
+
* derived from the model (e.g. a dynamic document-search mapping) lists
|
|
490
|
+
* every field — fail-closed for visibility gating.
|
|
491
|
+
*/
|
|
492
|
+
fields: readonly string[];
|
|
493
|
+
/** `true` for the index a request of this type uses when it names none. */
|
|
494
|
+
isDefault: boolean;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* The field visibility of a DB readable controller — see
|
|
498
|
+
* {@link AsDbReadableController.fieldVisibility}.
|
|
499
|
+
*
|
|
500
|
+
* @since 0.1.143
|
|
501
|
+
*/
|
|
502
|
+
interface TDbFieldVisibility {
|
|
503
|
+
/** `true` when `hasField` is overridden (visibility is request-scoped); else every real path is visible. */
|
|
504
|
+
readonly scoped: boolean;
|
|
505
|
+
/**
|
|
506
|
+
* `hasField(path)` and — when {@link scoped} — a `@db.column.derived`
|
|
507
|
+
* field of the bound readable only while its source path is visible too
|
|
508
|
+
* (a derived copy must not outlive a hidden source).
|
|
509
|
+
*/
|
|
510
|
+
readonly isVisible: (path: string) => boolean;
|
|
511
|
+
/**
|
|
512
|
+
* The paths sealed out of `readable`'s read projection for this request:
|
|
513
|
+
* its `@db.writeOnly` fields plus, when {@link scoped}, its derived fields
|
|
514
|
+
* whose source `hasField` hides. `prefix` is `readable`'s path from the
|
|
515
|
+
* controller: `""` for the bound readable, `"rel."` for a `$with` target.
|
|
516
|
+
*/
|
|
517
|
+
readonly sealedFor: (readable: AtscriptDbReadable<any>, prefix?: string) => ReadonlySet<string>;
|
|
518
|
+
}
|
|
353
519
|
/**
|
|
354
520
|
* Read-only database controller for Moost that works with any `AtscriptDbReadable`
|
|
355
521
|
* (tables or views). Provides query, pages, getOne, and meta endpoints.
|
|
@@ -385,13 +551,31 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
385
551
|
private _capabilities?;
|
|
386
552
|
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
387
553
|
protected metaCacheKey(): unknown;
|
|
388
|
-
/** Bound once: the visibility check ({@link hasField}) the gate hands to `capabilities.check`. */
|
|
389
|
-
private readonly _exists;
|
|
390
554
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
555
|
+
* THE field-visibility answer (since 0.1.143) every read surface consults:
|
|
556
|
+
* the capability gate, the index gate and `$search` fallback, id
|
|
557
|
+
* resolution, the `@db.writeOnly` / derived seals of the projection and of
|
|
558
|
+
* every `$with` level, `$actions` widening and action `requiredFields`
|
|
559
|
+
* (the actions module reaches it duck-typed, like {@link idSource}).
|
|
560
|
+
*/
|
|
561
|
+
protected readonly fieldVisibility: TDbFieldVisibility;
|
|
562
|
+
/** A subclass overrides {@link hasField}: visibility is request-scoped (derived rule, index gate, id options). */
|
|
563
|
+
private readonly _hasFieldOverridden;
|
|
564
|
+
/** `@db.column.derived` path → its source's logical path, per readable (bound + `$with` targets). */
|
|
565
|
+
private readonly _derivedSources;
|
|
566
|
+
/** The bound readable's entry of {@link _derivedSources}. */
|
|
567
|
+
private readonly _derivedSource;
|
|
568
|
+
/** `@db.writeOnly` paths of `$with` target readables, collected once per target. */
|
|
569
|
+
private readonly _targetWriteOnly;
|
|
570
|
+
private _indexFieldPathsCache?;
|
|
571
|
+
/** {@link _nativeSearch} per request, keyed by the request's parsed controls. */
|
|
572
|
+
private readonly _nativeSearchByRequest;
|
|
573
|
+
/**
|
|
574
|
+
* Id-resolution options (since 0.1.134): `{ isFieldVisible }` (the
|
|
575
|
+
* {@link fieldVisibility} check) when a subclass overrides {@link hasField},
|
|
576
|
+
* else `undefined` (the default accepts every real path, so resolution
|
|
577
|
+
* stays unfiltered). A unique index over a hidden field is never an
|
|
578
|
+
* identification.
|
|
395
579
|
*/
|
|
396
580
|
protected readonly _idOpts: TIdResolveOptions | undefined;
|
|
397
581
|
/** Narrowed id sources, one stable object per distinct visible-identification set. */
|
|
@@ -400,6 +584,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
400
584
|
private readonly _overlayIsNoOp;
|
|
401
585
|
/** `true` when a subclass implements {@link decorateRows} (the override switches the hook on). */
|
|
402
586
|
private readonly _decorates;
|
|
587
|
+
/** `true` when a subclass overrides {@link transformOne} or {@link transformFilter} (a row overlay may exist). */
|
|
588
|
+
private readonly _hasRowOverlay;
|
|
403
589
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
404
590
|
private readonly _quantityRefByPath;
|
|
405
591
|
/** `@db.column.searchable` paths — the `$search` fallback when the adapter has no native search. */
|
|
@@ -422,6 +608,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
422
608
|
get idSource(): IdValidationSource;
|
|
423
609
|
private _collectInvertibleFields;
|
|
424
610
|
private _collectQuantityRefs;
|
|
611
|
+
/** `readable`'s `@db.column.derived` path → source path map, collected once per readable. */
|
|
612
|
+
private _derivedSourcesOf;
|
|
425
613
|
private _collectAnnotated;
|
|
426
614
|
/**
|
|
427
615
|
* THE field-visibility hook: every gated path consults it before any
|
|
@@ -437,9 +625,18 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
437
625
|
* an action id (primary key and `preferredId` always are) — and the
|
|
438
626
|
* nested-object 400 hint lists visible leaves only. The default accepts every real path
|
|
439
627
|
* (`isValidFieldPath`). `/meta` does NOT consult it — prune hidden fields
|
|
440
|
-
* there with `applyMetaOverlay
|
|
441
|
-
*
|
|
442
|
-
*
|
|
628
|
+
* there with `applyMetaOverlay` ({@link indexFieldPaths} names what each
|
|
629
|
+
* search / geo index reads).
|
|
630
|
+
*
|
|
631
|
+
* Since 0.1.143 an override also gates the engine's indexes: a native
|
|
632
|
+
* text-search index (`$index`, or the default one), a vector index
|
|
633
|
+
* (`$vector`) or a geo index (`/geo`, `$index`) reading a hidden path
|
|
634
|
+
* answers exactly like a nonexistent index (400); a hidden DEFAULT text
|
|
635
|
+
* index falls back to the `@db.column.searchable` substring search over
|
|
636
|
+
* visible fields (or ignores the term when there are none). A
|
|
637
|
+
* `@db.column.derived` field is visible only while its source path is,
|
|
638
|
+
* and one whose source is hidden is sealed out of every read projection
|
|
639
|
+
* for the request, like a `@db.writeOnly` field.
|
|
443
640
|
*/
|
|
444
641
|
protected hasField(path: string): boolean;
|
|
445
642
|
/**
|
|
@@ -457,7 +654,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
457
654
|
* ran first); a bucket's source is checked like any other path (op
|
|
458
655
|
* `bucket`). After the per-path checks the core `$having` rule runs
|
|
459
656
|
* (`checkHavingKeys`: aliases or `$groupBy` fields only), so a readable mock
|
|
460
|
-
* and a real table answer alike.
|
|
657
|
+
* and a real table answer alike. Last (since 0.1.143, when {@link hasField}
|
|
658
|
+
* is overridden) the index gate: a text / vector / geo index the request
|
|
659
|
+
* uses must read only visible paths — see {@link indexFieldPaths}.
|
|
461
660
|
*/
|
|
462
661
|
protected checkCapabilities(parsed: {
|
|
463
662
|
filter?: FilterExpr;
|
|
@@ -482,8 +681,91 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
482
681
|
* resolved against the target table through `isValidFieldPath`.
|
|
483
682
|
*/
|
|
484
683
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
485
|
-
/**
|
|
486
|
-
|
|
684
|
+
/**
|
|
685
|
+
* {@link checkComputedSelect} (before the controls DTO), the controls DTO
|
|
686
|
+
* ({@link validateControls}), then the `$with` relation names at every
|
|
687
|
+
* level — BEFORE the `$with` sub-query paths ({@link validateInsights}),
|
|
688
|
+
* so a hidden or nonexistent nested relation answers `Unknown relation`,
|
|
689
|
+
* never `Unknown field "rel.sub"` (since 0.1.143) — then the insights and
|
|
690
|
+
* the joined-row write-only veto.
|
|
691
|
+
*/
|
|
692
|
+
protected validateParsed(parsed: Uniquery, type: TDbControlsType): HttpError | undefined;
|
|
693
|
+
/**
|
|
694
|
+
* `$with` relation names at every level (nested `$with` since 0.1.143):
|
|
695
|
+
* each segment of an entry's (dotted) name must be a relation of its
|
|
696
|
+
* level's readable that {@link hasField} accepts at its full path from
|
|
697
|
+
* this controller (`rel`, then `rel.sub` for a nested / dotted one) —
|
|
698
|
+
* hidden answers exactly like nonexistent: {@link unknownRelationError}
|
|
699
|
+
* with the entry's name and the relations visible at the level it failed
|
|
700
|
+
* at. A level whose target readable cannot be resolved is not descended.
|
|
701
|
+
*/
|
|
702
|
+
private _checkWithRelations;
|
|
703
|
+
/**
|
|
704
|
+
* `@db.writeOnly` paths of a `$with` target — its own fields only (its
|
|
705
|
+
* navigation descendants are sealed one level down, by their own target).
|
|
706
|
+
*/
|
|
707
|
+
private _writeOnlyOf;
|
|
708
|
+
/** {@link TDbFieldVisibility.sealedFor}. */
|
|
709
|
+
private _sealedFor;
|
|
710
|
+
/** The readable a `$with` entry name (`rel` or dotted `rel.sub`) loads from, if resolvable. */
|
|
711
|
+
private _relTarget;
|
|
712
|
+
/**
|
|
713
|
+
* Walks a `$with` tree pre-order: `visit(rel, target, path, controls)` for
|
|
714
|
+
* every entry whose target readable resolves (`path` = the entry's dotted
|
|
715
|
+
* path from this controller, `controls` = its sub-controls). The visitor
|
|
716
|
+
* returns replacement sub-controls, an `HttpError` to stop the walk (it is
|
|
717
|
+
* returned as-is), or `undefined` to keep the entry. Returns the rebuilt
|
|
718
|
+
* tree — the same array when nothing changed.
|
|
719
|
+
*/
|
|
720
|
+
private _walkWith;
|
|
721
|
+
/**
|
|
722
|
+
* The read controls with every level sealed — the root `$select` (`select`,
|
|
723
|
+
* the {@link transformProjection} result) and each `$with` entry's
|
|
724
|
+
* `$select` lose the paths {@link TDbFieldVisibility.sealedFor} names for
|
|
725
|
+
* their readable (an exclusion is forced when there is no projection), so
|
|
726
|
+
* sealed values never leave the database. Runs AFTER `transformProjection`
|
|
727
|
+
* so permission overlays compose: they see the wire `$select`, this
|
|
728
|
+
* guarantees the seal on whatever they return.
|
|
729
|
+
*/
|
|
730
|
+
private _sealControls;
|
|
731
|
+
/**
|
|
732
|
+
* The text / vector / geo indexes of the bound readable with the LOGICAL
|
|
733
|
+
* field paths each reads, and which one answers when a request names none.
|
|
734
|
+
* Text and vector entries are the adapter's `getSearchIndexes()` (the names
|
|
735
|
+
* `$index` / `$vector` address, their `fields` and `isDefault`; an entry
|
|
736
|
+
* without `fields` lists every field — fail-closed); geo entries are the
|
|
737
|
+
* `@db.index.geo` indexes. The request gate checks every listed path against
|
|
738
|
+
* {@link hasField} (only when `hasField` is overridden); permission
|
|
739
|
+
* overlays use it to prune `/meta` (`searchIndexes`, `searchable`,
|
|
740
|
+
* `vectorSearchable`, `geoSearchable`). Computed once. Override to describe
|
|
741
|
+
* an index the model cannot express.
|
|
742
|
+
*
|
|
743
|
+
* @since 0.1.143
|
|
744
|
+
*/
|
|
745
|
+
protected indexFieldPaths(): readonly TDbIndexFieldPaths[];
|
|
746
|
+
private _searchIndexFieldPaths;
|
|
747
|
+
/** `@db.index.geo` indexes — their fields carry physical names, mapped back to logical paths. */
|
|
748
|
+
private _geoIndexFieldPaths;
|
|
749
|
+
/** Every path `entry` reads is visible to this request. */
|
|
750
|
+
private _indexVisible;
|
|
751
|
+
/**
|
|
752
|
+
* Native text search serves this request: the adapter searches natively
|
|
753
|
+
* and — under an overridden {@link hasField} — the default index (when the
|
|
754
|
+
* request names none) reads only visible fields. A named index is gated by
|
|
755
|
+
* {@link checkCapabilities}. Answered once per request (keyed by its
|
|
756
|
+
* parsed controls).
|
|
757
|
+
*/
|
|
758
|
+
private _nativeSearch;
|
|
759
|
+
private _resolveNativeSearch;
|
|
760
|
+
/**
|
|
761
|
+
* Index visibility gate (only when {@link hasField} is overridden), run by
|
|
762
|
+
* {@link checkCapabilities} on every read: the geo index `/geo` reads
|
|
763
|
+
* (`$index`, or the default one), the vector index `$vector` names (or the
|
|
764
|
+
* default one) and the text index `$index` names must read only visible
|
|
765
|
+
* paths; otherwise the request is answered exactly like one naming a
|
|
766
|
+
* nonexistent index (the core's wording).
|
|
767
|
+
*/
|
|
768
|
+
private _checkIndexGate;
|
|
487
769
|
/**
|
|
488
770
|
* Compute an embedding vector from a search term.
|
|
489
771
|
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
@@ -526,13 +808,16 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
526
808
|
private widenQuantityRefProjection;
|
|
527
809
|
private _widenQuantityArrayProjection;
|
|
528
810
|
private _widenQuantityMapProjection;
|
|
529
|
-
/** WHY: the URL parser only auto-coerces `$count`; every other boolean control reaches us as `"true"`/`"1"` and would fail DTO validation. */
|
|
530
|
-
private _coerceActionsControl;
|
|
531
811
|
/** Normalize a post-`widenPreferredIdProjection` $select into `string[] | null` (`null` = all fields). */
|
|
532
812
|
private _resolveProjectionForAugmenter;
|
|
533
813
|
/** WHY: filter row/rows envelopes by the per-request `applyMetaOverlay` action set; skip `meta()` when overlay is identity. */
|
|
534
814
|
private _resolveAugmentEnvelopes;
|
|
535
|
-
/**
|
|
815
|
+
/**
|
|
816
|
+
* Returns a widened `$select` only when at least one `requiredFields` entry
|
|
817
|
+
* is missing; `null` means "no widening needed". A field the request may
|
|
818
|
+
* not see (`hasField`, derived source) is never added (since 0.1.143) —
|
|
819
|
+
* the action predicate sees it as `undefined`.
|
|
820
|
+
*/
|
|
536
821
|
private _widenSelectForActions;
|
|
537
822
|
private _prepareAugmentation;
|
|
538
823
|
/**
|
|
@@ -543,20 +828,20 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
543
828
|
*/
|
|
544
829
|
private _collectSearchFallbackFields;
|
|
545
830
|
/**
|
|
546
|
-
*
|
|
547
|
-
* exclusion
|
|
548
|
-
*
|
|
549
|
-
* overlays compose (they see the wire `$select`; this guarantees the seal on
|
|
550
|
-
* whatever they return).
|
|
831
|
+
* `select` without the `sealed` paths (see {@link _sealControls}); an
|
|
832
|
+
* exclusion of them is forced when there is no projection, or when every
|
|
833
|
+
* requested path was sealed.
|
|
551
834
|
*/
|
|
552
|
-
private
|
|
835
|
+
private _sealSelect;
|
|
553
836
|
/** First `@db.writeOnly` field referenced by `$groupBy` / aggregate `$select`, or undefined. */
|
|
554
837
|
private _findWriteOnlyInAggregate;
|
|
555
838
|
/**
|
|
556
839
|
* Merges the `$search` fallback into the filter: a case-insensitive literal
|
|
557
840
|
* substring match OR'd across the `@db.column.searchable` fields, `$and`-combined
|
|
558
|
-
* with the existing filter. Applies only when
|
|
559
|
-
*
|
|
841
|
+
* with the existing filter. Applies only when native search does not serve
|
|
842
|
+
* the request (no native search, or — since 0.1.143 — its default index
|
|
843
|
+
* reads a field {@link hasField} hides) and the request isn't a vector
|
|
844
|
+
* search (`$vector` consumes the term).
|
|
560
845
|
*/
|
|
561
846
|
protected applySearchFallback(filter: FilterExpr | undefined, controls: Record<string, unknown>): FilterExpr | undefined;
|
|
562
847
|
/**
|
|
@@ -622,6 +907,32 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
622
907
|
* strategy to its read-method family (count vs no-count).
|
|
623
908
|
*/
|
|
624
909
|
private _runReadWithActions;
|
|
910
|
+
/**
|
|
911
|
+
* The filter addressing exactly the ONE row `id` means — the readable's
|
|
912
|
+
* PK-first `resolveRowFilter` (since 0.1.143) under this request's
|
|
913
|
+
* identifications (`_idOpts`). `scope` (the row overlay) restricts which
|
|
914
|
+
* rows count while the id is pinned, so a row outside it never shadows one
|
|
915
|
+
* inside it. Readables without it (partial mocks) fall back to
|
|
916
|
+
* `resolveIdFilter`.
|
|
917
|
+
*/
|
|
918
|
+
protected resolveRowFilter(id: unknown, scope?: FilterExpr): Promise<FilterExpr | null>;
|
|
919
|
+
/**
|
|
920
|
+
* The ONE row `id` addresses, read with `controls`. A hidden unique key is
|
|
921
|
+
* not an identification (since 0.1.134): the id resolves as if that index
|
|
922
|
+
* did not exist. Since 0.1.143 it resolves primary key first, counting
|
|
923
|
+
* only rows inside the overlay — an out-of-scope row never shadows an
|
|
924
|
+
* in-scope one, so the answer is the same as if it did not exist — in one
|
|
925
|
+
* step (`findOneByRow`). Readables without it (partial mocks) pin the row
|
|
926
|
+
* with {@link resolveRowFilter}, then read it.
|
|
927
|
+
*/
|
|
928
|
+
private _findRow;
|
|
929
|
+
/**
|
|
930
|
+
* The row overlay id-addressed endpoints (`/one`, `DELETE`) apply:
|
|
931
|
+
* `transformOne({})` when non-empty, and only when a subclass overrides
|
|
932
|
+
* {@link transformOne} / {@link transformFilter} — `undefined` otherwise,
|
|
933
|
+
* at no cost (since 0.1.143).
|
|
934
|
+
*/
|
|
935
|
+
protected rowOverlay(): Promise<FilterExpr | undefined>;
|
|
625
936
|
/**
|
|
626
937
|
* Pick the first identification (PK or unique index) whose fields are all
|
|
627
938
|
* present in the query. A unique index over a field {@link hasField} hides
|
|
@@ -651,7 +962,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
651
962
|
* (meters), `$index` (geo index name), plus the standard filter / `$select` /
|
|
652
963
|
* `$with` / pagination syntax. Each row carries a computed `$distance`
|
|
653
964
|
* (meters). With `$page` / `$size` the response is the `/pages` envelope;
|
|
654
|
-
* otherwise a plain row array (`$skip` / `$limit` compose).
|
|
965
|
+
* otherwise a plain row array (`$skip` / `$limit` compose). Since 0.1.143
|
|
966
|
+
* the controls pass {@link validateParsed} (type `"geo"`) like `/query`'s,
|
|
967
|
+
* and the geo index must read only fields {@link hasField} shows.
|
|
655
968
|
*/
|
|
656
969
|
geo(url: string): Promise<DataType[] | {
|
|
657
970
|
data: DataType[];
|
|
@@ -674,7 +987,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
674
987
|
* gating as {@link getOne}.
|
|
675
988
|
*/
|
|
676
989
|
getOneComposite(query: Record<string, string>, url: string): Promise<DataType | HttpError>;
|
|
677
|
-
|
|
990
|
+
/**
|
|
991
|
+
* The shared `/one` pipeline: validation + capability gate (since 0.1.128
|
|
992
|
+
* on the composite form too — an unknown `$select` path used to reach the
|
|
993
|
+
* driver there), the sealed projection, the row read and its augmentation.
|
|
994
|
+
*/
|
|
995
|
+
private _readOne;
|
|
678
996
|
/**
|
|
679
997
|
* **GET /meta** — returns table/view metadata for UI.
|
|
680
998
|
*
|
|
@@ -702,15 +1020,18 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
702
1020
|
* ### Write pipeline (since 0.1.128)
|
|
703
1021
|
*
|
|
704
1022
|
* ```
|
|
705
|
-
*
|
|
1023
|
+
* prepareRequest (when implemented) → shape gate (400)
|
|
1024
|
+
* → onWrite / onRemove (outside any transaction)
|
|
706
1025
|
* → table op — the table's own transaction: validate → guardWrite / guardRemove
|
|
707
|
-
* (only when overridden) → re-validate → write
|
|
1026
|
+
* (only when overridden) → re-validate → write → nested phases
|
|
1027
|
+
* → checkWrite (only when overridden)
|
|
708
1028
|
* → 404 / 409 disambiguation
|
|
709
1029
|
* ```
|
|
710
1030
|
*
|
|
711
1031
|
* The guard is the table's `guard` write option (`TWriteOptions.guard` /
|
|
712
1032
|
* `TDeleteOptions.guard`); overriding `guardWrite` / `guardRemove` is the
|
|
713
|
-
* switch that passes it.
|
|
1033
|
+
* switch that passes it. `checkWrite` is the `check` write option, with
|
|
1034
|
+
* the same switch (since 0.1.143). Built-in failures are THROWN as `HttpError`
|
|
714
1035
|
* (wire-identical to returning them; a throw also rolls back a user-level
|
|
715
1036
|
* `withTransaction` wrapper).
|
|
716
1037
|
*/
|
|
@@ -762,6 +1083,30 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
762
1083
|
* the guard.
|
|
763
1084
|
*/
|
|
764
1085
|
protected guardRemove(_ctx: TDbRemoveGuardContext$1<DataType>): void | Promise<void>;
|
|
1086
|
+
/**
|
|
1087
|
+
* Post-write check (since 0.1.143) — a row-level "WITH CHECK". Overriding it
|
|
1088
|
+
* is the switch (as with {@link guardWrite}): the override is passed to the
|
|
1089
|
+
* table as its `check` write option and runs once per insert / replace /
|
|
1090
|
+
* update call (bulk forms included), inside the table's transaction, AFTER
|
|
1091
|
+
* the main write and every nested-relation phase. `ctx.filters` holds one
|
|
1092
|
+
* exact primary-key filter per written row; `ctx.count(filter)` counts
|
|
1093
|
+
* inside the same transaction — e.g. verify every written row still matches
|
|
1094
|
+
* a policy filter:
|
|
1095
|
+
*
|
|
1096
|
+
* ```ts
|
|
1097
|
+
* protected async checkWrite(ctx: TDbWriteCheckContext) {
|
|
1098
|
+
* const n = await ctx.count({ $and: [{ $or: ctx.filters }, tenantFilter()] })
|
|
1099
|
+
* if (n !== ctx.filters.length) throw new HttpError(403)
|
|
1100
|
+
* }
|
|
1101
|
+
* ```
|
|
1102
|
+
*
|
|
1103
|
+
* Throw to reject: the transaction rolls back and the error propagates
|
|
1104
|
+
* unchanged. When `ctx.transactional` is `false` (the adapter's
|
|
1105
|
+
* transaction is a pass-through, e.g. a standalone MongoDB) the write is
|
|
1106
|
+
* already durable — validate before the write instead (`guardWrite`).
|
|
1107
|
+
* Removes have no post-image and never call it.
|
|
1108
|
+
*/
|
|
1109
|
+
protected checkWrite(_ctx: TDbWriteCheckContext$1): void | Promise<void>;
|
|
765
1110
|
/**
|
|
766
1111
|
* Runs `fn` inside the bound table's adapter transaction (nested calls
|
|
767
1112
|
* join it). For custom actions and routes that need one transaction across
|
|
@@ -773,10 +1118,11 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
773
1118
|
/** `deleteOne`'s trailing options — see {@link _hookArgs}. */
|
|
774
1119
|
private readonly _removeArgs;
|
|
775
1120
|
/**
|
|
776
|
-
* A table call's trailing options, built once: `guard` only when
|
|
777
|
-
* hook is overridden, `isFieldVisible` only when `hasField`
|
|
778
|
-
* PK-less payload never resolves through a hidden unique
|
|
779
|
-
* nothing, so an unmodified controller calls the table exactly
|
|
1121
|
+
* A table call's trailing options, built once: `guard` / `check` only when
|
|
1122
|
+
* the matching hook is overridden, `isFieldVisible` only when `hasField`
|
|
1123
|
+
* is (an id or a PK-less payload never resolves through a hidden unique
|
|
1124
|
+
* key) — else nothing, so an unmodified controller calls the table exactly
|
|
1125
|
+
* as before.
|
|
780
1126
|
*/
|
|
781
1127
|
private _hookArgs;
|
|
782
1128
|
/** Resolves a hook result: `undefined` aborts with `abortMessage`, an `Error` is thrown, anything else passes. */
|
|
@@ -802,7 +1148,13 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
802
1148
|
* application; callers can detect mismatches via `modifiedCount < N`.
|
|
803
1149
|
*/
|
|
804
1150
|
private _resolveBulkCas;
|
|
805
|
-
/**
|
|
1151
|
+
/**
|
|
1152
|
+
* Deletes by id (guard forwarded when overridden) and maps "nothing
|
|
1153
|
+
* deleted" to 404. Since 0.1.143 the row overlay ({@link rowOverlay}) is
|
|
1154
|
+
* the delete's `scope`: the id is pinned among in-scope rows inside the
|
|
1155
|
+
* table's transaction and an out-of-scope row is not deleted — a 404,
|
|
1156
|
+
* exactly like a missing one.
|
|
1157
|
+
*/
|
|
806
1158
|
private _deleteOrThrow;
|
|
807
1159
|
/**
|
|
808
1160
|
* **POST /** — inserts one or many records.
|
|
@@ -831,6 +1183,12 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
831
1183
|
* returns 404 when the row is genuinely missing, 409 with
|
|
832
1184
|
* `{ error: "version_mismatch", currentVersion: N }` when it's present
|
|
833
1185
|
* but the supplied version is stale (§6.3). Callers throw the result.
|
|
1186
|
+
*
|
|
1187
|
+
* The row is the one the write targeted (since 0.1.143): the table's
|
|
1188
|
+
* `recordFilter` — the write's own resolution, primary key first — so a
|
|
1189
|
+
* payload carrying the full primary key addresses that row only, never a
|
|
1190
|
+
* different row that merely shares a unique value with the payload.
|
|
1191
|
+
* Tables without it (partial mocks) resolve through {@link resolveRowFilter}.
|
|
834
1192
|
*/
|
|
835
1193
|
protected _disambiguateMismatch(data: unknown, versionColumn: string): Promise<HttpError>;
|
|
836
1194
|
/**
|
|
@@ -845,6 +1203,13 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
845
1203
|
}
|
|
846
1204
|
//#endregion
|
|
847
1205
|
//#region src/as-value-help.controller.d.ts
|
|
1206
|
+
/**
|
|
1207
|
+
* A value-help projection: the parsed `$select` (inclusion list, or a
|
|
1208
|
+
* `{ path: 0 | 1 }` map for the `-field` exclusion form).
|
|
1209
|
+
*
|
|
1210
|
+
* @since 0.1.143
|
|
1211
|
+
*/
|
|
1212
|
+
type ValueHelpSelect<T> = (keyof T | string)[] | Record<string, 0 | 1>;
|
|
848
1213
|
/**
|
|
849
1214
|
* Parsed Uniquery controls with the `$search` field carved out for value-help
|
|
850
1215
|
* use (the core DTO includes it but we narrow the type here so implementations
|
|
@@ -877,11 +1242,19 @@ interface ValueHelpQuery<T> {
|
|
|
877
1242
|
* one of their own (see {@link AsDbReadableController} for the
|
|
878
1243
|
* `@db.column.filterable` / `@db.column.sortable` pattern).
|
|
879
1244
|
*
|
|
880
|
-
* **
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
1245
|
+
* **Per-request scoping** (since 0.1.143) — the same three seams as the DB
|
|
1246
|
+
* controllers, applied by the base routes to every value-help source:
|
|
1247
|
+
* {@link transformFilter} (row overlay: `/query` / `/pages` filter, `/one`
|
|
1248
|
+
* rows), {@link transformProjection} (returned columns) and {@link hasField}
|
|
1249
|
+
* (a hidden field answers like an unknown one in filter / sort / select and
|
|
1250
|
+
* never matches `$search`).
|
|
1251
|
+
*
|
|
1252
|
+
* **Actions are NOT supported on value-help controllers.** The `/meta`
|
|
1253
|
+
* payload still includes `actions: []` for shape uniformity; since 0.1.143
|
|
1254
|
+
* any `@DbAction` / `@DbActions*` on a value-help controller is a hard error
|
|
1255
|
+
* (at decoration, or at construction for actions inherited from a base) —
|
|
1256
|
+
* previously it was dropped from `/meta` while its `@Post` route still ran
|
|
1257
|
+
* without any gate. Value-help is for FK pickers and dictionary surfaces.
|
|
885
1258
|
*/
|
|
886
1259
|
declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> extends AsReadableController<T, DataType> {
|
|
887
1260
|
/** Per-prop metadata map of the bound interface; eagerly built once. */
|
|
@@ -902,9 +1275,59 @@ declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType =
|
|
|
902
1275
|
data: DataType[];
|
|
903
1276
|
count: number;
|
|
904
1277
|
}>;
|
|
905
|
-
/**
|
|
1278
|
+
/**
|
|
1279
|
+
* Returns the row whose primary key matches `id`, or `null` on miss. The
|
|
1280
|
+
* `/one` routes then apply {@link transformFilter} (a row outside the
|
|
1281
|
+
* overlay is a 404) and {@link transformProjection} to it in memory.
|
|
1282
|
+
*/
|
|
906
1283
|
protected abstract getOne(id: string | number): Promise<DataType | null>;
|
|
1284
|
+
/**
|
|
1285
|
+
* THE field-visibility hook: `true` when `path` is a field of the bound
|
|
1286
|
+
* interface visible to this request. A path it rejects in the request's
|
|
1287
|
+
* filter / `$sort` / `$select` gets the same `Unknown field "x"` 400 as a
|
|
1288
|
+
* nonexistent one, and a hidden field never takes part in `$search`
|
|
1289
|
+
* (`AsJsonValueHelpController`). Override to hide fields per request — it
|
|
1290
|
+
* gates the request only; strip the column from responses with
|
|
1291
|
+
* {@link transformProjection}.
|
|
1292
|
+
*/
|
|
907
1293
|
protected hasField(path: string): boolean;
|
|
1294
|
+
/**
|
|
1295
|
+
* Row overlay — the value-help counterpart of the DB controllers'
|
|
1296
|
+
* `transformFilter`. Receives the request filter of `/query` / `/pages`
|
|
1297
|
+
* and returns the one to run (AND your scope in: `{ $and: [filter, scope] }`).
|
|
1298
|
+
* `/one` evaluates `transformFilter({})` against the found row in memory: a
|
|
1299
|
+
* row outside it answers 404, exactly like a missing one. Default: identity.
|
|
1300
|
+
* May be async.
|
|
1301
|
+
*
|
|
1302
|
+
* @since 0.1.143
|
|
1303
|
+
*/
|
|
1304
|
+
protected transformFilter(filter: FilterExpr): FilterExpr | Promise<FilterExpr>;
|
|
1305
|
+
/**
|
|
1306
|
+
* Projection hook — receives the request `$select` (`undefined` when
|
|
1307
|
+
* absent; `/one` always passes `undefined`) and returns the projection to
|
|
1308
|
+
* apply: an inclusion list / `{ path: 1 }` map, or an exclusion
|
|
1309
|
+
* `{ path: 0 }` map. Default: identity. May be async.
|
|
1310
|
+
*
|
|
1311
|
+
* @since 0.1.143
|
|
1312
|
+
*/
|
|
1313
|
+
protected transformProjection(select: ValueHelpSelect<DataType> | undefined): ValueHelpSelect<DataType> | undefined | Promise<ValueHelpSelect<DataType> | undefined>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Normalizes a value-help `$select` (the raw `parseUrl` form or a
|
|
1316
|
+
* {@link transformProjection} result) to the `@atscript/db-memory`
|
|
1317
|
+
* `{ path: 0 | 1 }` projection map:
|
|
1318
|
+
* - `string[]` (e.g. from `?$select=a,b`) → inclusion map `{ a: 1, b: 1 }`,
|
|
1319
|
+
* - a plain `{ path: 0 | 1 }` object → passed through (0 / falsy → exclude),
|
|
1320
|
+
* - anything else / empty → `undefined` (no projection; whole rows returned).
|
|
1321
|
+
*/
|
|
1322
|
+
protected normalizeSelect(select: unknown): Record<string, 0 | 1> | undefined;
|
|
1323
|
+
/** `filter` through {@link transformFilter} and `controls.$select` through {@link transformProjection}. */
|
|
1324
|
+
private _scopedQuery;
|
|
1325
|
+
/**
|
|
1326
|
+
* `getOne` + the row overlay (miss → 404) + the projection, applied in
|
|
1327
|
+
* memory — `getOne` is the subclass's own lookup (id coercion included),
|
|
1328
|
+
* so it is not re-expressed as a {@link query} filter.
|
|
1329
|
+
*/
|
|
1330
|
+
private _scopedOne;
|
|
908
1331
|
/**
|
|
909
1332
|
* **GET /query** — returns an array of matched rows (up to `$limit`).
|
|
910
1333
|
*/
|
|
@@ -964,8 +1387,8 @@ declare abstract class AsValueHelpController<T extends TAtscriptAnnotatedType =
|
|
|
964
1387
|
* ties). Direction via `-` prefix on the field name or `{ [field]: 'asc' |
|
|
965
1388
|
* 'desc' | 1 | -1 }`.
|
|
966
1389
|
* - Search is case-insensitive substring matching across every field listed in
|
|
967
|
-
* {@link searchableFields}
|
|
968
|
-
* engine has no `$search`.
|
|
1390
|
+
* {@link searchableFields} that {@link hasField} keeps visible. This is
|
|
1391
|
+
* value-help's own concern — the shared engine has no `$search`.
|
|
969
1392
|
* - Projection (`$select`) supports inclusion (`[fields]` / `{ f: 1 }`) and
|
|
970
1393
|
* exclusion (`{ f: 0 }`) with dot-path nesting; the primary key is NOT auto-
|
|
971
1394
|
* added (a value-help projection returns exactly the selected fields).
|
|
@@ -999,14 +1422,6 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
|
|
|
999
1422
|
* Returns `undefined` when nothing sortable was parsed (engine skips sorting).
|
|
1000
1423
|
*/
|
|
1001
1424
|
private normalizeSort;
|
|
1002
|
-
/**
|
|
1003
|
-
* Normalizes the raw `parseUrl` `$select` form to the engine's `{ path: 0 |
|
|
1004
|
-
* 1 }` projection map:
|
|
1005
|
-
* - `string[]` (e.g. from `?$select=a,b`) → inclusion map `{ a: 1, b: 1 }`,
|
|
1006
|
-
* - a plain `{ path: 0 | 1 }` object → passed through (0 / falsy → exclude),
|
|
1007
|
-
* - anything else / empty → `undefined` (no projection; whole rows returned).
|
|
1008
|
-
*/
|
|
1009
|
-
private normalizeSelect;
|
|
1010
1425
|
}
|
|
1011
1426
|
//#endregion
|
|
1012
1427
|
//#region src/actions/types.d.ts
|
|
@@ -1090,6 +1505,14 @@ interface BaseActionOpts extends Partial<Omit<TDbActionInfo$1, "name" | "level"
|
|
|
1090
1505
|
* `AsDbController`) — the bound table from the controller wins.
|
|
1091
1506
|
*/
|
|
1092
1507
|
table?: AtscriptDbTable<any>;
|
|
1508
|
+
/**
|
|
1509
|
+
* `'rows'` level (`@DbActionIDs` / `@DbActionRows`): the most identifiers
|
|
1510
|
+
* one request may carry. Above it the request is rejected with 400 before
|
|
1511
|
+
* any row is loaded. Default `1000`. Server-internal — never on the wire.
|
|
1512
|
+
*
|
|
1513
|
+
* @since 0.1.143
|
|
1514
|
+
*/
|
|
1515
|
+
maxIds?: number;
|
|
1093
1516
|
}
|
|
1094
1517
|
/**
|
|
1095
1518
|
* Options accepted by `@DbAction(name, opts?)`. Generic over `TRow` (the
|
|
@@ -1479,6 +1902,16 @@ declare function errorEnvelope(statusCode: number, message: string, errors: THtt
|
|
|
1479
1902
|
* message (defaults to `message`).
|
|
1480
1903
|
*/
|
|
1481
1904
|
declare function badRequest(path: string, message: string, top?: string): HttpError;
|
|
1905
|
+
/**
|
|
1906
|
+
* The 400 of a `$with` relation the request cannot reach — nonexistent, or
|
|
1907
|
+
* hidden by `hasField` (the two answer alike): `Unknown relation "<name>"`,
|
|
1908
|
+
* the envelope message listing `visible` (the relations the caller CAN
|
|
1909
|
+
* load at that level). The single source of this wording — a permission
|
|
1910
|
+
* layer that rejects a relation itself should throw this.
|
|
1911
|
+
*
|
|
1912
|
+
* @since 0.1.143
|
|
1913
|
+
*/
|
|
1914
|
+
declare function unknownRelationError(name: string, visible: readonly string[]): HttpError;
|
|
1482
1915
|
//#endregion
|
|
1483
1916
|
//#region src/validation-interceptor.d.ts
|
|
1484
1917
|
declare const validationErrorTransform: () => import("moost").TInterceptorDef;
|
|
@@ -1487,10 +1920,16 @@ declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorato
|
|
|
1487
1920
|
//#region src/actions/db-action.decorator.d.ts
|
|
1488
1921
|
/**
|
|
1489
1922
|
* Mark a controller method as a database action surfaced via `/meta`. Writes
|
|
1490
|
-
* `atscript_db_action` metadata and
|
|
1491
|
-
*
|
|
1492
|
-
*
|
|
1493
|
-
*
|
|
1923
|
+
* `atscript_db_action` metadata and, for every `'row'` / `'rows'` action,
|
|
1924
|
+
* registers a Moost interceptor: the gate when `disabled` is set, else the
|
|
1925
|
+
* bound-table injector that also verifies the ids against the controller's
|
|
1926
|
+
* row overlay (since 0.1.143). Either first awaits the controller's
|
|
1927
|
+
* `prepareRequest({ endpoint: "action", action })` when it defines one —
|
|
1928
|
+
* before any id is validated or row loaded; a `'table'`-level action on an
|
|
1929
|
+
* `AsReadableController` subclass gets an interceptor for that alone
|
|
1930
|
+
* (since 0.1.143). Stacking two `@DbAction` on the same method
|
|
1931
|
+
* is undefined and emits a warning. Throws on a value-help controller
|
|
1932
|
+
* (since 0.1.143 — value-help controllers do not support actions).
|
|
1494
1933
|
*
|
|
1495
1934
|
* Generic over `TRow` (annotate at the call site: `@DbAction<Order>(...)`)
|
|
1496
1935
|
* and `R` (the literal `requiredFields` tuple, inferred via `const R`).
|
|
@@ -1588,7 +2027,7 @@ declare function DbActionRows(): ParameterDecorator;
|
|
|
1588
2027
|
* `disabled` predicate is type-narrowed by its own `requiredFields` literal.
|
|
1589
2028
|
*
|
|
1590
2029
|
* Multiple `@DbActions` (and shortcut) decorators on the same class
|
|
1591
|
-
* accumulate.
|
|
2030
|
+
* accumulate. Throws on a value-help controller (since 0.1.143).
|
|
1592
2031
|
*/
|
|
1593
2032
|
declare function DbActions<TRow = unknown, const D extends Record<string, unknown> = {}>(dict: D & ValidatedDict<TRow, D>): ClassDecorator;
|
|
1594
2033
|
/** Sugar for `@DbActions` with `level: 'table'` injected into each entry. */
|
|
@@ -1784,6 +2223,26 @@ declare const QUERY_CONTROLS: readonly string[];
|
|
|
1784
2223
|
declare const PAGES_CONTROLS: readonly string[];
|
|
1785
2224
|
declare const ONE_CONTROLS: readonly string[];
|
|
1786
2225
|
//#endregion
|
|
2226
|
+
//#region src/permissions/crud-handlers.d.ts
|
|
2227
|
+
/**
|
|
2228
|
+
* The handler method(s) serving each CRUD op on `AsDbReadableController` /
|
|
2229
|
+
* `AsDbController` — what a permission layer authorizes a `/meta` `crud`
|
|
2230
|
+
* entry through (an op is allowed when ANY of its handlers is). `one` is
|
|
2231
|
+
* served by `/one/:id` and `/one?…`, `remove` by `DELETE /:id` and
|
|
2232
|
+
* `DELETE /?…`. Readables (no writes) serve only the read ops.
|
|
2233
|
+
*
|
|
2234
|
+
* @since 0.1.143
|
|
2235
|
+
*/
|
|
2236
|
+
declare const DB_CRUD_HANDLERS: Readonly<Record<TCrudOp$1, readonly string[]>>;
|
|
2237
|
+
/**
|
|
2238
|
+
* The handler method(s) serving each CRUD op on the value-help controllers
|
|
2239
|
+
* (`AsValueHelpController` / `AsJsonValueHelpController` — read ops only,
|
|
2240
|
+
* no `geo`). Same contract as {@link DB_CRUD_HANDLERS}.
|
|
2241
|
+
*
|
|
2242
|
+
* @since 0.1.143
|
|
2243
|
+
*/
|
|
2244
|
+
declare const VALUE_HELP_CRUD_HANDLERS: Readonly<Partial<Record<TCrudOp$1, readonly string[]>>>;
|
|
2245
|
+
//#endregion
|
|
1787
2246
|
//#region src/meta/terminal-ref.d.ts
|
|
1788
2247
|
/**
|
|
1789
2248
|
* Terminal-reference resolution for `/meta` and `/meta/form/:name`
|
|
@@ -1833,4 +2292,4 @@ declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef |
|
|
|
1833
2292
|
*/
|
|
1834
2293
|
declare function applyTerminalRefs(serialized: TSerializedAnnotatedType, runtime: TAtscriptAnnotatedType, options: TSerializeOptions): TSerializedAnnotatedType;
|
|
1835
2294
|
//#endregion
|
|
1836
|
-
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbDecorateContext, TDbDecorateEndpoint, type TDbRemoveGuardContext, type TDbWriteAction, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|
|
2295
|
+
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DB_CRUD_HANDLERS, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbControlsType, TDbDecorateContext, TDbDecorateEndpoint, TDbFieldVisibility, TDbIndexFieldPaths, TDbParsedRequest, type TDbRemoveGuardContext, TDbRequestContext, TDbRequestEndpoint, type TDbWriteAction, type TDbWriteCheckContext, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, VALUE_HELP_CRUD_HANDLERS, ValueHelpQuery, ValueHelpSelect, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, unknownRelationError, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|