@atscript/moost-db 0.1.147 → 0.1.149
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/{db-space-registry-C_wft7kl.mjs → db-space-registry-Cdb0FGCn.mjs} +28 -2
- package/dist/{db-space-registry-CKR6G-hi.d.mts → db-space-registry-Cvd8s9ql.d.mts} +17 -1
- package/dist/{db-space-registry-DqKr5Zdk.cjs → db-space-registry-D5gnC_-6.cjs} +32 -0
- package/dist/{db-space-registry-CWpYwZ4R.d.cts → db-space-registry-fXB8LnGe.d.cts} +17 -1
- package/dist/index.cjs +1475 -286
- package/dist/index.d.cts +445 -51
- package/dist/index.d.mts +445 -51
- package/dist/index.mjs +1474 -287
- package/dist/testing.cjs +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +16 -14
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as resolveDbSpace, i as provideDbSpace, n as clearDbSpaces, r as closeDbSpaces, t as DEFAULT_DB_SPACE } from "./db-space-registry-Cvd8s9ql.mjs";
|
|
2
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
|
|
3
3
|
import { HttpError, MoostHttp } from "@moostjs/event-http";
|
|
4
4
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
@@ -61,6 +61,14 @@ interface TDbRequestContext {
|
|
|
61
61
|
readonly hasRelationFilters?: boolean;
|
|
62
62
|
/** `"action"` / `"delegatedAction"` endpoints only: the `@DbAction` name being run. */
|
|
63
63
|
readonly action?: string;
|
|
64
|
+
/**
|
|
65
|
+
* `"insert"` endpoint only: the `?$onConflict=` mode the request carries
|
|
66
|
+
* (`"ignore"`); absent for a plain insert. A controller can refuse it by
|
|
67
|
+
* throwing from `prepareRequest`.
|
|
68
|
+
*
|
|
69
|
+
* @since 0.1.148
|
|
70
|
+
*/
|
|
71
|
+
readonly onConflict?: "ignore";
|
|
64
72
|
}
|
|
65
73
|
/** Control DTO a {@link AsReadableController.validateControls} call checks against. @since 0.1.143 (`"geo"`) */
|
|
66
74
|
type TDbControlsType = "query" | "pages" | "getOne" | "geo";
|
|
@@ -210,8 +218,8 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
210
218
|
protected prepareRequest?(ctx: TDbRequestContext): void | Promise<void>;
|
|
211
219
|
/**
|
|
212
220
|
* The ONE request entry of every built-in route: with a `url` (read
|
|
213
|
-
* endpoints) it parses the query string — `/one`
|
|
214
|
-
* controls ({@link parseControlsOnlyFromUrl}), every other endpoint the
|
|
221
|
+
* endpoints, and `POST /`) it parses the query string — `/one` and `POST /`
|
|
222
|
+
* keep only the `$` controls ({@link parseControlsOnlyFromUrl}), every other endpoint the
|
|
215
223
|
* whole query ({@link parseQueryString}) — and coerces boolean controls the
|
|
216
224
|
* URL grammar leaves as strings (`$actions=true`); then it awaits
|
|
217
225
|
* {@link prepareRequest} (when implemented) with the parsed controls.
|
|
@@ -377,6 +385,12 @@ type TDbQueryTargetOpts = boolean | {
|
|
|
377
385
|
* registered symbol, like `ACTION_OVERLAY`).
|
|
378
386
|
*/
|
|
379
387
|
declare const RESOLVE_TARGET: unique symbol;
|
|
388
|
+
/**
|
|
389
|
+
* `q` of {@link AsDbReadableController.resolveQuery}: a `GET /query` string, or a
|
|
390
|
+
* query-target envelope without `dryRun`.
|
|
391
|
+
* @since 0.1.149
|
|
392
|
+
*/
|
|
393
|
+
type TDbResolveQueryInput = string | Omit<TDbActionQueryTarget, "dryRun">;
|
|
380
394
|
/** What {@link RESOLVE_TARGET} resolves. */
|
|
381
395
|
interface TTargetRequest {
|
|
382
396
|
action: string;
|
|
@@ -392,6 +406,8 @@ interface TTargetRequest {
|
|
|
392
406
|
* rows); `"read"` — `transformFilter` (a view resolving rows it delegates).
|
|
393
407
|
*/
|
|
394
408
|
overlay: "action" | "read";
|
|
409
|
+
/** Identity fields to report the READ-visible subset of ({@link TResolvedTarget.visibleOf}). */
|
|
410
|
+
visibleOf?: readonly string[];
|
|
395
411
|
/** Fields of the phase-1 read (sorted by the first identification's fields). */
|
|
396
412
|
select: readonly string[];
|
|
397
413
|
/** Key sets `exclude` entries may use besides the controller's identifications. */
|
|
@@ -406,6 +422,8 @@ interface TResolvedTarget {
|
|
|
406
422
|
dryRun: boolean;
|
|
407
423
|
/** The validated `exclude` entries (already applied to {@link rows}). */
|
|
408
424
|
exclude: Record<string, unknown>[];
|
|
425
|
+
/** {@link TTargetRequest.visibleOf} filtered by the read's field visibility. */
|
|
426
|
+
visibleOf?: readonly string[];
|
|
409
427
|
/**
|
|
410
428
|
* The rows `ids` address that STILL match the target (filter, search,
|
|
411
429
|
* overlay, `queryTargetScope`, `exclude`), aligned with `ids`; `select`
|
|
@@ -589,6 +607,45 @@ type EntryRequiredFields<E, TRow> = E extends {
|
|
|
589
607
|
} ? R extends readonly FlatKey<TRow>[] ? R : [] : [];
|
|
590
608
|
type ValidatedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntry<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
591
609
|
type ValidatedUnpinnedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntryUnpinned<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
610
|
+
/**
|
|
611
|
+
* An id as an id-addressed endpoint received it: a path scalar (always a
|
|
612
|
+
* string from the URL) or an identification object (`?field=value` forms,
|
|
613
|
+
* action bodies).
|
|
614
|
+
*
|
|
615
|
+
* @since 0.1.148
|
|
616
|
+
*/
|
|
617
|
+
type TDbRowIdInput = string | number | boolean | Record<string, unknown>;
|
|
618
|
+
/**
|
|
619
|
+
* Which endpoint asks `resolveRowIds`:
|
|
620
|
+
*
|
|
621
|
+
* - `"action"` — an action route: the body's validated ids;
|
|
622
|
+
* - `"available"` — `GET /meta/actions/:id` | `?…` (and a view asking its source);
|
|
623
|
+
* - `"one"` — `GET /one/:id` | `?…`;
|
|
624
|
+
* - `"remove"` — `DELETE /:id` | `?…`.
|
|
625
|
+
*
|
|
626
|
+
* @since 0.1.148
|
|
627
|
+
*/
|
|
628
|
+
type TDbRowIdPurpose = "action" | "available" | "one" | "remove";
|
|
629
|
+
/**
|
|
630
|
+
* Context of `AsDbReadableController.resolveRowIds`.
|
|
631
|
+
*
|
|
632
|
+
* @since 0.1.148
|
|
633
|
+
*/
|
|
634
|
+
interface TDbRowIdsContext {
|
|
635
|
+
readonly purpose: TDbRowIdPurpose;
|
|
636
|
+
/** `"action"` only: the action's name. */
|
|
637
|
+
readonly action?: string;
|
|
638
|
+
/** `"action"` only: the action's level. */
|
|
639
|
+
readonly level?: "row" | "rows";
|
|
640
|
+
/**
|
|
641
|
+
* The row overlay the endpoint will apply to the resolved id
|
|
642
|
+
* (`rowOverlay()`; for actions the gate's overlay), `undefined` when none.
|
|
643
|
+
* Server data — never sent to the client. Resolve INSIDE it when an alias
|
|
644
|
+
* could name several rows, so a row the caller can't reach never shadows
|
|
645
|
+
* one they can.
|
|
646
|
+
*/
|
|
647
|
+
readonly overlay?: FilterExpr;
|
|
648
|
+
}
|
|
592
649
|
//#endregion
|
|
593
650
|
//#region src/actions/discover.d.ts
|
|
594
651
|
/**
|
|
@@ -622,6 +679,24 @@ declare const ALLOWED_ACTIONS: unique symbol;
|
|
|
622
679
|
/** `true` when the controller class declares `@DbActionsFrom` (inherited included). */
|
|
623
680
|
declare function hasActionDelegations(ctor: Function): boolean;
|
|
624
681
|
//#endregion
|
|
682
|
+
//#region src/actions/rows-by-id.d.ts
|
|
683
|
+
/** One id the client sent, with the {@link identityKey} of the id it resolved to. */
|
|
684
|
+
interface TRequestedId {
|
|
685
|
+
id: Record<string, unknown>;
|
|
686
|
+
key: string;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* The result of a `resolveRowIds` call over an action's ids: the resolved
|
|
690
|
+
* ids with duplicate identities collapsed to the first (what handlers and
|
|
691
|
+
* the row load see) and — only when an id changed or collapsed — EVERY
|
|
692
|
+
* request id in request order (`requests`), the single model all refusals,
|
|
693
|
+
* reasons, summaries and counts are judged and reported in.
|
|
694
|
+
*/
|
|
695
|
+
interface TAppliedIds {
|
|
696
|
+
ids: Record<string, unknown>[];
|
|
697
|
+
requests?: readonly TRequestedId[];
|
|
698
|
+
}
|
|
699
|
+
//#endregion
|
|
625
700
|
//#region src/actions/scope-context.d.ts
|
|
626
701
|
/**
|
|
627
702
|
* Which surface asks {@link TDbActionScopeContext} — see
|
|
@@ -636,17 +711,30 @@ declare function hasActionDelegations(ctor: Function): boolean;
|
|
|
636
711
|
type TDbActionScopePurpose = "execute" | "rows" | "available";
|
|
637
712
|
/**
|
|
638
713
|
* The candidate rows `AsDbReadableController.actionRowScope` is asked about
|
|
639
|
-
* (since 0.1.147). Every candidate is
|
|
640
|
-
*
|
|
714
|
+
* (since 0.1.147). Every candidate is inside the controller's row overlay;
|
|
715
|
+
* the hook's result restricts them further.
|
|
716
|
+
*
|
|
717
|
+
* Exception (since 0.1.148): at `purpose: "execute"` with NO row overlay, the
|
|
718
|
+
* hook is asked BEFORE the rows are loaded when the request's ids are in
|
|
719
|
+
* `preferredId` shape — `ids` are then the request's (resolved) ids, deduped,
|
|
720
|
+
* and may name rows that do not exist (`loadRows` omits them). A restriction
|
|
721
|
+
* it returns joins the single row load (`id ∧ scope`); an answer of
|
|
722
|
+
* `undefined` / `null` / `{}` makes the gate load nothing.
|
|
641
723
|
*
|
|
642
724
|
* @since 0.1.147
|
|
643
725
|
*/
|
|
644
726
|
interface TDbActionScopeContext {
|
|
645
|
-
/**
|
|
727
|
+
/**
|
|
728
|
+
* Which surface asks: `"execute"` — the action gate (≤ `maxIds` ids; one
|
|
729
|
+
* batch of a query target); `"rows"` — `$actions` on a read (also a view's
|
|
730
|
+
* delegated verdicts), the read's rows; `"available"` — `GET /meta/actions/:id`,
|
|
731
|
+
* the one row.
|
|
732
|
+
*/
|
|
646
733
|
readonly purpose: TDbActionScopePurpose;
|
|
647
734
|
/**
|
|
648
735
|
* The candidates' identities (`preferredId`-shaped), deduped and never
|
|
649
|
-
* empty
|
|
736
|
+
* empty — at `"execute"` without a row overlay, the request's ids (they may
|
|
737
|
+
* not exist; see above). ONE array object per evaluation, shared by every action of it —
|
|
650
738
|
* memoize on it (`WeakMap`) when several actions derive the same filter.
|
|
651
739
|
*/
|
|
652
740
|
readonly ids: readonly Record<string, unknown>[];
|
|
@@ -672,6 +760,10 @@ declare const ACTION_OVERLAY: unique symbol;
|
|
|
672
760
|
declare const ACTION_SCOPE: unique symbol;
|
|
673
761
|
/** `true` when the controller overrides `actionRowScope` (since 0.1.147). */
|
|
674
762
|
declare const ACTION_SCOPED: unique symbol;
|
|
763
|
+
/** The controller's internal `resolveRowIds` call for an action's ids (since 0.1.148). */
|
|
764
|
+
declare const ROW_RESOLVE_IDS: unique symbol;
|
|
765
|
+
/** `true` when the controller overrides `resolveRowIds` (since 0.1.148). */
|
|
766
|
+
declare const ROW_RESOLVES: unique symbol;
|
|
675
767
|
//#endregion
|
|
676
768
|
//#region src/meta/field-capabilities.d.ts
|
|
677
769
|
/**
|
|
@@ -713,16 +805,37 @@ interface TFieldCapability {
|
|
|
713
805
|
* `bucketSourceVerdict`). Since 0.1.132.
|
|
714
806
|
*/
|
|
715
807
|
bucketable: boolean;
|
|
808
|
+
/**
|
|
809
|
+
* `$groupBy` on this path passes the gate: physically filterable (adapter ∧
|
|
810
|
+
* ¬writeOnly ∧ ¬encrypted) and, on a table declaring dimensions / measures,
|
|
811
|
+
* a dimension. Since 0.1.148.
|
|
812
|
+
*/
|
|
813
|
+
groupable: boolean;
|
|
814
|
+
/** Present when `groupable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
815
|
+
groupReason?: string;
|
|
716
816
|
/** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
717
817
|
bucketReason?: string;
|
|
818
|
+
/**
|
|
819
|
+
* The path may be an operand of query-time arithmetic: a numeric field
|
|
820
|
+
* ({@link numericOperandProblem}) that aggregates (`$groupBy` / aggregate
|
|
821
|
+
* capability, ¬writeOnly, visible). Only on an adapter with
|
|
822
|
+
* `supportsAggregateExpressions()`. Since 0.1.148.
|
|
823
|
+
*/
|
|
824
|
+
numeric: boolean;
|
|
718
825
|
}
|
|
719
826
|
/** One rejected path: `path` is the offending logical path, `message` the full sentence. */
|
|
720
827
|
interface TCapabilityVerdict {
|
|
721
828
|
path: string;
|
|
722
829
|
message: string;
|
|
723
830
|
}
|
|
831
|
+
/**
|
|
832
|
+
* The positions {@link FieldCapabilityIndex.check} judges: the core's path
|
|
833
|
+
* positions plus the `$select` of an aggregate query (`groupedSelect`), where
|
|
834
|
+
* a plain field must be a `$groupBy` key — a display-only decoration never is.
|
|
835
|
+
*/
|
|
836
|
+
type TGateOp = TQueryPathOp$1 | "groupedSelect";
|
|
724
837
|
/** The readable members the index reads. */
|
|
725
|
-
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "dimensions" | "measures">;
|
|
838
|
+
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "supportsAggregateExpressions" | "dimensions" | "measures">;
|
|
726
839
|
/**
|
|
727
840
|
* Capability index of one readable.
|
|
728
841
|
*
|
|
@@ -756,6 +869,8 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
756
869
|
readonly bucketUnits: readonly BucketUnit[];
|
|
757
870
|
/** Aggregate functions the adapter renders, in canonical `ALL_AGGREGATE_FNS` order (`/meta.aggregateFns`). */
|
|
758
871
|
readonly aggregateFns: readonly AggregateFn[];
|
|
872
|
+
/** Whether the adapter renders aggregate arithmetic (`/meta.aggregateExpressions`). */
|
|
873
|
+
readonly aggregateExpressions: boolean;
|
|
759
874
|
/** The adapter-level capabilities this index was built against — see {@link adapterSignature}. */
|
|
760
875
|
readonly signature: string;
|
|
761
876
|
/**
|
|
@@ -764,8 +879,19 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
764
879
|
* {@link signature} differs from this is stale. Any new adapter-level input
|
|
765
880
|
* the index reads must be added here.
|
|
766
881
|
*/
|
|
767
|
-
static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns">): string;
|
|
882
|
+
static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "supportsAggregateExpressions">): string;
|
|
883
|
+
/**
|
|
884
|
+
* The navigation paths of a readable: its `navFields`, else its relation
|
|
885
|
+
* names (partial readables list only the latter).
|
|
886
|
+
*/
|
|
887
|
+
static navPathsOf(source: Pick<TCapabilityReadable, "navFields" | "relations">): Set<string>;
|
|
768
888
|
private readonly _entries;
|
|
889
|
+
/**
|
|
890
|
+
* Declared display-only decorations (`@DbDecorations`, since 0.1.148) —
|
|
891
|
+
* virtual entries: key → the readable paths it `requires`. Selectable only;
|
|
892
|
+
* visible while every required path is.
|
|
893
|
+
*/
|
|
894
|
+
private readonly _decorations;
|
|
769
895
|
/** Nested-object parents (never listed, always selectable) → their listed leaves. */
|
|
770
896
|
private readonly _objectParents;
|
|
771
897
|
/** What `bucketSourceVerdict` reads of the table (JSON-value parents, dimensions, measures). */
|
|
@@ -774,10 +900,16 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
774
900
|
get leaves(): ReadonlyMap<string, unknown>;
|
|
775
901
|
/** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
776
902
|
get objectParents(): ReadonlyMap<string, unknown>;
|
|
777
|
-
constructor(source: TCapabilityReadable, writeOnly: ReadonlySet<string>);
|
|
903
|
+
constructor(source: TCapabilityReadable, writeOnly: ReadonlySet<string>, decorations?: ReadonlyMap<string, readonly string[]>);
|
|
778
904
|
private _buildEntry;
|
|
779
905
|
/** Listed leaves in descriptor order — the `/meta.fields` projection source. */
|
|
780
906
|
entries(): IterableIterator<[path: string, cap: TFieldCapability, fd: TDbFieldMeta]>;
|
|
907
|
+
/** The declared decoration keys, in declaration order. */
|
|
908
|
+
get decorationKeys(): IterableIterator<string>;
|
|
909
|
+
/** The capability of the declared decoration `key` (selectable only), `undefined` when `key` is none. */
|
|
910
|
+
decorationCap(key: string): Readonly<TFieldCapability> | undefined;
|
|
911
|
+
/** The decoration `key` is visible: every path it `requires` passes `exists` (the hidden-field hook). */
|
|
912
|
+
decorationVisible(key: string, exists: (path: string) => boolean): boolean;
|
|
781
913
|
/** Physical filter capability (adapter ∧ ¬writeOnly ∧ ¬encrypted) — ignores the manual-mode policy. */
|
|
782
914
|
isPhysicallyFilterable(path: string): boolean;
|
|
783
915
|
/**
|
|
@@ -800,6 +932,11 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
800
932
|
* JSON-stored column" — clients pin that wording, so do not "align" it
|
|
801
933
|
* with the core backstop's text.
|
|
802
934
|
*
|
|
935
|
+
* A declared decoration (`@DbDecorations`) is a virtual entry: `select` while
|
|
936
|
+
* every path it requires passes `exists`, any other position a display-only
|
|
937
|
+
* refusal (`groupedSelect` is a `$select` of an aggregate query), and hidden
|
|
938
|
+
* sources answer `Unknown field` like a nonexistent path.
|
|
939
|
+
*
|
|
803
940
|
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
804
941
|
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
805
942
|
*
|
|
@@ -809,10 +946,19 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
809
946
|
* name the prefixed one. A filter on a navigation path names the predicate
|
|
810
947
|
* alternative (`ticket=$some(status=…)`).
|
|
811
948
|
*/
|
|
812
|
-
check(local: string,
|
|
949
|
+
check(local: string, gateOp: TGateOp, exists: (path: string) => boolean, predicate?: TFilterPredicate, prefix?: string): TCapabilityVerdict | undefined;
|
|
813
950
|
}
|
|
814
951
|
//#endregion
|
|
815
952
|
//#region src/as-db-readable.controller.d.ts
|
|
953
|
+
/** Options of {@link AsDbReadableController.resolveQuery}. @since 0.1.149 */
|
|
954
|
+
interface TDbResolveQueryOpts<K extends string = string> {
|
|
955
|
+
/** Fields to return besides the identity. Gated like `/query` `$select` under `hasField`. */
|
|
956
|
+
select?: readonly K[];
|
|
957
|
+
/** Most rows (default 1000); `q.maxRows` can only lower it. */
|
|
958
|
+
cap?: number;
|
|
959
|
+
/** Server-side restriction, ANDed in. Trusted: not gated, not shown to `prepareRequest`. */
|
|
960
|
+
scope?: FilterExpr;
|
|
961
|
+
}
|
|
816
962
|
/** Read endpoint a {@link AsDbReadableController.decorateRows} call serves. */
|
|
817
963
|
type TDbDecorateEndpoint = "query" | "pages" | "geo" | "one";
|
|
818
964
|
/**
|
|
@@ -832,6 +978,15 @@ interface TDbDecorateContext {
|
|
|
832
978
|
projection: UniqueryControls["$select"] | undefined;
|
|
833
979
|
/** The request's parsed controls (`$select`, `$with`, `$actions`, …). Read-only by convention. */
|
|
834
980
|
controls: Record<string, unknown>;
|
|
981
|
+
/**
|
|
982
|
+
* The declared decoration keys ([`@DbDecorations`](./decorations/db-decorations.decorator.ts))
|
|
983
|
+
* this response must carry — the ones the client asked for (all of them
|
|
984
|
+
* without a `$select`) whose sources are visible and kept by
|
|
985
|
+
* `transformProjection`. Compute only these; a declared key not listed is
|
|
986
|
+
* removed from the rows after the hook. Empty when none is declared.
|
|
987
|
+
* Undeclared (`$`-prefixed) keys are not affected. Since 0.1.148.
|
|
988
|
+
*/
|
|
989
|
+
decorations: ReadonlySet<string>;
|
|
835
990
|
}
|
|
836
991
|
/**
|
|
837
992
|
* One text / vector / geo index of the bound readable with the LOGICAL field
|
|
@@ -940,6 +1095,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
940
1095
|
private readonly _derivedSource;
|
|
941
1096
|
/** `@db.writeOnly` paths of `$with` target readables, collected once per target. */
|
|
942
1097
|
private readonly _targetWriteOnly;
|
|
1098
|
+
/** Own leaf paths per readable (bound + `$with` targets), see {@link _leavesOf}. */
|
|
1099
|
+
private readonly _targetLeaves;
|
|
943
1100
|
private _indexFieldPathsCache?;
|
|
944
1101
|
/** {@link _nativeSearch} per request, keyed by the request's parsed controls. */
|
|
945
1102
|
private readonly _nativeSearchByRequest;
|
|
@@ -963,8 +1120,14 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
963
1120
|
private readonly _gateFieldsMemo;
|
|
964
1121
|
/** `true` when a subclass overrides {@link allowedActions}. */
|
|
965
1122
|
private readonly _hasAllowedActions;
|
|
1123
|
+
/** The class's validated `@DbDecorations` (since 0.1.148), `undefined` when none is declared. */
|
|
1124
|
+
private readonly _decorations;
|
|
1125
|
+
/** The decoration plumbing of {@link _decorations} — see `DecorationPlanner`. */
|
|
1126
|
+
private readonly _planner;
|
|
966
1127
|
/** `true` when a subclass overrides {@link actionRowScope} (the gate, `$actions` and `/meta/actions` apply it). */
|
|
967
1128
|
private readonly _hasActionRowScope;
|
|
1129
|
+
/** @internal `true` when a subclass overrides {@link resolveRowIds} (every id-addressed endpoint calls it; since 0.1.148). */
|
|
1130
|
+
readonly [ROW_RESOLVES]: boolean;
|
|
968
1131
|
/** `true` when the class declares `@DbActionsFrom` (since 0.1.147). */
|
|
969
1132
|
private readonly _hasDelegations;
|
|
970
1133
|
/** `transformProjection` is overridden (a delegation's id paths are checked against it). */
|
|
@@ -982,6 +1145,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
982
1145
|
*/
|
|
983
1146
|
private readonly _invertibleFields;
|
|
984
1147
|
constructor(app: Moost, readable?: AtscriptDbReadable<T>);
|
|
1148
|
+
/**
|
|
1149
|
+
* The class's `@DbDecorations`, validated against the bound readable once
|
|
1150
|
+
* per class and readable (a `[moost-db]` error when invalid); warns once
|
|
1151
|
+
* when `decorateRows` is not implemented.
|
|
1152
|
+
*/
|
|
1153
|
+
private _resolveDecorations;
|
|
985
1154
|
/**
|
|
986
1155
|
* The identifications this request may address rows through (since
|
|
987
1156
|
* 0.1.134): the readable's own, minus unique indexes over fields
|
|
@@ -1018,7 +1187,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1018
1187
|
* (`$vector`) or a geo index (`/geo`, `$index`) reading a hidden path
|
|
1019
1188
|
* answers exactly like a nonexistent index (400); a hidden DEFAULT text
|
|
1020
1189
|
* index falls back to the `@db.column.searchable` substring search over
|
|
1021
|
-
* visible fields (or ignores the term when there are none
|
|
1190
|
+
* visible fields (or ignores the term when there are none — on list
|
|
1191
|
+
* endpoints; query targets, delegated targets and {@link resolveQuery}
|
|
1192
|
+
* answer 400 `TARGET_INVALID` instead). A
|
|
1022
1193
|
* `@db.column.derived` field is visible only while its source path is,
|
|
1023
1194
|
* and one whose source is hidden is sealed out of every read projection
|
|
1024
1195
|
* for the request, like a `@db.writeOnly` field. The same holds for a
|
|
@@ -1231,34 +1402,24 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1231
1402
|
* The rows the row-level action `actionName` may run on (since 0.1.145),
|
|
1232
1403
|
* as an extra row filter; `undefined` or `{}` = no restriction (the
|
|
1233
1404
|
* default). Enforced by the action gate — the action's ids / rows are
|
|
1234
|
-
* loaded under {@link rowOverlay}, then checked against this filter
|
|
1235
|
-
*
|
|
1405
|
+
* loaded under {@link rowOverlay}, then checked against this filter (or —
|
|
1406
|
+
* without an overlay — the filter joins the load), so an id outside it gets
|
|
1407
|
+
* the same 404 "Row not found for action identifier"
|
|
1236
1408
|
* as a missing one — and reflected in `$actions` and
|
|
1237
1409
|
* `GET /meta/actions`, which list the action only on rows inside it.
|
|
1238
1410
|
*
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
* one evaluation (memoize on it with a `WeakMap`).
|
|
1252
|
-
* - Candidates are already inside the row overlay — ids that do not exist
|
|
1253
|
-
* or fall outside it never reach the hook.
|
|
1254
|
-
* - The result only restricts (`ids ∧ rowOverlay ∧ scope`); a throw fails
|
|
1255
|
-
* the request — never a silent "allow".
|
|
1256
|
-
* - The filter runs straight against the bound readable: it may use
|
|
1257
|
-
* fields {@link hasField} hides, and nothing of it (nor of
|
|
1258
|
-
* `ctx.loadRows`) reaches the response. Equal filters — the same object,
|
|
1259
|
-
* or structurally equal ones — share one id-only query.
|
|
1260
|
-
* - Runs after {@link prepareRequest} and, on the action route, after the
|
|
1261
|
-
* request body is read (it needs the ids).
|
|
1411
|
+
* The hook receives the candidate rows (`ctx`), so a scope can depend on
|
|
1412
|
+
* them — what `ctx.purpose`, `ctx.ids` and `ctx.loadRows` hold, and when the
|
|
1413
|
+
* hook is asked before any load, is documented on {@link TDbActionScopeContext}.
|
|
1414
|
+
* Called only with at least one candidate, at most once per action per
|
|
1415
|
+
* evaluation. An answer restricting nothing (`undefined`, `null`, `{}`)
|
|
1416
|
+
* makes the gate load no row at all; a restriction is folded into the one
|
|
1417
|
+
* row load. The result only restricts (`ids ∧ rowOverlay ∧ scope`); a throw
|
|
1418
|
+
* fails the request — never a silent "allow". The filter runs straight
|
|
1419
|
+
* against the bound readable: it may use fields {@link hasField} hides, and
|
|
1420
|
+
* nothing of it reaches the response. Runs after {@link prepareRequest} and,
|
|
1421
|
+
* on the action route, after the request body is read and
|
|
1422
|
+
* {@link resolveRowIds}.
|
|
1262
1423
|
*
|
|
1263
1424
|
* Not overriding it costs nothing; a one-parameter override keeps working.
|
|
1264
1425
|
* moost-db always passes `ctx` — it is optional in the signature only so
|
|
@@ -1311,6 +1472,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1311
1472
|
* {@link checkCapabilities}, {@link hasField}), where the client
|
|
1312
1473
|
* predicates' {@link transformRelationFilter} also runs: a query target
|
|
1313
1474
|
* never filters on, nor counts by, a field the caller can't read.
|
|
1475
|
+
* {@link resolveQuery} does not call it (there is no action of this
|
|
1476
|
+
* controller to scope).
|
|
1314
1477
|
*
|
|
1315
1478
|
* @since 0.1.147
|
|
1316
1479
|
*/
|
|
@@ -1320,6 +1483,18 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1320
1483
|
* May return a Promise for async lookups.
|
|
1321
1484
|
*/
|
|
1322
1485
|
protected transformProjection(projection?: UniqueryControls["$select"]): UniqueryControls["$select"] | undefined | Promise<UniqueryControls["$select"] | undefined>;
|
|
1486
|
+
/**
|
|
1487
|
+
* The shared projection step of `/query`, `/pages`, `/geo` and `/one`:
|
|
1488
|
+
* splits the declared decoration keys out of the wire `$select`
|
|
1489
|
+
* ({@link DecorationPlanner.plan}), runs {@link transformProjection} on the
|
|
1490
|
+
* rest (decoration keys never reach it — a permission layer needs no
|
|
1491
|
+
* change; their `requires` paths are added so the hook's inputs are read),
|
|
1492
|
+
* then seals every projection level. `finish` completes it once the
|
|
1493
|
+
* endpoint is past its own checks: the preferred-id widening (an
|
|
1494
|
+
* `HttpError` for a mixed `$select`) and the decoration step — what every
|
|
1495
|
+
* read endpoint then passes to {@link _runReadWithActions}.
|
|
1496
|
+
*/
|
|
1497
|
+
private _projectRead;
|
|
1323
1498
|
private widenPreferredIdProjection;
|
|
1324
1499
|
private _widenArrayProjection;
|
|
1325
1500
|
private _widenMapProjection;
|
|
@@ -1394,6 +1569,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1394
1569
|
* loaded candidates ({@link ACTION_SCOPE}).
|
|
1395
1570
|
*/
|
|
1396
1571
|
[ACTION_OVERLAY](): Promise<FilterExpr | undefined>;
|
|
1572
|
+
/**
|
|
1573
|
+
* @internal {@link resolveRowIds} for an action's validated ids (since
|
|
1574
|
+
* 0.1.148): the output validated, duplicate identities collapsed, and the
|
|
1575
|
+
* ids as the client sent them kept for the error bodies and summaries.
|
|
1576
|
+
*/
|
|
1577
|
+
[ROW_RESOLVE_IDS](ids: readonly Record<string, unknown>[], ctx: TDbRowIdsContext): Promise<TAppliedIds>;
|
|
1397
1578
|
/** @internal {@link actionRowScope} for the gate's loaded candidates (since 0.1.147). */
|
|
1398
1579
|
[ACTION_SCOPE](action: string, ctx: TDbActionScopeContext): Promise<FilterExpr | undefined>;
|
|
1399
1580
|
/** @internal `true` when {@link actionRowScope} is overridden (since 0.1.147). */
|
|
@@ -1409,12 +1590,55 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1409
1590
|
* the first batch, which directly follows the snapshot.
|
|
1410
1591
|
*/
|
|
1411
1592
|
[RESOLVE_TARGET](req: TTargetRequest): Promise<TResolvedTarget>;
|
|
1593
|
+
/**
|
|
1594
|
+
* The shared resolver behind query targets and {@link resolveQuery}: the
|
|
1595
|
+
* query body validated, checked and run as a READ of this controller, then
|
|
1596
|
+
* ONE read of `select` ordered by `sort` — `filter (+ $search) ∧ overlay ∧
|
|
1597
|
+
* scope ∧ ¬exclude`, at most `cap + 1` rows. More than `cap` → 400
|
|
1598
|
+
* `TARGET_TOO_LARGE`; a count other than `expectCount` → 409
|
|
1599
|
+
* `TARGET_CHANGED`.
|
|
1600
|
+
*
|
|
1601
|
+
* Everything that depends on the read's visibility — the `$search`
|
|
1602
|
+
* fallback, the native-search memo, the un-appliable-term refusal and the
|
|
1603
|
+
* delegated identity — is computed INSIDE the read child, where the
|
|
1604
|
+
* permission layer's per-request state is the read's.
|
|
1605
|
+
*/
|
|
1606
|
+
private _resolveMatching;
|
|
1607
|
+
/**
|
|
1608
|
+
* Rows of THIS controller matching `q`, resolved as a READ of it for the
|
|
1609
|
+
* current event's caller (since 0.1.149) — from your own command, e.g. to
|
|
1610
|
+
* act on "every issue matching this search". `q` is a `GET /query` string
|
|
1611
|
+
* (`$search` / `$index` and a filter only) or a query-target envelope
|
|
1612
|
+
* `{ q, exclude?, expectCount?, maxRows? }` (no `dryRun`).
|
|
1613
|
+
*
|
|
1614
|
+
* The read runs under this controller's full read policy
|
|
1615
|
+
* ({@link prepareRequest} with `endpoint: "query"`, {@link hasField},
|
|
1616
|
+
* {@link validateControls}, the capability / index gate and the
|
|
1617
|
+
* {@link transformFilter} overlay) with the current event's identity.
|
|
1618
|
+
* Route interceptors and guards of the `query` route do not run; read
|
|
1619
|
+
* authorization belongs in {@link prepareRequest}. {@link queryTargetScope}
|
|
1620
|
+
* is not called. Hooks see no route params of the caller (only a call from
|
|
1621
|
+
* the routed event's own controller instance keeps its params); pass route-derived
|
|
1622
|
+
* restrictions as `opts.scope`. Joins the caller's open transaction.
|
|
1623
|
+
*
|
|
1624
|
+
* Rows are ordered by identity (`preferredId`, else the primary key) and
|
|
1625
|
+
* carry the identity fields plus `opts.select` (gated like `/query`
|
|
1626
|
+
* `$select`; `transformProjection` is not applied — hide fields with
|
|
1627
|
+
* {@link hasField}; decoration keys and navigation paths are refused). An
|
|
1628
|
+
* identity-less readable is ordered by `select` (each path sortable, else
|
|
1629
|
+
* `TARGET_INVALID`). More than `opts.cap` (default 1000) rows → 400
|
|
1630
|
+
* `TARGET_TOO_LARGE`; a count other than `expectCount` → 409
|
|
1631
|
+
* `TARGET_CHANGED`; a `$search` that can't be applied → 400
|
|
1632
|
+
* `TARGET_INVALID`. Must be awaited inside a running event handler.
|
|
1633
|
+
*/
|
|
1634
|
+
resolveQuery<K extends string = never>(q: TDbResolveQueryInput, opts?: TDbResolveQueryOpts<K>): Promise<Array<Pick<DataType, K & keyof DataType> & Record<string, unknown>>>;
|
|
1412
1635
|
/**
|
|
1413
1636
|
* Runs `fn` as a READ of this controller (since 0.1.147): in a child of
|
|
1414
1637
|
* the current event whose controller context is this controller's `query`
|
|
1415
1638
|
* handler, after `prepareRequest({ endpoint: "query", controls, filter })` — the
|
|
1416
1639
|
* request-scoped state a permission layer builds there (read grant, field
|
|
1417
|
-
* visibility) is the read's and stays in the child.
|
|
1640
|
+
* visibility) is the read's and stays in the child. `routeParams` (since
|
|
1641
|
+
* 0.1.149) replaces the route params the child's hooks read.
|
|
1418
1642
|
*/
|
|
1419
1643
|
private _asRead;
|
|
1420
1644
|
/**
|
|
@@ -1427,18 +1651,23 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1427
1651
|
/**
|
|
1428
1652
|
* `select` without the `sealed` paths (see {@link _sealControls}); an
|
|
1429
1653
|
* exclusion of them is forced when there is no projection, or when every
|
|
1430
|
-
* requested path was sealed.
|
|
1654
|
+
* requested path was sealed. An inclusion naming a PARENT of a sealed path
|
|
1655
|
+
* (`secret` over a write-only `secret.hash`) is replaced by the parent's
|
|
1656
|
+
* unsealed leaves, so a sealed descendant never rides along with it.
|
|
1431
1657
|
*/
|
|
1432
1658
|
private _sealSelect;
|
|
1433
|
-
/**
|
|
1434
|
-
private
|
|
1659
|
+
/** Own leaf field paths (no navigation, no ignored field) of `readable`, once per readable. */
|
|
1660
|
+
private _leavesOf;
|
|
1435
1661
|
/**
|
|
1436
1662
|
* Merges the `$search` fallback into the filter: a case-insensitive literal
|
|
1437
1663
|
* substring match OR'd across the `@db.column.searchable` fields, `$and`-combined
|
|
1438
1664
|
* with the existing filter. Applies only when native search does not serve
|
|
1439
1665
|
* the request (no native search, or — since 0.1.143 — its default index
|
|
1440
1666
|
* reads a field {@link hasField} hides) and the request isn't a vector
|
|
1441
|
-
* search (`$vector` consumes the term).
|
|
1667
|
+
* search (`$vector` consumes the term). Lenient on list endpoints: a term
|
|
1668
|
+
* nothing can apply is ignored. Resolvers (query targets, `resolveQuery`)
|
|
1669
|
+
* refuse it — a subclass override that applies the term must return a new
|
|
1670
|
+
* filter object.
|
|
1442
1671
|
*/
|
|
1443
1672
|
protected applySearchFallback(filter: FilterExpr | undefined, controls: Record<string, unknown>): FilterExpr | undefined;
|
|
1444
1673
|
/**
|
|
@@ -1473,11 +1702,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1473
1702
|
* (reach them through the parent row), a `/one` 404, or value-help
|
|
1474
1703
|
* controllers.
|
|
1475
1704
|
*
|
|
1476
|
-
*
|
|
1477
|
-
* `$actions` and `$distance`, so they can never collide with a field
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1705
|
+
* Two kinds of keys. **Undeclared** keys: name them with a `$` prefix, like
|
|
1706
|
+
* `$actions` and `$distance`, so they can never collide with a field; they
|
|
1707
|
+
* are always kept and a client cannot select them. **Declared** keys
|
|
1708
|
+
* ({@link DbDecorations}, since 0.1.148): list them with `@DbDecorations`,
|
|
1709
|
+
* and a client may name them in `$select`; compute only `ctx.decorations`,
|
|
1710
|
+
* read your inputs from the columns `requires` names, and any declared key
|
|
1711
|
+
* not in `ctx.decorations` — and every column added only for the hook — is
|
|
1712
|
+
* removed from the rows afterwards. Do not overwrite `$actions` or
|
|
1713
|
+
* `$disabledReasons`. Without `@DbDecorations`, columns the hook needs but
|
|
1714
|
+
* the client did not select must be added in {@link transformProjection} —
|
|
1715
|
+
* they are then part of the response.
|
|
1481
1716
|
*
|
|
1482
1717
|
* ```ts
|
|
1483
1718
|
* protected async decorateRows(rows: Record<string, unknown>[], ctx: TDbDecorateContext) {
|
|
@@ -1564,13 +1799,86 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1564
1799
|
* strategy to its read-method family (count vs no-count).
|
|
1565
1800
|
*/
|
|
1566
1801
|
private _runReadWithActions;
|
|
1802
|
+
/**
|
|
1803
|
+
* Maps the ids an id-addressed endpoint received to the rows' current ids
|
|
1804
|
+
* (since 0.1.148) — the seam for stale or alias ids, e.g. a natural key that
|
|
1805
|
+
* was renamed. Called once per request, after {@link prepareRequest} and
|
|
1806
|
+
* after the request's own validation, before anything reads the row, by:
|
|
1807
|
+
*
|
|
1808
|
+
* | `ctx.purpose` | endpoint | `ids` |
|
|
1809
|
+
* | --- | --- | --- |
|
|
1810
|
+
* | `"one"` | `GET /one/:id`, `GET /one?…` | one id: the path string, or the `?`-form identification object |
|
|
1811
|
+
* | `"available"` | `GET /meta/actions/:id`, `?…` (and a view asking its source) | one id, as above |
|
|
1812
|
+
* | `"remove"` | `DELETE /:id`, `DELETE /?…` (`AsDbController`) | one id, as above |
|
|
1813
|
+
* | `"action"` | an action route | the body's validated ids (one for a `'row'` action) |
|
|
1814
|
+
*
|
|
1815
|
+
* Contract:
|
|
1816
|
+
*
|
|
1817
|
+
* - Return one id per input id, index-aligned (anything else is a 500). An
|
|
1818
|
+
* id that already names a row must come back UNCHANGED — the current
|
|
1819
|
+
* holder of a key wins over an alias; so must an id you cannot resolve
|
|
1820
|
+
* (never throw for an unknown alias: a custom error is an oracle — the
|
|
1821
|
+
* endpoint then answers its normal miss).
|
|
1822
|
+
* - The output is validated (a server bug is a 500, never a client 400): a
|
|
1823
|
+
* scalar is resolved like a path scalar (primary key first, then the
|
|
1824
|
+
* visible unique keys, inside the row overlay); an object must be one of
|
|
1825
|
+
* the visible identifications ({@link idSource}); an `"action"` id must
|
|
1826
|
+
* be such an object.
|
|
1827
|
+
* - The resolved id is never trusted for access: the endpoint still reads
|
|
1828
|
+
* or deletes it under {@link rowOverlay} and the visible identifications.
|
|
1829
|
+
* `ctx.overlay` is that overlay — resolve INSIDE it when an alias could
|
|
1830
|
+
* name several rows, so a row the caller can't reach never shadows one
|
|
1831
|
+
* they can. Don't log or return the canonical id in errors.
|
|
1832
|
+
* - Handlers (`@DbActionID()`, `useDbActionId()`), {@link rowOverlay} reads,
|
|
1833
|
+
* {@link actionRowScope} and `onRemove` / `guardRemove` receive the
|
|
1834
|
+
* resolved ids; error bodies and `summary()` echo the ids the client sent.
|
|
1835
|
+
* - Write bodies (`POST` / `PUT` / `PATCH`) are not resolved — use
|
|
1836
|
+
* `onWrite`. Not called by value-help controllers, `$actions` on a read
|
|
1837
|
+
* or a query target. Not overriding it costs nothing.
|
|
1838
|
+
*
|
|
1839
|
+
* It is NOT overridden by {@link resolveRowFilter}, which does not take part
|
|
1840
|
+
* in `/one` for real tables and views.
|
|
1841
|
+
*
|
|
1842
|
+
* ```ts
|
|
1843
|
+
* protected async resolveRowIds(ids: readonly TDbRowIdInput[], ctx: TDbRowIdsContext) {
|
|
1844
|
+
* return Promise.all(ids.map(async (id) => {
|
|
1845
|
+
* const key = typeof id === "object" ? id.code : id
|
|
1846
|
+
* if (typeof key !== "string") return id
|
|
1847
|
+
* // the current holder of the key wins; consult the alias table on a miss
|
|
1848
|
+
* // resolve INSIDE the overlay: a row the caller cannot reach never wins
|
|
1849
|
+
* const inScope = (code: string) =>
|
|
1850
|
+
* this.readable.count({ filter: ctx.overlay ? { $and: [{ code }, ctx.overlay] } : { code } })
|
|
1851
|
+
* if (await inScope(key)) return id
|
|
1852
|
+
* const alias = await aliases.findOne({ filter: { oldCode: key } })
|
|
1853
|
+
* if (!alias || !(await inScope(alias.newCode))) return id
|
|
1854
|
+
* return typeof id === "object" ? { code: alias.newCode } : alias.newCode
|
|
1855
|
+
* }))
|
|
1856
|
+
* }
|
|
1857
|
+
* ```
|
|
1858
|
+
*
|
|
1859
|
+
* @since 0.1.148
|
|
1860
|
+
*/
|
|
1861
|
+
protected resolveRowIds(ids: readonly TDbRowIdInput[], _ctx: TDbRowIdsContext): readonly TDbRowIdInput[] | Promise<readonly TDbRowIdInput[]>;
|
|
1862
|
+
/** {@link resolveRowIds} with its output validated (a server bug is a 500). */
|
|
1863
|
+
private _runResolveRowIds;
|
|
1864
|
+
/**
|
|
1865
|
+
* @internal The one id of an id-addressed endpoint through
|
|
1866
|
+
* {@link resolveRowIds} (identity, at no cost, when it is not overridden)
|
|
1867
|
+
* and the row overlay it was resolved inside — computed once, for the
|
|
1868
|
+
* endpoint's read or delete under that overlay.
|
|
1869
|
+
*/
|
|
1870
|
+
protected _resolveWithOverlay(id: TDbRowIdInput, purpose: Exclude<TDbRowIdPurpose, "action">): Promise<{
|
|
1871
|
+
id: TDbRowIdInput;
|
|
1872
|
+
overlay: FilterExpr | undefined;
|
|
1873
|
+
}>;
|
|
1567
1874
|
/**
|
|
1568
1875
|
* The filter addressing exactly the ONE row `id` means — the readable's
|
|
1569
1876
|
* PK-first `resolveRowFilter` (since 0.1.143) under this request's
|
|
1570
1877
|
* identifications (`_idOpts`). `scope` (the row overlay) restricts which
|
|
1571
1878
|
* rows count while the id is pinned, so a row outside it never shadows one
|
|
1572
1879
|
* inside it. Readables without it (partial mocks) fall back to
|
|
1573
|
-
* `resolveIdFilter`.
|
|
1880
|
+
* `resolveIdFilter`. Not an alias seam: `/one` reads through `findOneByRow`
|
|
1881
|
+
* on every real table or view — map stale ids in {@link resolveRowIds}.
|
|
1574
1882
|
*/
|
|
1575
1883
|
protected resolveRowFilter(id: unknown, scope?: FilterExpr): Promise<FilterExpr | null>;
|
|
1576
1884
|
/**
|
|
@@ -1668,6 +1976,26 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1668
1976
|
* `/one?…` rules.
|
|
1669
1977
|
*/
|
|
1670
1978
|
availableActions(query: Record<string, string>): Promise<TDbAvailableActions$1 | HttpError>;
|
|
1979
|
+
/** Every field of every identification (primary key and unique indexes) the view addresses a row by. */
|
|
1980
|
+
private _identificationFields;
|
|
1981
|
+
/**
|
|
1982
|
+
* A delegation's source id: each source id field from the row's mapped path
|
|
1983
|
+
* of the RESOLVED `id`. A path of ANY of the view's identifications
|
|
1984
|
+
* (`consumed` — not just the one the request matched: `?id=1&code=T-OLD` names
|
|
1985
|
+
* `code` too) is NEVER taken from the raw `?` query: that value may be an
|
|
1986
|
+
* alias `resolveRowIds` rewrote, and the source would see (and answer for)
|
|
1987
|
+
* it. Paths outside the identification fall back to `fallback`'s (the raw
|
|
1988
|
+
* query's) value; `undefined` when a path has none.
|
|
1989
|
+
*/
|
|
1990
|
+
private _sourceIdOf;
|
|
1991
|
+
/**
|
|
1992
|
+
* {@link _availableActions} for a request id (`names`: only those actions):
|
|
1993
|
+
* through {@link resolveRowIds} (`"available"`) first, the row overlay
|
|
1994
|
+
* computed once for both — and the resolved id returned as an object (a
|
|
1995
|
+
* scalar is the single-field `preferredId` value) for the delegated part to
|
|
1996
|
+
* derive its source id from.
|
|
1997
|
+
*/
|
|
1998
|
+
private _availableResolved;
|
|
1671
1999
|
/**
|
|
1672
2000
|
* **POST /delegated-actions/:name** — a query target for a `@DbActionsFrom`
|
|
1673
2001
|
* action whose source action declares `queryTarget` (since 0.1.147); the
|
|
@@ -1787,7 +2115,9 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
1787
2115
|
*/
|
|
1788
2116
|
protected onWrite(action: TDbWriteAction$1, data: unknown): unknown;
|
|
1789
2117
|
/**
|
|
1790
|
-
* Intercepts delete operations.
|
|
2118
|
+
* Intercepts delete operations. Receives the id {@link resolveRowIds}
|
|
2119
|
+
* resolved (the one the request carried when it is not overridden; since
|
|
2120
|
+
* 0.1.148). Return `undefined` to abort (500 "Not
|
|
1791
2121
|
* deleted"); return an `Error` instance to respond with that error.
|
|
1792
2122
|
* Runs outside any transaction. May be async (e.g. to resolve composite
|
|
1793
2123
|
* ids from external state).
|
|
@@ -1895,8 +2225,16 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
1895
2225
|
private _deleteOrThrow;
|
|
1896
2226
|
/**
|
|
1897
2227
|
* **POST /** — inserts one or many records.
|
|
2228
|
+
*
|
|
2229
|
+
* `?$onConflict=ignore` (since 0.1.148) skips rows colliding on the primary
|
|
2230
|
+
* key or a unique index instead of answering 409. The response then is
|
|
2231
|
+
* `{ insertedId?, conflict }` for an object body and
|
|
2232
|
+
* `{ insertedCount, insertedIds, inserted, conflicts }` for an array body.
|
|
2233
|
+
* Any other `$` control on POST answers 400.
|
|
1898
2234
|
*/
|
|
1899
|
-
insert(payload: unknown): Promise<unknown>;
|
|
2235
|
+
insert(payload: unknown, url?: string): Promise<unknown>;
|
|
2236
|
+
/** The only POST control: `$onConflict` (`error` | `ignore`). Anything else `$…` → 400. */
|
|
2237
|
+
private _readOnConflict;
|
|
1900
2238
|
/**
|
|
1901
2239
|
* **PUT /** — fully replaces one or many records matched by primary key.
|
|
1902
2240
|
*
|
|
@@ -2161,6 +2499,59 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
|
|
|
2161
2499
|
private normalizeSort;
|
|
2162
2500
|
}
|
|
2163
2501
|
//#endregion
|
|
2502
|
+
//#region src/decorations/db-decorations.decorator.d.ts
|
|
2503
|
+
/** Options of {@link DbDecorations}. @since 0.1.148 */
|
|
2504
|
+
interface TDbDecorationsOpts<D = Record<string, unknown>> {
|
|
2505
|
+
/**
|
|
2506
|
+
* Per decoration key, the bound readable's field paths `decorateRows` reads
|
|
2507
|
+
* to compute it. Those paths are selected (and kept visible) automatically
|
|
2508
|
+
* and stripped from the response unless the client selected them. A
|
|
2509
|
+
* decoration that reveals a field's data MUST list it: the decoration is
|
|
2510
|
+
* then served — and listed in `/meta` — only while that field is visible.
|
|
2511
|
+
*/
|
|
2512
|
+
requires?: { [K in keyof D & string]?: readonly string[] };
|
|
2513
|
+
}
|
|
2514
|
+
/** Class metadata written by {@link DbDecorations}. */
|
|
2515
|
+
interface TDbDecorationsMeta {
|
|
2516
|
+
/** The declared interface (a plain object type, no `@db.table` / `@db.view`). */
|
|
2517
|
+
type: TAtscriptAnnotatedType;
|
|
2518
|
+
/** Decoration key → the readable's field paths it reads. */
|
|
2519
|
+
requires: Readonly<Record<string, readonly string[]>>;
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Declares display-only (decoration) fields of a table or view controller —
|
|
2523
|
+
* values `decorateRows` computes and attaches to rows (since 0.1.148).
|
|
2524
|
+
*
|
|
2525
|
+
* `type` is a plain atscript interface (no `@db.table` / `@db.view`) whose
|
|
2526
|
+
* top-level props are the decorations: their `@meta.label`, `@expect.*` and
|
|
2527
|
+
* `@ui.*` annotations travel in `/meta.decorations`, each key is listed in
|
|
2528
|
+
* `/meta.fields` with `decoration: true`, and a client may name it in
|
|
2529
|
+
* `$select`. A decoration is never filterable, sortable or groupable, and it
|
|
2530
|
+
* is not part of `/meta.type` (forms and write validation never see it).
|
|
2531
|
+
*
|
|
2532
|
+
* ```ts
|
|
2533
|
+
* @TableController(TicketTable)
|
|
2534
|
+
* @DbDecorations(TicketDecorations, { requires: { ownerName: ["ownerId"] } })
|
|
2535
|
+
* export class TicketsController extends AsDbController<typeof TicketTable> {
|
|
2536
|
+
* protected async decorateRows(rows: Record<string, unknown>[], ctx: TDbDecorateContext) {
|
|
2537
|
+
* if (ctx.decorations.has("ownerName")) {
|
|
2538
|
+
* // read rows[i].ownerId, set rows[i].ownerName
|
|
2539
|
+
* }
|
|
2540
|
+
* }
|
|
2541
|
+
* }
|
|
2542
|
+
* ```
|
|
2543
|
+
*
|
|
2544
|
+
* Validated once per class at first use (a `[moost-db]` error): the type is an
|
|
2545
|
+
* object interface; keys are top-level identifiers that collide with no field
|
|
2546
|
+
* or relation of the readable; every `requires` path is an own, readable
|
|
2547
|
+
* (not `@db.writeOnly`) field or a parent object of own fields (on SQL a nested
|
|
2548
|
+
* object is flattened to leaf columns; the hook still gets the whole object). Inherited under `@Inherit()`. Not supported on
|
|
2549
|
+
* value-help controllers.
|
|
2550
|
+
*
|
|
2551
|
+
* @since 0.1.148
|
|
2552
|
+
*/
|
|
2553
|
+
declare function DbDecorations<D extends TAtscriptAnnotatedType>(type: D, opts?: TDbDecorationsOpts<TAtscriptDataType<D>>): ClassDecorator;
|
|
2554
|
+
//#endregion
|
|
2164
2555
|
//#region src/actions/keys.d.ts
|
|
2165
2556
|
type TDbActionRowMarker = true;
|
|
2166
2557
|
/** Stamped by `@InputForm(FormType)` — the compiled `.as` class + the wire name (`FormType.name`). */
|
|
@@ -2201,6 +2592,7 @@ declare module "moost" {
|
|
|
2201
2592
|
atscript_db_action_rows?: TDbActionRowMarker;
|
|
2202
2593
|
atscript_db_endpoint?: TDbRequestEndpoint;
|
|
2203
2594
|
atscript_db_actions_from?: TDbActionsFromMeta[];
|
|
2595
|
+
atscript_db_decorations?: TDbDecorationsMeta;
|
|
2204
2596
|
}
|
|
2205
2597
|
interface TMoostParamsMetadata {
|
|
2206
2598
|
atscript_db_action_param?: TDbActionParamKind;
|
|
@@ -2262,6 +2654,8 @@ interface AtscriptDbMeta {
|
|
|
2262
2654
|
atscript_db_endpoint?: TDbRequestEndpoint;
|
|
2263
2655
|
/** Class-level — written by `@DbActionsFrom(...)` (since 0.1.147). Decorators accumulate. */
|
|
2264
2656
|
atscript_db_actions_from?: TDbActionsFromMeta[];
|
|
2657
|
+
/** Class-level — written by `@DbDecorations(...)` (since 0.1.148). */
|
|
2658
|
+
atscript_db_decorations?: TDbDecorationsMeta;
|
|
2265
2659
|
}
|
|
2266
2660
|
/**
|
|
2267
2661
|
* Param-level metadata written by `@atscript/moost-db`'s param
|
|
@@ -3094,4 +3488,4 @@ declare const REL_FILTER_CLIENT_MAX_DEPTH = 3;
|
|
|
3094
3488
|
*/
|
|
3095
3489
|
declare const REL_FILTER_CLIENT_MAX_NODES = 8;
|
|
3096
3490
|
//#endregion
|
|
3097
|
-
export { ActionDisabledError, type ActionDisabledErrorBody, ActionTargetError, type ActionTargetErrorBody, 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, DbActionTarget, DbActions, DbActionsFrom, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, REL_FILTER_CLIENT_MAX_DEPTH, REL_FILTER_CLIENT_MAX_NODES, ReadableController, TABLE_DEF, type TActionTargetErrorCode, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionEnvelope, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionQueryTarget, type TDbActionScopeContext, type TDbActionScopePurpose, type TDbActionTarget, type TDbActionTargetSummary, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbActionsFromMeta, type TDbActionsFromOpts, type TDbAvailableActions, type TDbClassActionMeta, TDbControlsType, TDbDecorateContext, TDbDecorateEndpoint, TDbFieldVisibility, TDbIndexFieldPaths, TDbParsedRequest, type TDbQueryTargetOpts, 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, discoverRowLevelActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, getDbEndpoint, hasActionDelegations, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, unknownRelationError, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, useDbActionTarget, validationErrorTransform };
|
|
3491
|
+
export { ActionDisabledError, type ActionDisabledErrorBody, ActionTargetError, type ActionTargetErrorBody, 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, DbActionTarget, DbActions, DbActionsFrom, DbDecorations, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, REL_FILTER_CLIENT_MAX_DEPTH, REL_FILTER_CLIENT_MAX_NODES, ReadableController, TABLE_DEF, type TActionTargetErrorCode, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionEnvelope, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionQueryTarget, type TDbActionScopeContext, type TDbActionScopePurpose, type TDbActionTarget, type TDbActionTargetSummary, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbActionsFromMeta, type TDbActionsFromOpts, type TDbAvailableActions, type TDbClassActionMeta, TDbControlsType, TDbDecorateContext, TDbDecorateEndpoint, type TDbDecorationsOpts, TDbFieldVisibility, TDbIndexFieldPaths, TDbParsedRequest, type TDbQueryTargetOpts, type TDbRemoveGuardContext, TDbRequestContext, TDbRequestEndpoint, type TDbResolveQueryInput, TDbResolveQueryOpts, type TDbRowIdInput, type TDbRowIdPurpose, type TDbRowIdsContext, type TDbWriteAction, type TDbWriteCheckContext, type TDbWriteGuardContext, type TFieldCapability, type TGateOp, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, VALUE_HELP_CRUD_HANDLERS, ValueHelpQuery, ValueHelpSelect, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, closeDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, discoverRowLevelActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, getDbEndpoint, hasActionDelegations, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, unknownRelationError, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, useDbActionTarget, validationErrorTransform };
|