@atscript/moost-db 0.1.58 → 0.1.60

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
@@ -4,7 +4,7 @@ import { HttpError } from "@moostjs/event-http";
4
4
  import * as moost from "moost";
5
5
  import { Moost, TConsoleBase } from "moost";
6
6
  import * as _uniqu_url0 from "@uniqu/url";
7
- import { AtscriptDbReadable, AtscriptDbTable, FilterExpr, TCrudOp, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbFieldMeta, TMetaResponse, Uniquery, UniqueryControls } from "@atscript/db";
7
+ import { AtscriptDbReadable, AtscriptDbTable, FilterExpr, FlatOf, TCrudOp, TCrudPermissions, TCrudPermissions as TCrudPermissions$1, TDbActionInfo, TDbActionInfo as TDbActionInfo$1, TDbActionIntent, TDbActionIntent as TDbActionIntent$1, TDbActionLevel, TDbActionLevel as TDbActionLevel$1, TDbActionProcessor, TDbFieldMeta, TIdentification, TMetaResponse, Uniquery, UniqueryControls } from "@atscript/db";
8
8
  import * as _wooksjs_event_core0 from "@wooksjs/event-core";
9
9
 
10
10
  //#region src/as-readable.controller.d.ts
@@ -154,6 +154,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
154
154
  /** Reference to the underlying readable (table or view). */
155
155
  protected readable: AtscriptDbReadable<T>;
156
156
  private readonly _gates;
157
+ private readonly _preferredIdSet;
158
+ private readonly _compositeIdShapes;
159
+ private readonly _overlayIsNoOp;
157
160
  constructor(readable: AtscriptDbReadable<T>, app: Moost);
158
161
  private _buildGates;
159
162
  private _collectAnnotated;
@@ -176,6 +179,27 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
176
179
  * May return a Promise for async lookups.
177
180
  */
178
181
  protected transformProjection(projection?: UniqueryControls["$select"]): UniqueryControls["$select"] | undefined | Promise<UniqueryControls["$select"] | undefined>;
182
+ private widenPreferredIdProjection;
183
+ private _widenArrayProjection;
184
+ private _widenMapProjection;
185
+ /** WHY: the URL parser only auto-coerces `$count`; every other boolean control reaches us as `"true"`/`"1"` and would fail DTO validation. */
186
+ private _coerceActionsControl;
187
+ /** Normalize a post-`widenPreferredIdProjection` $select into `string[] | null` (`null` = all fields). */
188
+ private _resolveProjectionForAugmenter;
189
+ /** WHY: filter row/rows envelopes by the per-request `applyMetaOverlay` action set; skip `meta()` when overlay is identity. */
190
+ private _resolveAugmentEnvelopes;
191
+ /** Returns a widened `$select` only when at least one `requiredFields` entry is missing; `null` means "no widening needed". */
192
+ private _widenSelectForActions;
193
+ private _prepareAugmentation;
194
+ private _resolveReadStrategy;
195
+ /**
196
+ * Shared `query` / `pages` pipeline: prepare actions augmentation + read
197
+ * strategy in parallel, pre-widen $select for `requiredFields`, run
198
+ * `exec`, and augment `result.data` with `$actions` when the request set
199
+ * `$actions=true`. Caller dispatches the strategy to its read-method
200
+ * family (count vs no-count).
201
+ */
202
+ private _runReadWithActions;
179
203
  /**
180
204
  * Extracts a composite identifier object from query params.
181
205
  * Tries composite primary key first, then compound unique indexes.
@@ -204,6 +228,7 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
204
228
  * (composite primary key or compound unique index).
205
229
  */
206
230
  getOneComposite(query: Record<string, string>, url: string): Promise<DataType | HttpError>;
231
+ private _findByIdAndAugment;
207
232
  /**
208
233
  * **GET /meta** — returns table/view metadata for UI.
209
234
  *
@@ -464,70 +489,81 @@ declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorato
464
489
  /** `'rows'`-level batch policy — controls whether failing rows reject or are filtered out. */
465
490
  type TOnDisabledRows = "reject" | "skip";
466
491
  /**
467
- * Options accepted by `@DbAction(name, opts?)`. Structurally derived from
468
- * {@link TDbActionInfo} so every wire-shape addition propagates here, EXCEPT
469
- * `disabled` and `requiredFields` which differ in shape between decorator
470
- * opts (function / dev-supplied) and the wire (string / forwarded verbatim).
471
- *
472
- * Fields owned by the framework (`name`, `level`, `processor`, `value`) are
473
- * excluded `name` comes from the decorator argument, `level` is inferred
474
- * from `@DbActionPK*` / `@DbActionRow*` usage, `processor` is fixed to
475
- * `'backend'` for method-decorator actions, and `value` is filled from the
476
- * `@Post` path.
477
- *
478
- * Generic over `TRow` so the `disabled` predicate can be type-checked against
479
- * the bound table's row shape. Note: TS decorators cannot infer `TRow` from
480
- * the enclosing controller's class generic, so the dev MUST annotate the row
481
- * arg explicitly (`(row: Order) => …`) to get type-checking.
492
+ * Dot-notation field paths of `TRow`'s flat type. Drives both the runtime
493
+ * projection widening and the type narrowing of the `disabled` predicate's
494
+ * row argument. Relations are absent from `FlatOf<T>` listing a relation
495
+ * field is therefore a compile error.
496
+ *
497
+ * Permissive fallback when `TRow = unknown` (no explicit decorator generic):
498
+ * any string is allowed and the `disabled` predicate's row arg is `any[]`,
499
+ * preserving the prior loose typing for un-annotated call sites.
482
500
  */
483
- type DbActionOpts<TRow = unknown> = Partial<Omit<TDbActionInfo$1, "name" | "level" | "processor" | "value" | "disabled" | "requiredFields">> & {
501
+ type FlatKey<TRow> = unknown extends TRow ? string : keyof FlatOf<TRow> & string;
502
+ /** Row-shape narrowing for the `disabled` predicate. Falls back to `any` when `TRow = unknown`. */
503
+ type DisabledRowsArg<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? any[] : Pick<FlatOf<TRow>, R[number] & keyof FlatOf<TRow>>[];
504
+ interface NoGate {
505
+ requiredFields?: never;
506
+ disabled?: never;
507
+ onDisabledRows?: never;
508
+ }
509
+ interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
484
510
  /**
485
- * Per-row gate predicate. Truthy action is disabled for that row.
486
- * Server enforces (via the gate interceptor); UI evaluates the same
487
- * expression to grey-out / hide the button.
488
- *
489
- * The dev MUST annotate the row arg explicitly (`(row: Order) => …`) —
490
- * TS decorators cannot infer `TRow` from the enclosing class generic.
511
+ * Dot-notation field paths the predicate references. SERVER-INTERNAL
512
+ * never emitted on the `/meta` wire. Consumed verbatim to widen the DB
513
+ * projection so `disabled` always sees the fields it declared.
491
514
  */
492
- disabled?: (row: TRow) => boolean;
515
+ requiredFields: R;
493
516
  /**
494
- * Optional dot-notation field paths the UI should union into `$select`.
495
- * Plain `string[]` in v1.
496
- *
497
- * TODO: upgrade to typed `PathOf<TRow>[]` in a follow-up — the recursive
498
- * type pattern is finicky for nested objects/arrays/optionals and isn't
499
- * blocking v1.
517
+ * Sync batch gate predicate returns a parallel `boolean[]` aligned with
518
+ * the input. `true` = disabled for the corresponding row. The `rows`
519
+ * argument is type-narrowed to `Pick<FlatOf<TRow>, R[number]>[]`; reading
520
+ * a field not listed in `requiredFields` is a compile error.
500
521
  *
501
- * When omitted, the UI parses the stringified `disabled` itself to extract
502
- * row-property accesses. When present, the UI uses this list verbatim — the
503
- * server does NOT auto-derive or merge.
522
+ * Promise return is NOT permitted the predicate is consumed in the
523
+ * same tick by the gate and the augmenter.
504
524
  */
505
- requiredFields?: string[];
525
+ disabled?: (rows: DisabledRowsArg<TRow, R>) => boolean[];
506
526
  /**
507
527
  * `'rows'`-level batch policy. Default `'reject'`.
508
528
  *
509
- * - `'reject'`: evaluate every row (FULL scan, NOT short-circuit) before
510
- * throwing; if any row fails, the error body lists ALL failing PKs;
511
- * handler not invoked.
512
- * - `'skip'`: filter cached rows + cached PKs to passing-only;
513
- * zero survivors → reject. Handler runs against the survivors.
529
+ * - `'reject'`: evaluate every row before throwing; if any row fails, the
530
+ * error body lists ALL failing IDs; handler not invoked.
531
+ * - `'skip'`: filter cached rows + cached IDs to passing-only; zero
532
+ * survivors reject. Handler runs against the survivors.
514
533
  *
515
534
  * Ignored for `'row'` and `'table'` level actions.
516
535
  */
517
536
  onDisabledRows?: TOnDisabledRows;
537
+ }
538
+ /**
539
+ * Loose gate shape used when `TRow = unknown` (no explicit decorator generic).
540
+ * Preserves the prior un-typed call-site flexibility; the runtime still drops
541
+ * actions where `disabled` is set without `requiredFields`.
542
+ */
543
+ interface LooseGate {
544
+ requiredFields?: string[];
545
+ disabled?: (rows: any[]) => boolean[];
546
+ onDisabledRows?: TOnDisabledRows;
547
+ }
548
+ type GateOpts<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? LooseGate : NoGate | WithGate<TRow, R>;
549
+ interface BaseActionOpts extends Partial<Omit<TDbActionInfo$1, "name" | "level" | "processor" | "value" | "disabled">> {
518
550
  /**
519
551
  * Bound table reference. REQUIRED on non-`AsDbReadableController` classes
520
552
  * when `disabled` is set OR a `@DbActionRow*` parameter is declared.
521
553
  *
522
554
  * Silently ignored on `AsDbReadableController` subclasses (which include
523
- * `AsDbController`) — the bound table from the controller wins; the
524
- * gate / thin interceptor probes `instanceof AsDbReadableController` and
525
- * populates the bound-table slot from `controller.readable` before
526
- * checking `opts.table`.
555
+ * `AsDbController`) — the bound table from the controller wins.
527
556
  */
528
557
  table?: AtscriptDbTable<any>;
529
- };
530
- interface DbActionsEntryCommon {
558
+ }
559
+ /**
560
+ * Options accepted by `@DbAction(name, opts?)`. Generic over `TRow` (the
561
+ * controller's bound atscript type) and `R` (the literal `requiredFields`
562
+ * tuple). Both are inferred at the call site via the decorator's `<TRow>`
563
+ * argument plus `const R` generic.
564
+ */
565
+ type DbActionOpts<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = BaseActionOpts & GateOpts<TRow, R>;
566
+ interface DbActionsEntryCommonBase {
531
567
  label: string;
532
568
  level: TDbActionLevel$1;
533
569
  icon?: string;
@@ -539,51 +575,36 @@ interface DbActionsEntryCommon {
539
575
  promptText?: string | [string, string];
540
576
  /** Mirrors {@link TDbActionInfo.shortcut} — single-character UI hint. */
541
577
  shortcut?: string;
542
- /**
543
- * UI-only gate predicate (class-level dict entries do NOT register a
544
- * server-side gate interceptor — the dict entry's `value` may point at an
545
- * endpoint in another controller). The wire emits `fn.toString()` so the
546
- * UI can grey-out / hide the button. For symmetric server enforcement at
547
- * the actual `@Post`-bound handler, declare `@DbAction(name, { disabled })`
548
- * on that handler too.
549
- *
550
- * Not generic over `TRow` — devs type the row arg explicitly.
551
- */
552
- disabled?: (row: any) => boolean;
553
- /** Same as method-decorator `requiredFields`. UI hint, not server-derived. */
554
- requiredFields?: string[];
555
- /**
556
- * Reserved for future API symmetry with method-decorator opts. Currently a
557
- * no-op for class-level dict entries (no gate interceptor registers).
558
- */
559
- onDisabledRows?: TOnDisabledRows;
560
578
  }
579
+ type DbActionsEntryWithGate<TRow, R extends readonly FlatKey<TRow>[]> = DbActionsEntryCommonBase & GateOpts<TRow, R>;
561
580
  /**
562
581
  * Class-level dict entry. `value` semantics by processor:
563
582
  *
564
- * - `'navigate'` — REQUIRED, non-empty. The URL template (with `$1` substituted client-side).
565
- * - `'backend'` — REQUIRED, non-empty. The full HTTP POST path the UI client should invoke.
566
- * For row/rows entries the dev-supplied path MUST point to a `@Post`-bound
567
- * handler accepting the PK-shaped JSON body (single PK scalar / composite
568
- * object / array thereof) — typically a method using `@DbActionPK()` or
569
- * `@DbActionPKs()`. The meta builder does NOT validate this.
583
+ * - `'navigate'` — REQUIRED, non-empty. URL template (`$1` substituted client-side).
584
+ * - `'backend'` — REQUIRED, non-empty. Full HTTP POST path the UI client invokes.
570
585
  * - `'custom'` — `value` is forbidden in the entry; the meta builder fills it
571
586
  * with the dict key.
572
587
  */
573
- type TDbActionsEntry = (DbActionsEntryCommon & {
588
+ type TDbActionsEntry<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = (DbActionsEntryWithGate<TRow, R> & {
574
589
  processor: "navigate";
575
590
  value: string;
576
- }) | (DbActionsEntryCommon & {
591
+ }) | (DbActionsEntryWithGate<TRow, R> & {
577
592
  processor: "custom";
578
593
  value?: never;
579
- }) | (DbActionsEntryCommon & {
594
+ }) | (DbActionsEntryWithGate<TRow, R> & {
580
595
  processor: "backend";
581
596
  value: string;
582
597
  });
583
598
  /** Distributes `Omit` across the discriminated union members. */
584
599
  type DistributiveOmit<T, K extends keyof T> = T extends T ? Omit<T, K> : never;
585
600
  /** Same as {@link TDbActionsEntry} but without the `level` field — used by the level-pinned shortcuts. */
586
- type TDbActionsEntryUnpinned = DistributiveOmit<TDbActionsEntry, "level">;
601
+ type TDbActionsEntryUnpinned<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = DistributiveOmit<TDbActionsEntry<TRow, R>, "level">;
602
+ type DbActionsDictBase = Record<string, unknown>;
603
+ type EntryRequiredFields<E, TRow> = E extends {
604
+ requiredFields: infer R;
605
+ } ? R extends readonly FlatKey<TRow>[] ? R : [] : [];
606
+ type ValidatedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntry<TRow, EntryRequiredFields<D[K], TRow>> };
607
+ type ValidatedUnpinnedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntryUnpinned<TRow, EntryRequiredFields<D[K], TRow>> };
587
608
  //#endregion
588
609
  //#region src/actions/db-action.decorator.d.ts
589
610
  /**
@@ -592,8 +613,13 @@ type TDbActionsEntryUnpinned = DistributiveOmit<TDbActionsEntry, "level">;
592
613
  * (gate when `disabled` is set, thin bound-table injector when only
593
614
  * `@DbActionRow*` is present). Stacking two `@DbAction` on the same method
594
615
  * is undefined and emits a warning.
616
+ *
617
+ * Generic over `TRow` (annotate at the call site: `@DbAction<Order>(...)`)
618
+ * and `R` (the literal `requiredFields` tuple, inferred via `const R`).
619
+ * The `disabled` predicate's `rows` argument is type-narrowed to
620
+ * `Pick<FlatOf<TRow>, R[number]>[]`.
595
621
  */
596
- declare function DbAction<TRow = unknown>(name: string, opts?: DbActionOpts<TRow>): MethodDecorator;
622
+ declare function DbAction<TRow = unknown, const R extends readonly FlatKey<TRow>[] = []>(name: string, opts?: DbActionOpts<TRow, R>): MethodDecorator;
597
623
  //#endregion
598
624
  //#region src/actions/db-action-default.decorator.d.ts
599
625
  /**
@@ -602,49 +628,56 @@ declare function DbAction<TRow = unknown>(name: string, opts?: DbActionOpts<TRow
602
628
  */
603
629
  declare function DbActionDefault(): MethodDecorator;
604
630
  //#endregion
605
- //#region src/actions/db-action-pk.decorator.d.ts
631
+ //#region src/actions/db-action-id.decorator.d.ts
606
632
  /**
607
- * Parameter resolver that reads the primary key from the JSON request body
608
- * and validates it against the bound table's PK schema.
633
+ * Parameter resolver that reads a row identifier from the JSON request body
634
+ * and validates it against the bound table's legitimate identifiers.
609
635
  *
610
- * - Single-field PK JSON-encoded scalar (`"abc"`, `42`, `true`).
611
- * - Composite PK JSON object with all PK fields.
636
+ * Body shape is always a JSON object — no scalar form. The object's key set
637
+ * MUST exactly match one of the table's legitimate identifications:
612
638
  *
613
- * Validation is strict no type coercion. Mismatches throw a
639
+ * - Single-field PK `{ id: "abc" }` (or whatever the PK prop is named).
640
+ * - Composite PK → `{ tenantId: "...", userId: "..." }`.
641
+ * - Single-field unique index → `{ slug: "alpha" }`.
642
+ * - Compound unique index → `{ tenantId: "...", slug: "..." }`.
643
+ *
644
+ * Strict — unknown fields are rejected, no type coercion. Mismatches throw a
614
645
  * `ValidatorError` which the existing validation interceptor surfaces as
615
646
  * HTTP 400 with the same envelope as DTO failures.
616
647
  *
617
648
  * Marks the param so {@link discoverActions} can infer the action's `level`
618
649
  * as `'row'`.
619
650
  *
620
- * Implementation note: the resolver is a thin reader of the cached PK wook
651
+ * Implementation note: the resolver is a thin reader of the cached ID wook
621
652
  * — validation logic lives in the wook factory, which runs once per request
622
653
  * regardless of how many readers consume the value.
623
654
  */
624
- declare function DbActionPK(): ParameterDecorator;
655
+ declare function DbActionID(): ParameterDecorator;
625
656
  //#endregion
626
- //#region src/actions/db-action-pks.decorator.d.ts
657
+ //#region src/actions/db-action-ids.decorator.d.ts
627
658
  /**
628
- * Parameter resolver that reads a JSON array of primary keys from the request
629
- * body and validates each entry against the bound table's PK schema.
659
+ * Parameter resolver that reads a JSON array of row identifiers from the
660
+ * request body and validates each entry against the bound table.
630
661
  *
631
- * - Scalar PK JSON array of scalars (`["a","b","c"]`).
632
- * - Composite PK JSON array of objects.
662
+ * Body shape is always a JSON array of objects — no scalar form. Each
663
+ * element's key set MUST exactly match one of the table's legitimate
664
+ * identifications (PK or any unique index). Elements MAY mix shapes:
665
+ * `[{ id: "1" }, { slug: "alpha" }]` is valid when both `id` is the PK
666
+ * and `slug` is a unique index.
633
667
  *
634
- * Validation is strict no type coercion. Marks the param so
635
- * {@link discoverActions} can infer the action's `level` as `'rows'`.
668
+ * Strict unknown fields are rejected, no type coercion. Marks the param
669
+ * so {@link discoverActions} can infer the action's `level` as `'rows'`.
636
670
  *
637
671
  * In `'rows'` skip mode the resolved value reflects the gate interceptor's
638
- * filtered subset (the cached PK slot is overwritten in place); see
639
- * {@link dbActionPksSlot} for precedence details.
672
+ * filtered subset (the cached ID slot is overwritten in place); see
673
+ * {@link dbActionIdsSlot} for precedence details.
640
674
  */
641
- declare function DbActionPKs(): ParameterDecorator;
675
+ declare function DbActionIDs(): ParameterDecorator;
642
676
  //#endregion
643
677
  //#region src/actions/db-action-row.decorator.d.ts
644
678
  /**
645
- * Parameter decorator that injects the row whose PK was supplied in the
646
- * request body. Reads from the cached row wook (fetched once per request,
647
- * shared with the gate interceptor when `disabled` is set).
679
+ * Parameter decorator that injects the row whose identifier was supplied in
680
+ * the request body.
648
681
  *
649
682
  * Marks the param so {@link discoverActions} infers the action's `level` as
650
683
  * `'row'`. Co-occurrence with `@DbActionRows()` (or any multi-cardinality
@@ -655,8 +688,8 @@ declare function DbActionPKs(): ParameterDecorator;
655
688
  */
656
689
  declare function DbActionRow(): ParameterDecorator;
657
690
  /**
658
- * Parameter decorator that injects the rows-array fetched by primary keys
659
- * from the request body. Reads from the cached row-array wook.
691
+ * Parameter decorator that injects the rows fetched by the identifiers
692
+ * supplied in the request body.
660
693
  *
661
694
  * Marks the param so {@link discoverActions} infers the action's `level` as
662
695
  * `'rows'`. In `'rows'` + `'skip'` mode the resolved value contains only the
@@ -672,50 +705,46 @@ declare function DbActionRows(): ParameterDecorator;
672
705
  * the level-pinned shortcuts (`@DbTableActions`, `@DbRowActions`,
673
706
  * `@DbRowsActions`) to avoid repeating `level`.
674
707
  *
675
- * The dictionary key serves as the action `name`. Entries do NOT bind any
676
- * HTTP route the meta builder surfaces them in `/meta` only. For
677
- * `processor: 'backend'`, the dev-supplied `value` MUST point to a real
678
- * `@Post`-bound endpoint accepting the level-determined body shape.
708
+ * Generic over `TRow` (annotate at the call site: `@DbActions<Order>(...)`)
709
+ * and `D` (the literal dict, captured via `const D`). Each entry's
710
+ * `disabled` predicate is type-narrowed by its own `requiredFields` literal.
679
711
  *
680
712
  * Multiple `@DbActions` (and shortcut) decorators on the same class
681
713
  * accumulate.
682
714
  */
683
- declare function DbActions(dict: Record<string, TDbActionsEntry>): ClassDecorator;
715
+ declare function DbActions<TRow = unknown, const D extends Record<string, unknown> = {}>(dict: D & ValidatedDict<TRow, D>): ClassDecorator;
684
716
  /** Sugar for `@DbActions` with `level: 'table'` injected into each entry. */
685
- declare function DbTableActions(dict: Record<string, TDbActionsEntryUnpinned>): ClassDecorator;
717
+ declare function DbTableActions<TRow = unknown, const D extends Record<string, unknown> = {}>(dict: D & ValidatedUnpinnedDict<TRow, D>): ClassDecorator;
686
718
  /** Sugar for `@DbActions` with `level: 'row'` injected into each entry. */
687
- declare function DbRowActions(dict: Record<string, TDbActionsEntryUnpinned>): ClassDecorator;
719
+ declare function DbRowActions<TRow = unknown, const D extends Record<string, unknown> = {}>(dict: D & ValidatedUnpinnedDict<TRow, D>): ClassDecorator;
688
720
  /** Sugar for `@DbActions` with `level: 'rows'` injected into each entry. */
689
- declare function DbRowsActions(dict: Record<string, TDbActionsEntryUnpinned>): ClassDecorator;
721
+ declare function DbRowsActions<TRow = unknown, const D extends Record<string, unknown> = {}>(dict: D & ValidatedUnpinnedDict<TRow, D>): ClassDecorator;
690
722
  //#endregion
691
723
  //#region src/actions/discover.d.ts
692
724
  /**
693
- * Discover all actions declared on a controller and produce the `/meta` array.
694
- * Reads class + method metadata via `getMoostMate()` and resolves bound POST
695
- * paths through the Moost controller overview.
696
- *
697
- * Result is memoized per controller constructor — discovery walks every
698
- * handler entry and reads decorator metadata, which is wasted work to repeat
699
- * across instances.
725
+ * Pairs the wire-shaped `info` with the original decorator opts / dict entry,
726
+ * so the augmenter can invoke the live `disabled` reference (deliberately
727
+ * absent from the wire `info`).
700
728
  */
701
- declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionInfo$1[];
729
+ interface TDbActionEnvelope {
730
+ info: TDbActionInfo$1;
731
+ raw: DbActionOpts | TDbActionsEntry;
732
+ }
733
+ /** Discover actions on a controller, memoized per ctor. `info`-only callers map `e => e.info`. */
734
+ declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
702
735
  //#endregion
703
- //#region src/actions/pk-validation.d.ts
704
- /**
705
- * Minimal shape required to validate PKs against a table — supplied by the
706
- * controller's underlying `AtscriptDbReadable`/`AtscriptDbTable`.
707
- */
708
- interface PkValidationSource {
709
- primaryKeys: readonly string[];
736
+ //#region src/actions/id-validation.d.ts
737
+ interface IdValidationSource {
738
+ getIdentifications(): readonly TIdentification[];
710
739
  fieldDescriptors: readonly TDbFieldMeta[];
711
740
  }
712
741
  //#endregion
713
- //#region src/actions/pk-cache.d.ts
714
- declare const useDbActionPk: _wooksjs_event_core0.WookComposable<{
715
- load: () => Promise<unknown>;
742
+ //#region src/actions/id-cache.d.ts
743
+ declare const useDbActionId: _wooksjs_event_core0.WookComposable<{
744
+ load: () => Promise<Record<string, unknown>>;
716
745
  }>;
717
- declare const useDbActionPks: _wooksjs_event_core0.WookComposable<{
718
- load: () => Promise<unknown[]>;
746
+ declare const useDbActionIds: _wooksjs_event_core0.WookComposable<{
747
+ load: () => Promise<Record<string, unknown>[]>;
719
748
  }>;
720
749
  //#endregion
721
750
  //#region src/actions/row-cache.d.ts
@@ -723,7 +752,7 @@ declare const useDbActionRow: _wooksjs_event_core0.WookComposable<{
723
752
  load: () => Promise<unknown>;
724
753
  }>;
725
754
  declare const useDbActionRows: _wooksjs_event_core0.WookComposable<{
726
- load: () => Promise<unknown[]>;
755
+ load: () => Promise<(Record<string, unknown> | undefined)[]>;
727
756
  }>;
728
757
  //#endregion
729
758
  //#region src/actions/action-disabled-error.d.ts
@@ -736,8 +765,8 @@ declare const useDbActionRows: _wooksjs_event_core0.WookComposable<{
736
765
  *
737
766
  * - `name: 'ActionDisabledError'` — discriminator the client matches.
738
767
  * - `action` — the `@DbAction` name that rejected the request.
739
- * - `pk?` — present only for `'row'`-level rejections.
740
- * - `pks?` — present only for `'rows'`-level rejections.
768
+ * - `id?` — present only for `'row'`-level rejections.
769
+ * - `ids?` — present only for `'rows'`-level rejections.
741
770
  *
742
771
  * `message` is populated with a human-readable string so generic
743
772
  * `ClientError` consumers (which read `body.message`) still get something
@@ -748,27 +777,42 @@ interface ActionDisabledErrorBody {
748
777
  message: string;
749
778
  statusCode: 409;
750
779
  action: string;
751
- pk?: unknown;
752
- pks?: unknown[];
780
+ id?: Record<string, unknown>;
781
+ ids?: Record<string, unknown>[];
753
782
  }
754
783
  /**
755
784
  * Thrown by the gate interceptor when `disabled` returns truthy. Composes
756
785
  * with Moost's existing error mapper to produce HTTP 409 with the wire body
757
786
  * defined by {@link ActionDisabledErrorBody}.
758
787
  *
759
- * - `'row'`-level rejection: pass `(action, pk)` — the body emits `pk`.
760
- * - `'rows'`-level rejection: pass `(action, undefined, pks)` — the body
761
- * emits `pks` (the FULL list of failing PKs in reject mode; the FULL list
762
- * of request PKs in skip mode with zero survivors).
788
+ * - `'row'`-level rejection: pass `(action, id)` — the body emits `id`.
789
+ * - `'rows'`-level rejection: pass `(action, undefined, ids)` — the body
790
+ * emits `ids` (the FULL list of failing IDs in reject mode; the FULL list
791
+ * of request IDs in skip mode with zero survivors).
763
792
  */
764
793
  declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
765
794
  name: string;
766
- constructor(action: string, pk?: unknown, pks?: unknown[]);
795
+ constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[]);
767
796
  }
768
797
  //#endregion
798
+ //#region src/actions/per-row.d.ts
799
+ /**
800
+ * Lift a per-row predicate into the batch shape required by
801
+ * `@DbAction` opts.`disabled` and class-level dict `disabled`. Polarity is
802
+ * preserved — `true` from `fn` means the action is disabled for that row.
803
+ *
804
+ * ```ts
805
+ * @DbAction<Order>('archive', {
806
+ * requiredFields: ['status'],
807
+ * disabled: perRow(r => r.status === 'archived'),
808
+ * })
809
+ * ```
810
+ */
811
+ declare const perRow: <TRow>(fn: (row: TRow) => boolean) => (rows: TRow[]) => boolean[];
812
+ //#endregion
769
813
  //#region src/permissions/crud-controls.d.ts
770
814
  declare const QUERY_CONTROLS: readonly string[];
771
815
  declare const PAGES_CONTROLS: readonly string[];
772
816
  declare const ONE_CONTROLS: readonly string[];
773
817
  //#endregion
774
- export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, DbAction, DbActionDefault, type DbActionOpts, DbActionPK, DbActionPKs, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, ONE_CONTROLS, PAGES_CONTROLS, type PkValidationSource, QUERY_CONTROLS, READABLE_DEF, ReadableController, ReadableGates, TABLE_DEF, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, discoverActions, useDbActionPk, useDbActionPks, useDbActionRow, useDbActionRows, validationErrorTransform };
818
+ export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, DbAction, DbActionDefault, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, type IdValidationSource, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, ReadableGates, TABLE_DEF, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, discoverActions, perRow, useDbActionId, useDbActionIds, useDbActionRow, useDbActionRows, validationErrorTransform };