@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/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as resolveDbSpace, n as clearDbSpaces, r as provideDbSpace, t as DEFAULT_DB_SPACE } from "./db-space-registry-CKR6G-hi.mjs";
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` keeps only the `$`
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 already inside the controller's row
640
- * overlay; the hook's result restricts them further.
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
- /** Which surface asks — the action gate, `$actions` on a read, or `GET /meta/actions`. */
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. ONE array object per evaluation, shared by every action of it —
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, op: TQueryPathOp$1, exists: (path: string) => boolean, predicate?: TFilterPredicate, prefix?: string): TCapabilityVerdict | undefined;
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). A
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, so an
1235
- * id outside it gets the same 404 "Row not found for action identifier"
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
- * Since 0.1.147 the hook receives the candidate rows (`ctx`), so a scope
1240
- * can depend on them — e.g. derive `{ ticketKey: { $in: … } }` from a
1241
- * related table read for exactly these rows:
1242
- *
1243
- * | `ctx.purpose` | asked by | `ctx.ids` |
1244
- * | --- | --- | --- |
1245
- * | `"execute"` | the action gate | the loaded ids / rows (≤ `maxIds`; one batch of a query target) |
1246
- * | `"rows"` | `$actions` on a read (and a view's delegated verdicts) | the read's rows |
1247
- * | `"available"` | `GET /meta/actions/:id` | the one row |
1248
- *
1249
- * - Called only with at least one candidate, at most once per action per
1250
- * evaluation; `ctx.ids` is the same array object for every action of
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
- /** First `@db.writeOnly` field referenced by `$groupBy` / aggregate `$select`, or undefined. */
1434
- private _findWriteOnlyInAggregate;
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
- * Convention (not enforced): name decoration keys with a `$` prefix, like
1477
- * `$actions` and `$distance`, so they can never collide with a field name.
1478
- * Do not overwrite `$actions` or `$disabledReasons`. Columns the
1479
- * hook needs but the client did not select must be added in
1480
- * {@link transformProjection} — they are then part of the response.
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. Return `undefined` to abort (500 "Not
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 };