@atscript/moost-db 0.1.147 → 0.1.148

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.cts 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-CWpYwZ4R.cjs";
1
+ import { a as resolveDbSpace, i as provideDbSpace, n as clearDbSpaces, r as closeDbSpaces, t as DEFAULT_DB_SPACE } from "./db-space-registry-fXB8LnGe.cjs";
2
2
  import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
3
3
  import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, TCrudOp, TCrudOp as TCrudOp$1, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbActionTargetSummary, TDbActionTargetSummary as TDbActionTargetSummary$1, TDbAvailableActions, TDbAvailableActions as TDbAvailableActions$1, TDbFieldMeta, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteCheckContext, TDbWriteCheckContext as TDbWriteCheckContext$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
4
4
  import { HttpError } from "@moostjs/event-http";
@@ -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.
@@ -589,6 +597,45 @@ type EntryRequiredFields<E, TRow> = E extends {
589
597
  } ? R extends readonly FlatKey<TRow>[] ? R : [] : [];
590
598
  type ValidatedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntry<TRow, EntryRequiredFields<D[K], TRow>> };
591
599
  type ValidatedUnpinnedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntryUnpinned<TRow, EntryRequiredFields<D[K], TRow>> };
600
+ /**
601
+ * An id as an id-addressed endpoint received it: a path scalar (always a
602
+ * string from the URL) or an identification object (`?field=value` forms,
603
+ * action bodies).
604
+ *
605
+ * @since 0.1.148
606
+ */
607
+ type TDbRowIdInput = string | number | boolean | Record<string, unknown>;
608
+ /**
609
+ * Which endpoint asks `resolveRowIds`:
610
+ *
611
+ * - `"action"` — an action route: the body's validated ids;
612
+ * - `"available"` — `GET /meta/actions/:id` | `?…` (and a view asking its source);
613
+ * - `"one"` — `GET /one/:id` | `?…`;
614
+ * - `"remove"` — `DELETE /:id` | `?…`.
615
+ *
616
+ * @since 0.1.148
617
+ */
618
+ type TDbRowIdPurpose = "action" | "available" | "one" | "remove";
619
+ /**
620
+ * Context of `AsDbReadableController.resolveRowIds`.
621
+ *
622
+ * @since 0.1.148
623
+ */
624
+ interface TDbRowIdsContext {
625
+ readonly purpose: TDbRowIdPurpose;
626
+ /** `"action"` only: the action's name. */
627
+ readonly action?: string;
628
+ /** `"action"` only: the action's level. */
629
+ readonly level?: "row" | "rows";
630
+ /**
631
+ * The row overlay the endpoint will apply to the resolved id
632
+ * (`rowOverlay()`; for actions the gate's overlay), `undefined` when none.
633
+ * Server data — never sent to the client. Resolve INSIDE it when an alias
634
+ * could name several rows, so a row the caller can't reach never shadows
635
+ * one they can.
636
+ */
637
+ readonly overlay?: FilterExpr;
638
+ }
592
639
  //#endregion
593
640
  //#region src/actions/discover.d.ts
594
641
  /**
@@ -622,6 +669,24 @@ declare const ALLOWED_ACTIONS: unique symbol;
622
669
  /** `true` when the controller class declares `@DbActionsFrom` (inherited included). */
623
670
  declare function hasActionDelegations(ctor: Function): boolean;
624
671
  //#endregion
672
+ //#region src/actions/rows-by-id.d.ts
673
+ /** One id the client sent, with the {@link identityKey} of the id it resolved to. */
674
+ interface TRequestedId {
675
+ id: Record<string, unknown>;
676
+ key: string;
677
+ }
678
+ /**
679
+ * The result of a `resolveRowIds` call over an action's ids: the resolved
680
+ * ids with duplicate identities collapsed to the first (what handlers and
681
+ * the row load see) and — only when an id changed or collapsed — EVERY
682
+ * request id in request order (`requests`), the single model all refusals,
683
+ * reasons, summaries and counts are judged and reported in.
684
+ */
685
+ interface TAppliedIds {
686
+ ids: Record<string, unknown>[];
687
+ requests?: readonly TRequestedId[];
688
+ }
689
+ //#endregion
625
690
  //#region src/actions/scope-context.d.ts
626
691
  /**
627
692
  * Which surface asks {@link TDbActionScopeContext} — see
@@ -636,17 +701,30 @@ declare function hasActionDelegations(ctor: Function): boolean;
636
701
  type TDbActionScopePurpose = "execute" | "rows" | "available";
637
702
  /**
638
703
  * 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.
704
+ * (since 0.1.147). Every candidate is inside the controller's row overlay;
705
+ * the hook's result restricts them further.
706
+ *
707
+ * Exception (since 0.1.148): at `purpose: "execute"` with NO row overlay, the
708
+ * hook is asked BEFORE the rows are loaded when the request's ids are in
709
+ * `preferredId` shape — `ids` are then the request's (resolved) ids, deduped,
710
+ * and may name rows that do not exist (`loadRows` omits them). A restriction
711
+ * it returns joins the single row load (`id ∧ scope`); an answer of
712
+ * `undefined` / `null` / `{}` makes the gate load nothing.
641
713
  *
642
714
  * @since 0.1.147
643
715
  */
644
716
  interface TDbActionScopeContext {
645
- /** Which surface asks — the action gate, `$actions` on a read, or `GET /meta/actions`. */
717
+ /**
718
+ * Which surface asks: `"execute"` — the action gate (≤ `maxIds` ids; one
719
+ * batch of a query target); `"rows"` — `$actions` on a read (also a view's
720
+ * delegated verdicts), the read's rows; `"available"` — `GET /meta/actions/:id`,
721
+ * the one row.
722
+ */
646
723
  readonly purpose: TDbActionScopePurpose;
647
724
  /**
648
725
  * The candidates' identities (`preferredId`-shaped), deduped and never
649
- * empty. ONE array object per evaluation, shared by every action of it —
726
+ * empty — at `"execute"` without a row overlay, the request's ids (they may
727
+ * not exist; see above). ONE array object per evaluation, shared by every action of it —
650
728
  * memoize on it (`WeakMap`) when several actions derive the same filter.
651
729
  */
652
730
  readonly ids: readonly Record<string, unknown>[];
@@ -672,6 +750,10 @@ declare const ACTION_OVERLAY: unique symbol;
672
750
  declare const ACTION_SCOPE: unique symbol;
673
751
  /** `true` when the controller overrides `actionRowScope` (since 0.1.147). */
674
752
  declare const ACTION_SCOPED: unique symbol;
753
+ /** The controller's internal `resolveRowIds` call for an action's ids (since 0.1.148). */
754
+ declare const ROW_RESOLVE_IDS: unique symbol;
755
+ /** `true` when the controller overrides `resolveRowIds` (since 0.1.148). */
756
+ declare const ROW_RESOLVES: unique symbol;
675
757
  //#endregion
676
758
  //#region src/meta/field-capabilities.d.ts
677
759
  /**
@@ -713,16 +795,37 @@ interface TFieldCapability {
713
795
  * `bucketSourceVerdict`). Since 0.1.132.
714
796
  */
715
797
  bucketable: boolean;
798
+ /**
799
+ * `$groupBy` on this path passes the gate: physically filterable (adapter ∧
800
+ * ¬writeOnly ∧ ¬encrypted) and, on a table declaring dimensions / measures,
801
+ * a dimension. Since 0.1.148.
802
+ */
803
+ groupable: boolean;
804
+ /** Present when `groupable` is `false` — the reason clause appended to the HTTP 400 message. */
805
+ groupReason?: string;
716
806
  /** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
717
807
  bucketReason?: string;
808
+ /**
809
+ * The path may be an operand of query-time arithmetic: a numeric field
810
+ * ({@link numericOperandProblem}) that aggregates (`$groupBy` / aggregate
811
+ * capability, ¬writeOnly, visible). Only on an adapter with
812
+ * `supportsAggregateExpressions()`. Since 0.1.148.
813
+ */
814
+ numeric: boolean;
718
815
  }
719
816
  /** One rejected path: `path` is the offending logical path, `message` the full sentence. */
720
817
  interface TCapabilityVerdict {
721
818
  path: string;
722
819
  message: string;
723
820
  }
821
+ /**
822
+ * The positions {@link FieldCapabilityIndex.check} judges: the core's path
823
+ * positions plus the `$select` of an aggregate query (`groupedSelect`), where
824
+ * a plain field must be a `$groupBy` key — a display-only decoration never is.
825
+ */
826
+ type TGateOp = TQueryPathOp$1 | "groupedSelect";
724
827
  /** The readable members the index reads. */
725
- type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "dimensions" | "measures">;
828
+ type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "supportsAggregateExpressions" | "dimensions" | "measures">;
726
829
  /**
727
830
  * Capability index of one readable.
728
831
  *
@@ -756,6 +859,8 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
756
859
  readonly bucketUnits: readonly BucketUnit[];
757
860
  /** Aggregate functions the adapter renders, in canonical `ALL_AGGREGATE_FNS` order (`/meta.aggregateFns`). */
758
861
  readonly aggregateFns: readonly AggregateFn[];
862
+ /** Whether the adapter renders aggregate arithmetic (`/meta.aggregateExpressions`). */
863
+ readonly aggregateExpressions: boolean;
759
864
  /** The adapter-level capabilities this index was built against — see {@link adapterSignature}. */
760
865
  readonly signature: string;
761
866
  /**
@@ -764,8 +869,19 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
764
869
  * {@link signature} differs from this is stale. Any new adapter-level input
765
870
  * the index reads must be added here.
766
871
  */
767
- static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns">): string;
872
+ static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits" | "aggregateFns" | "supportsAggregateExpressions">): string;
873
+ /**
874
+ * The navigation paths of a readable: its `navFields`, else its relation
875
+ * names (partial readables list only the latter).
876
+ */
877
+ static navPathsOf(source: Pick<TCapabilityReadable, "navFields" | "relations">): Set<string>;
768
878
  private readonly _entries;
879
+ /**
880
+ * Declared display-only decorations (`@DbDecorations`, since 0.1.148) —
881
+ * virtual entries: key → the readable paths it `requires`. Selectable only;
882
+ * visible while every required path is.
883
+ */
884
+ private readonly _decorations;
769
885
  /** Nested-object parents (never listed, always selectable) → their listed leaves. */
770
886
  private readonly _objectParents;
771
887
  /** What `bucketSourceVerdict` reads of the table (JSON-value parents, dimensions, measures). */
@@ -774,10 +890,16 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
774
890
  get leaves(): ReadonlyMap<string, unknown>;
775
891
  /** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
776
892
  get objectParents(): ReadonlyMap<string, unknown>;
777
- constructor(source: TCapabilityReadable, writeOnly: ReadonlySet<string>);
893
+ constructor(source: TCapabilityReadable, writeOnly: ReadonlySet<string>, decorations?: ReadonlyMap<string, readonly string[]>);
778
894
  private _buildEntry;
779
895
  /** Listed leaves in descriptor order — the `/meta.fields` projection source. */
780
896
  entries(): IterableIterator<[path: string, cap: TFieldCapability, fd: TDbFieldMeta]>;
897
+ /** The declared decoration keys, in declaration order. */
898
+ get decorationKeys(): IterableIterator<string>;
899
+ /** The capability of the declared decoration `key` (selectable only), `undefined` when `key` is none. */
900
+ decorationCap(key: string): Readonly<TFieldCapability> | undefined;
901
+ /** The decoration `key` is visible: every path it `requires` passes `exists` (the hidden-field hook). */
902
+ decorationVisible(key: string, exists: (path: string) => boolean): boolean;
781
903
  /** Physical filter capability (adapter ∧ ¬writeOnly ∧ ¬encrypted) — ignores the manual-mode policy. */
782
904
  isPhysicallyFilterable(path: string): boolean;
783
905
  /**
@@ -800,6 +922,11 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
800
922
  * JSON-stored column" — clients pin that wording, so do not "align" it
801
923
  * with the core backstop's text.
802
924
  *
925
+ * A declared decoration (`@DbDecorations`) is a virtual entry: `select` while
926
+ * every path it requires passes `exists`, any other position a display-only
927
+ * refusal (`groupedSelect` is a `$select` of an aggregate query), and hidden
928
+ * sources answer `Unknown field` like a nonexistent path.
929
+ *
803
930
  * `predicate` is a filter entry's class (`collectQueryPaths` records it per
804
931
  * occurrence); it only matters for `op === "filter"` on a listed leaf.
805
932
  *
@@ -809,7 +936,7 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
809
936
  * name the prefixed one. A filter on a navigation path names the predicate
810
937
  * alternative (`ticket=$some(status=…)`).
811
938
  */
812
- check(local: string, op: TQueryPathOp$1, exists: (path: string) => boolean, predicate?: TFilterPredicate, prefix?: string): TCapabilityVerdict | undefined;
939
+ check(local: string, gateOp: TGateOp, exists: (path: string) => boolean, predicate?: TFilterPredicate, prefix?: string): TCapabilityVerdict | undefined;
813
940
  }
814
941
  //#endregion
815
942
  //#region src/as-db-readable.controller.d.ts
@@ -832,6 +959,15 @@ interface TDbDecorateContext {
832
959
  projection: UniqueryControls["$select"] | undefined;
833
960
  /** The request's parsed controls (`$select`, `$with`, `$actions`, …). Read-only by convention. */
834
961
  controls: Record<string, unknown>;
962
+ /**
963
+ * The declared decoration keys ([`@DbDecorations`](./decorations/db-decorations.decorator.ts))
964
+ * this response must carry — the ones the client asked for (all of them
965
+ * without a `$select`) whose sources are visible and kept by
966
+ * `transformProjection`. Compute only these; a declared key not listed is
967
+ * removed from the rows after the hook. Empty when none is declared.
968
+ * Undeclared (`$`-prefixed) keys are not affected. Since 0.1.148.
969
+ */
970
+ decorations: ReadonlySet<string>;
835
971
  }
836
972
  /**
837
973
  * One text / vector / geo index of the bound readable with the LOGICAL field
@@ -940,6 +1076,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
940
1076
  private readonly _derivedSource;
941
1077
  /** `@db.writeOnly` paths of `$with` target readables, collected once per target. */
942
1078
  private readonly _targetWriteOnly;
1079
+ /** Own leaf paths per readable (bound + `$with` targets), see {@link _leavesOf}. */
1080
+ private readonly _targetLeaves;
943
1081
  private _indexFieldPathsCache?;
944
1082
  /** {@link _nativeSearch} per request, keyed by the request's parsed controls. */
945
1083
  private readonly _nativeSearchByRequest;
@@ -963,8 +1101,14 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
963
1101
  private readonly _gateFieldsMemo;
964
1102
  /** `true` when a subclass overrides {@link allowedActions}. */
965
1103
  private readonly _hasAllowedActions;
1104
+ /** The class's validated `@DbDecorations` (since 0.1.148), `undefined` when none is declared. */
1105
+ private readonly _decorations;
1106
+ /** The decoration plumbing of {@link _decorations} — see `DecorationPlanner`. */
1107
+ private readonly _planner;
966
1108
  /** `true` when a subclass overrides {@link actionRowScope} (the gate, `$actions` and `/meta/actions` apply it). */
967
1109
  private readonly _hasActionRowScope;
1110
+ /** @internal `true` when a subclass overrides {@link resolveRowIds} (every id-addressed endpoint calls it; since 0.1.148). */
1111
+ readonly [ROW_RESOLVES]: boolean;
968
1112
  /** `true` when the class declares `@DbActionsFrom` (since 0.1.147). */
969
1113
  private readonly _hasDelegations;
970
1114
  /** `transformProjection` is overridden (a delegation's id paths are checked against it). */
@@ -982,6 +1126,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
982
1126
  */
983
1127
  private readonly _invertibleFields;
984
1128
  constructor(app: Moost, readable?: AtscriptDbReadable<T>);
1129
+ /**
1130
+ * The class's `@DbDecorations`, validated against the bound readable once
1131
+ * per class and readable (a `[moost-db]` error when invalid); warns once
1132
+ * when `decorateRows` is not implemented.
1133
+ */
1134
+ private _resolveDecorations;
985
1135
  /**
986
1136
  * The identifications this request may address rows through (since
987
1137
  * 0.1.134): the readable's own, minus unique indexes over fields
@@ -1231,34 +1381,24 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1231
1381
  * The rows the row-level action `actionName` may run on (since 0.1.145),
1232
1382
  * as an extra row filter; `undefined` or `{}` = no restriction (the
1233
1383
  * 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"
1384
+ * loaded under {@link rowOverlay}, then checked against this filter (or —
1385
+ * without an overlay — the filter joins the load), so an id outside it gets
1386
+ * the same 404 "Row not found for action identifier"
1236
1387
  * as a missing one — and reflected in `$actions` and
1237
1388
  * `GET /meta/actions`, which list the action only on rows inside it.
1238
1389
  *
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).
1390
+ * The hook receives the candidate rows (`ctx`), so a scope can depend on
1391
+ * them — what `ctx.purpose`, `ctx.ids` and `ctx.loadRows` hold, and when the
1392
+ * hook is asked before any load, is documented on {@link TDbActionScopeContext}.
1393
+ * Called only with at least one candidate, at most once per action per
1394
+ * evaluation. An answer restricting nothing (`undefined`, `null`, `{}`)
1395
+ * makes the gate load no row at all; a restriction is folded into the one
1396
+ * row load. The result only restricts (`ids ∧ rowOverlay ∧ scope`); a throw
1397
+ * fails the request — never a silent "allow". The filter runs straight
1398
+ * against the bound readable: it may use fields {@link hasField} hides, and
1399
+ * nothing of it reaches the response. Runs after {@link prepareRequest} and,
1400
+ * on the action route, after the request body is read and
1401
+ * {@link resolveRowIds}.
1262
1402
  *
1263
1403
  * Not overriding it costs nothing; a one-parameter override keeps working.
1264
1404
  * moost-db always passes `ctx` — it is optional in the signature only so
@@ -1320,6 +1460,18 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1320
1460
  * May return a Promise for async lookups.
1321
1461
  */
1322
1462
  protected transformProjection(projection?: UniqueryControls["$select"]): UniqueryControls["$select"] | undefined | Promise<UniqueryControls["$select"] | undefined>;
1463
+ /**
1464
+ * The shared projection step of `/query`, `/pages`, `/geo` and `/one`:
1465
+ * splits the declared decoration keys out of the wire `$select`
1466
+ * ({@link DecorationPlanner.plan}), runs {@link transformProjection} on the
1467
+ * rest (decoration keys never reach it — a permission layer needs no
1468
+ * change; their `requires` paths are added so the hook's inputs are read),
1469
+ * then seals every projection level. `finish` completes it once the
1470
+ * endpoint is past its own checks: the preferred-id widening (an
1471
+ * `HttpError` for a mixed `$select`) and the decoration step — what every
1472
+ * read endpoint then passes to {@link _runReadWithActions}.
1473
+ */
1474
+ private _projectRead;
1323
1475
  private widenPreferredIdProjection;
1324
1476
  private _widenArrayProjection;
1325
1477
  private _widenMapProjection;
@@ -1394,6 +1546,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1394
1546
  * loaded candidates ({@link ACTION_SCOPE}).
1395
1547
  */
1396
1548
  [ACTION_OVERLAY](): Promise<FilterExpr | undefined>;
1549
+ /**
1550
+ * @internal {@link resolveRowIds} for an action's validated ids (since
1551
+ * 0.1.148): the output validated, duplicate identities collapsed, and the
1552
+ * ids as the client sent them kept for the error bodies and summaries.
1553
+ */
1554
+ [ROW_RESOLVE_IDS](ids: readonly Record<string, unknown>[], ctx: TDbRowIdsContext): Promise<TAppliedIds>;
1397
1555
  /** @internal {@link actionRowScope} for the gate's loaded candidates (since 0.1.147). */
1398
1556
  [ACTION_SCOPE](action: string, ctx: TDbActionScopeContext): Promise<FilterExpr | undefined>;
1399
1557
  /** @internal `true` when {@link actionRowScope} is overridden (since 0.1.147). */
@@ -1427,11 +1585,13 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1427
1585
  /**
1428
1586
  * `select` without the `sealed` paths (see {@link _sealControls}); an
1429
1587
  * exclusion of them is forced when there is no projection, or when every
1430
- * requested path was sealed.
1588
+ * requested path was sealed. An inclusion naming a PARENT of a sealed path
1589
+ * (`secret` over a write-only `secret.hash`) is replaced by the parent's
1590
+ * unsealed leaves, so a sealed descendant never rides along with it.
1431
1591
  */
1432
1592
  private _sealSelect;
1433
- /** First `@db.writeOnly` field referenced by `$groupBy` / aggregate `$select`, or undefined. */
1434
- private _findWriteOnlyInAggregate;
1593
+ /** Own leaf field paths (no navigation, no ignored field) of `readable`, once per readable. */
1594
+ private _leavesOf;
1435
1595
  /**
1436
1596
  * Merges the `$search` fallback into the filter: a case-insensitive literal
1437
1597
  * substring match OR'd across the `@db.column.searchable` fields, `$and`-combined
@@ -1473,11 +1633,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1473
1633
  * (reach them through the parent row), a `/one` 404, or value-help
1474
1634
  * controllers.
1475
1635
  *
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.
1636
+ * Two kinds of keys. **Undeclared** keys: name them with a `$` prefix, like
1637
+ * `$actions` and `$distance`, so they can never collide with a field; they
1638
+ * are always kept and a client cannot select them. **Declared** keys
1639
+ * ({@link DbDecorations}, since 0.1.148): list them with `@DbDecorations`,
1640
+ * and a client may name them in `$select`; compute only `ctx.decorations`,
1641
+ * read your inputs from the columns `requires` names, and any declared key
1642
+ * not in `ctx.decorations` — and every column added only for the hook — is
1643
+ * removed from the rows afterwards. Do not overwrite `$actions` or
1644
+ * `$disabledReasons`. Without `@DbDecorations`, columns the hook needs but
1645
+ * the client did not select must be added in {@link transformProjection} —
1646
+ * they are then part of the response.
1481
1647
  *
1482
1648
  * ```ts
1483
1649
  * protected async decorateRows(rows: Record<string, unknown>[], ctx: TDbDecorateContext) {
@@ -1564,13 +1730,86 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1564
1730
  * strategy to its read-method family (count vs no-count).
1565
1731
  */
1566
1732
  private _runReadWithActions;
1733
+ /**
1734
+ * Maps the ids an id-addressed endpoint received to the rows' current ids
1735
+ * (since 0.1.148) — the seam for stale or alias ids, e.g. a natural key that
1736
+ * was renamed. Called once per request, after {@link prepareRequest} and
1737
+ * after the request's own validation, before anything reads the row, by:
1738
+ *
1739
+ * | `ctx.purpose` | endpoint | `ids` |
1740
+ * | --- | --- | --- |
1741
+ * | `"one"` | `GET /one/:id`, `GET /one?…` | one id: the path string, or the `?`-form identification object |
1742
+ * | `"available"` | `GET /meta/actions/:id`, `?…` (and a view asking its source) | one id, as above |
1743
+ * | `"remove"` | `DELETE /:id`, `DELETE /?…` (`AsDbController`) | one id, as above |
1744
+ * | `"action"` | an action route | the body's validated ids (one for a `'row'` action) |
1745
+ *
1746
+ * Contract:
1747
+ *
1748
+ * - Return one id per input id, index-aligned (anything else is a 500). An
1749
+ * id that already names a row must come back UNCHANGED — the current
1750
+ * holder of a key wins over an alias; so must an id you cannot resolve
1751
+ * (never throw for an unknown alias: a custom error is an oracle — the
1752
+ * endpoint then answers its normal miss).
1753
+ * - The output is validated (a server bug is a 500, never a client 400): a
1754
+ * scalar is resolved like a path scalar (primary key first, then the
1755
+ * visible unique keys, inside the row overlay); an object must be one of
1756
+ * the visible identifications ({@link idSource}); an `"action"` id must
1757
+ * be such an object.
1758
+ * - The resolved id is never trusted for access: the endpoint still reads
1759
+ * or deletes it under {@link rowOverlay} and the visible identifications.
1760
+ * `ctx.overlay` is that overlay — resolve INSIDE it when an alias could
1761
+ * name several rows, so a row the caller can't reach never shadows one
1762
+ * they can. Don't log or return the canonical id in errors.
1763
+ * - Handlers (`@DbActionID()`, `useDbActionId()`), {@link rowOverlay} reads,
1764
+ * {@link actionRowScope} and `onRemove` / `guardRemove` receive the
1765
+ * resolved ids; error bodies and `summary()` echo the ids the client sent.
1766
+ * - Write bodies (`POST` / `PUT` / `PATCH`) are not resolved — use
1767
+ * `onWrite`. Not called by value-help controllers, `$actions` on a read
1768
+ * or a query target. Not overriding it costs nothing.
1769
+ *
1770
+ * It is NOT overridden by {@link resolveRowFilter}, which does not take part
1771
+ * in `/one` for real tables and views.
1772
+ *
1773
+ * ```ts
1774
+ * protected async resolveRowIds(ids: readonly TDbRowIdInput[], ctx: TDbRowIdsContext) {
1775
+ * return Promise.all(ids.map(async (id) => {
1776
+ * const key = typeof id === "object" ? id.code : id
1777
+ * if (typeof key !== "string") return id
1778
+ * // the current holder of the key wins; consult the alias table on a miss
1779
+ * // resolve INSIDE the overlay: a row the caller cannot reach never wins
1780
+ * const inScope = (code: string) =>
1781
+ * this.readable.count({ filter: ctx.overlay ? { $and: [{ code }, ctx.overlay] } : { code } })
1782
+ * if (await inScope(key)) return id
1783
+ * const alias = await aliases.findOne({ filter: { oldCode: key } })
1784
+ * if (!alias || !(await inScope(alias.newCode))) return id
1785
+ * return typeof id === "object" ? { code: alias.newCode } : alias.newCode
1786
+ * }))
1787
+ * }
1788
+ * ```
1789
+ *
1790
+ * @since 0.1.148
1791
+ */
1792
+ protected resolveRowIds(ids: readonly TDbRowIdInput[], _ctx: TDbRowIdsContext): readonly TDbRowIdInput[] | Promise<readonly TDbRowIdInput[]>;
1793
+ /** {@link resolveRowIds} with its output validated (a server bug is a 500). */
1794
+ private _runResolveRowIds;
1795
+ /**
1796
+ * @internal The one id of an id-addressed endpoint through
1797
+ * {@link resolveRowIds} (identity, at no cost, when it is not overridden)
1798
+ * and the row overlay it was resolved inside — computed once, for the
1799
+ * endpoint's read or delete under that overlay.
1800
+ */
1801
+ protected _resolveWithOverlay(id: TDbRowIdInput, purpose: Exclude<TDbRowIdPurpose, "action">): Promise<{
1802
+ id: TDbRowIdInput;
1803
+ overlay: FilterExpr | undefined;
1804
+ }>;
1567
1805
  /**
1568
1806
  * The filter addressing exactly the ONE row `id` means — the readable's
1569
1807
  * PK-first `resolveRowFilter` (since 0.1.143) under this request's
1570
1808
  * identifications (`_idOpts`). `scope` (the row overlay) restricts which
1571
1809
  * rows count while the id is pinned, so a row outside it never shadows one
1572
1810
  * inside it. Readables without it (partial mocks) fall back to
1573
- * `resolveIdFilter`.
1811
+ * `resolveIdFilter`. Not an alias seam: `/one` reads through `findOneByRow`
1812
+ * on every real table or view — map stale ids in {@link resolveRowIds}.
1574
1813
  */
1575
1814
  protected resolveRowFilter(id: unknown, scope?: FilterExpr): Promise<FilterExpr | null>;
1576
1815
  /**
@@ -1668,6 +1907,26 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
1668
1907
  * `/one?…` rules.
1669
1908
  */
1670
1909
  availableActions(query: Record<string, string>): Promise<TDbAvailableActions$1 | HttpError>;
1910
+ /** Every field of every identification (primary key and unique indexes) the view addresses a row by. */
1911
+ private _identificationFields;
1912
+ /**
1913
+ * A delegation's source id: each source id field from the row's mapped path
1914
+ * of the RESOLVED `id`. A path of ANY of the view's identifications
1915
+ * (`consumed` — not just the one the request matched: `?id=1&code=T-OLD` names
1916
+ * `code` too) is NEVER taken from the raw `?` query: that value may be an
1917
+ * alias `resolveRowIds` rewrote, and the source would see (and answer for)
1918
+ * it. Paths outside the identification fall back to `fallback`'s (the raw
1919
+ * query's) value; `undefined` when a path has none.
1920
+ */
1921
+ private _sourceIdOf;
1922
+ /**
1923
+ * {@link _availableActions} for a request id (`names`: only those actions):
1924
+ * through {@link resolveRowIds} (`"available"`) first, the row overlay
1925
+ * computed once for both — and the resolved id returned as an object (a
1926
+ * scalar is the single-field `preferredId` value) for the delegated part to
1927
+ * derive its source id from.
1928
+ */
1929
+ private _availableResolved;
1671
1930
  /**
1672
1931
  * **POST /delegated-actions/:name** — a query target for a `@DbActionsFrom`
1673
1932
  * action whose source action declares `queryTarget` (since 0.1.147); the
@@ -1787,7 +2046,9 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
1787
2046
  */
1788
2047
  protected onWrite(action: TDbWriteAction$1, data: unknown): unknown;
1789
2048
  /**
1790
- * Intercepts delete operations. Return `undefined` to abort (500 "Not
2049
+ * Intercepts delete operations. Receives the id {@link resolveRowIds}
2050
+ * resolved (the one the request carried when it is not overridden; since
2051
+ * 0.1.148). Return `undefined` to abort (500 "Not
1791
2052
  * deleted"); return an `Error` instance to respond with that error.
1792
2053
  * Runs outside any transaction. May be async (e.g. to resolve composite
1793
2054
  * ids from external state).
@@ -1895,8 +2156,16 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
1895
2156
  private _deleteOrThrow;
1896
2157
  /**
1897
2158
  * **POST /** — inserts one or many records.
2159
+ *
2160
+ * `?$onConflict=ignore` (since 0.1.148) skips rows colliding on the primary
2161
+ * key or a unique index instead of answering 409. The response then is
2162
+ * `{ insertedId?, conflict }` for an object body and
2163
+ * `{ insertedCount, insertedIds, inserted, conflicts }` for an array body.
2164
+ * Any other `$` control on POST answers 400.
1898
2165
  */
1899
- insert(payload: unknown): Promise<unknown>;
2166
+ insert(payload: unknown, url?: string): Promise<unknown>;
2167
+ /** The only POST control: `$onConflict` (`error` | `ignore`). Anything else `$…` → 400. */
2168
+ private _readOnConflict;
1900
2169
  /**
1901
2170
  * **PUT /** — fully replaces one or many records matched by primary key.
1902
2171
  *
@@ -2161,6 +2430,59 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
2161
2430
  private normalizeSort;
2162
2431
  }
2163
2432
  //#endregion
2433
+ //#region src/decorations/db-decorations.decorator.d.ts
2434
+ /** Options of {@link DbDecorations}. @since 0.1.148 */
2435
+ interface TDbDecorationsOpts<D = Record<string, unknown>> {
2436
+ /**
2437
+ * Per decoration key, the bound readable's field paths `decorateRows` reads
2438
+ * to compute it. Those paths are selected (and kept visible) automatically
2439
+ * and stripped from the response unless the client selected them. A
2440
+ * decoration that reveals a field's data MUST list it: the decoration is
2441
+ * then served — and listed in `/meta` — only while that field is visible.
2442
+ */
2443
+ requires?: { [K in keyof D & string]?: readonly string[] };
2444
+ }
2445
+ /** Class metadata written by {@link DbDecorations}. */
2446
+ interface TDbDecorationsMeta {
2447
+ /** The declared interface (a plain object type, no `@db.table` / `@db.view`). */
2448
+ type: TAtscriptAnnotatedType;
2449
+ /** Decoration key → the readable's field paths it reads. */
2450
+ requires: Readonly<Record<string, readonly string[]>>;
2451
+ }
2452
+ /**
2453
+ * Declares display-only (decoration) fields of a table or view controller —
2454
+ * values `decorateRows` computes and attaches to rows (since 0.1.148).
2455
+ *
2456
+ * `type` is a plain atscript interface (no `@db.table` / `@db.view`) whose
2457
+ * top-level props are the decorations: their `@meta.label`, `@expect.*` and
2458
+ * `@ui.*` annotations travel in `/meta.decorations`, each key is listed in
2459
+ * `/meta.fields` with `decoration: true`, and a client may name it in
2460
+ * `$select`. A decoration is never filterable, sortable or groupable, and it
2461
+ * is not part of `/meta.type` (forms and write validation never see it).
2462
+ *
2463
+ * ```ts
2464
+ * @TableController(TicketTable)
2465
+ * @DbDecorations(TicketDecorations, { requires: { ownerName: ["ownerId"] } })
2466
+ * export class TicketsController extends AsDbController<typeof TicketTable> {
2467
+ * protected async decorateRows(rows: Record<string, unknown>[], ctx: TDbDecorateContext) {
2468
+ * if (ctx.decorations.has("ownerName")) {
2469
+ * // read rows[i].ownerId, set rows[i].ownerName
2470
+ * }
2471
+ * }
2472
+ * }
2473
+ * ```
2474
+ *
2475
+ * Validated once per class at first use (a `[moost-db]` error): the type is an
2476
+ * object interface; keys are top-level identifiers that collide with no field
2477
+ * or relation of the readable; every `requires` path is an own, readable
2478
+ * (not `@db.writeOnly`) field or a parent object of own fields (on SQL a nested
2479
+ * object is flattened to leaf columns; the hook still gets the whole object). Inherited under `@Inherit()`. Not supported on
2480
+ * value-help controllers.
2481
+ *
2482
+ * @since 0.1.148
2483
+ */
2484
+ declare function DbDecorations<D extends TAtscriptAnnotatedType>(type: D, opts?: TDbDecorationsOpts<TAtscriptDataType<D>>): ClassDecorator;
2485
+ //#endregion
2164
2486
  //#region src/actions/keys.d.ts
2165
2487
  type TDbActionRowMarker = true;
2166
2488
  /** Stamped by `@InputForm(FormType)` — the compiled `.as` class + the wire name (`FormType.name`). */
@@ -2201,6 +2523,7 @@ declare module "moost" {
2201
2523
  atscript_db_action_rows?: TDbActionRowMarker;
2202
2524
  atscript_db_endpoint?: TDbRequestEndpoint;
2203
2525
  atscript_db_actions_from?: TDbActionsFromMeta[];
2526
+ atscript_db_decorations?: TDbDecorationsMeta;
2204
2527
  }
2205
2528
  interface TMoostParamsMetadata {
2206
2529
  atscript_db_action_param?: TDbActionParamKind;
@@ -2262,6 +2585,8 @@ interface AtscriptDbMeta {
2262
2585
  atscript_db_endpoint?: TDbRequestEndpoint;
2263
2586
  /** Class-level — written by `@DbActionsFrom(...)` (since 0.1.147). Decorators accumulate. */
2264
2587
  atscript_db_actions_from?: TDbActionsFromMeta[];
2588
+ /** Class-level — written by `@DbDecorations(...)` (since 0.1.148). */
2589
+ atscript_db_decorations?: TDbDecorationsMeta;
2265
2590
  }
2266
2591
  /**
2267
2592
  * Param-level metadata written by `@atscript/moost-db`'s param
@@ -3094,4 +3419,4 @@ declare const REL_FILTER_CLIENT_MAX_DEPTH = 3;
3094
3419
  */
3095
3420
  declare const REL_FILTER_CLIENT_MAX_NODES = 8;
3096
3421
  //#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 };
3422
+ 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 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 };