@atscript/moost-db 0.1.146 → 0.1.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3774 -1603
- package/dist/index.d.cts +895 -230
- package/dist/index.d.mts +896 -231
- package/dist/index.mjs +3770 -1608
- package/package.json +19 -19
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { i as resolveDbSpace, n as clearDbSpaces, r as provideDbSpace, t as DEFAULT_DB_SPACE } from "./db-space-registry-CWpYwZ4R.cjs";
|
|
2
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializedAnnotatedType, TValidatorOptions, Validator } from "@atscript/typescript/utils";
|
|
3
|
-
import { AggregateFn, AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, TCrudOp, 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, TDbAvailableActions, 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";
|
|
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";
|
|
5
5
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
6
6
|
import { parseUrl } from "@uniqu/url";
|
|
7
|
+
import { EventContext } from "@wooksjs/event-core";
|
|
8
|
+
|
|
7
9
|
//#region src/as-readable.controller.d.ts
|
|
8
10
|
/**
|
|
9
11
|
* Endpoint a {@link AsReadableController.prepareRequest} call serves. `/one`
|
|
@@ -12,11 +14,14 @@ import { parseUrl } from "@uniqu/url";
|
|
|
12
14
|
* like DB readables do; every `@DbAction` handler (row, rows and table
|
|
13
15
|
* level) reports `"action"` with the action's name in
|
|
14
16
|
* {@link TDbRequestContext.action}; `GET /meta/actions/:id` and
|
|
15
|
-
* `/meta/actions?…` report `"availableActions"` (since 0.1.145)
|
|
17
|
+
* `/meta/actions?…` report `"availableActions"` (since 0.1.145); a view's
|
|
18
|
+
* `POST /delegated-actions/:name` (a query target for a `@DbActionsFrom`
|
|
19
|
+
* action — a read of THIS controller's rows) reports `"delegatedAction"` with
|
|
20
|
+
* the action's name in {@link TDbRequestContext.action} (since 0.1.147).
|
|
16
21
|
*
|
|
17
22
|
* @since 0.1.143
|
|
18
23
|
*/
|
|
19
|
-
type TDbRequestEndpoint = "query" | "pages" | "geo" | "one" | "meta" | "metaForm" | "insert" | "replace" | "update" | "remove" | "action" | "availableActions";
|
|
24
|
+
type TDbRequestEndpoint = "query" | "pages" | "geo" | "one" | "meta" | "metaForm" | "insert" | "replace" | "update" | "remove" | "action" | "availableActions" | "delegatedAction";
|
|
20
25
|
/**
|
|
21
26
|
* Context passed to {@link AsReadableController.prepareRequest}.
|
|
22
27
|
*
|
|
@@ -32,7 +37,29 @@ interface TDbRequestContext {
|
|
|
32
37
|
* `metaForm`, writes and actions.
|
|
33
38
|
*/
|
|
34
39
|
readonly controls?: Record<string, unknown>;
|
|
35
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* The parsed CLIENT filter of `query`, `pages` and `geo` (absent when the
|
|
42
|
+
* URL carries none, and on every other endpoint) — e.g. for a permission
|
|
43
|
+
* layer to resolve which relations the request's relational predicates
|
|
44
|
+
* (`ticket=$some(…)`) touch, alongside `controls.$with`. A deep-frozen
|
|
45
|
+
* COPY of the parsed filter — reading it is all a hook can do; the request
|
|
46
|
+
* gate judges the original. Server-side filters (`transformFilter`, …) are
|
|
47
|
+
* not in it.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.1.147
|
|
50
|
+
*/
|
|
51
|
+
readonly filter?: FilterExpr;
|
|
52
|
+
/**
|
|
53
|
+
* Read endpoints (`query`, `pages`, `geo`, `one`): whether {@link filter} holds a relational
|
|
54
|
+
* predicate (`ticket=$some(…)`) — `false` without a filter. Reading
|
|
55
|
+
* `filter` copies the whole filter on first access; check this first when
|
|
56
|
+
* only the predicates matter. `$with` sub-filters are not counted (they are
|
|
57
|
+
* in `controls.$with`).
|
|
58
|
+
*
|
|
59
|
+
* @since 0.1.147
|
|
60
|
+
*/
|
|
61
|
+
readonly hasRelationFilters?: boolean;
|
|
62
|
+
/** `"action"` / `"delegatedAction"` endpoints only: the `@DbAction` name being run. */
|
|
36
63
|
readonly action?: string;
|
|
37
64
|
}
|
|
38
65
|
/** Control DTO a {@link AsReadableController.validateControls} call checks against. @since 0.1.143 (`"geo"`) */
|
|
@@ -321,14 +348,330 @@ interface IdValidationSource {
|
|
|
321
348
|
readonly fieldDescriptors: readonly TDbFieldMeta[];
|
|
322
349
|
}
|
|
323
350
|
//#endregion
|
|
351
|
+
//#region src/actions/query-target.d.ts
|
|
352
|
+
/**
|
|
353
|
+
* A query target — the `query` of an action request body: "every row
|
|
354
|
+
* matching this query" instead of a list of identifiers (since 0.1.147).
|
|
355
|
+
*
|
|
356
|
+
* @since 0.1.147
|
|
357
|
+
*/
|
|
358
|
+
interface TDbActionQueryTarget {
|
|
359
|
+
/** The query string `GET /query` accepts — filter plus `$search` / `$index` ONLY. */
|
|
360
|
+
q: string;
|
|
361
|
+
/** Identifiers to leave out (any identification; at most the action's `maxIds`). */
|
|
362
|
+
exclude?: Record<string, unknown>[];
|
|
363
|
+
/** Guard: the request fails with 409 `TARGET_CHANGED` when the match count differs. */
|
|
364
|
+
expectCount?: number;
|
|
365
|
+
/** Client-side cap (it never raises the server's `maxRows`). */
|
|
366
|
+
maxRows?: number;
|
|
367
|
+
/** Resolve and count only — the reply is `{ matched }`, the handler does not run. */
|
|
368
|
+
dryRun?: boolean;
|
|
369
|
+
}
|
|
370
|
+
/** `queryTarget` of `@DbAction` options. `true` = the defaults. @since 0.1.147 */
|
|
371
|
+
type TDbQueryTargetOpts = boolean | {
|
|
372
|
+
maxRows?: number;
|
|
373
|
+
batchSize?: number;
|
|
374
|
+
};
|
|
375
|
+
/**
|
|
376
|
+
* Key of `AsDbReadableController`'s internal query-target resolver (a
|
|
377
|
+
* registered symbol, like `ACTION_OVERLAY`).
|
|
378
|
+
*/
|
|
379
|
+
declare const RESOLVE_TARGET: unique symbol;
|
|
380
|
+
/** What {@link RESOLVE_TARGET} resolves. */
|
|
381
|
+
interface TTargetRequest {
|
|
382
|
+
action: string;
|
|
383
|
+
/** The raw `query` of the request body — validated by the resolver. */
|
|
384
|
+
query: unknown;
|
|
385
|
+
/** The most rows the target may match (server side). */
|
|
386
|
+
cap: number;
|
|
387
|
+
/** The most `exclude` entries. */
|
|
388
|
+
maxExclude: number;
|
|
389
|
+
/**
|
|
390
|
+
* The row overlay the target resolves under, besides `queryTargetScope`:
|
|
391
|
+
* `"action"` — the controller's `rowOverlay()` (its own action runs on the
|
|
392
|
+
* rows); `"read"` — `transformFilter` (a view resolving rows it delegates).
|
|
393
|
+
*/
|
|
394
|
+
overlay: "action" | "read";
|
|
395
|
+
/** Fields of the phase-1 read (sorted by the first identification's fields). */
|
|
396
|
+
select: readonly string[];
|
|
397
|
+
/** Key sets `exclude` entries may use besides the controller's identifications. */
|
|
398
|
+
excludeShapes?: readonly (readonly string[])[];
|
|
399
|
+
}
|
|
400
|
+
/** A resolved query target: the phase-1 snapshot plus its re-check. */
|
|
401
|
+
interface TResolvedTarget {
|
|
402
|
+
/** Rows matched at phase 1 (after `exclude`). */
|
|
403
|
+
matched: number;
|
|
404
|
+
/** The phase-1 rows (`select` fields), ordered by identity. */
|
|
405
|
+
rows: Record<string, unknown>[];
|
|
406
|
+
dryRun: boolean;
|
|
407
|
+
/** The validated `exclude` entries (already applied to {@link rows}). */
|
|
408
|
+
exclude: Record<string, unknown>[];
|
|
409
|
+
/**
|
|
410
|
+
* The rows `ids` address that STILL match the target (filter, search,
|
|
411
|
+
* overlay, `queryTargetScope`, `exclude`), aligned with `ids`; `select`
|
|
412
|
+
* plus the id fields. The FIRST call directly follows the snapshot and is
|
|
413
|
+
* not re-checked (served from {@link rows}, or read by identity alone
|
|
414
|
+
* when `select` needs more fields); every later call re-checks.
|
|
415
|
+
*/
|
|
416
|
+
load(ids: readonly Record<string, unknown>[], select: Iterable<string>): Promise<Array<Record<string, unknown> | undefined>>;
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/actions/types.d.ts
|
|
420
|
+
/**
|
|
421
|
+
* One entry of a `disabled` predicate's result. Truthy = the action is
|
|
422
|
+
* disabled for that row; falsy (`false`, `""`) = enabled. A non-empty string
|
|
423
|
+
* disables the action AND carries a human-readable reason — surfaced in the
|
|
424
|
+
* 409 `ActionDisabledError` message/body and in the row's `$disabledReasons`.
|
|
425
|
+
*
|
|
426
|
+
* @since 0.1.141 (`string`)
|
|
427
|
+
*/
|
|
428
|
+
type TDbActionDisabledVerdict = boolean | string;
|
|
429
|
+
/** `'rows'`-level batch policy — controls whether failing rows reject or are filtered out. */
|
|
430
|
+
type TOnDisabledRows = "reject" | "skip";
|
|
431
|
+
/**
|
|
432
|
+
* Dot-notation field paths of `TRow`'s flat type. Drives both the runtime
|
|
433
|
+
* projection widening and the type narrowing of the `disabled` predicate's
|
|
434
|
+
* row argument. Relations are absent from `FlatOf<T>` — listing a relation
|
|
435
|
+
* field is therefore a compile error.
|
|
436
|
+
*
|
|
437
|
+
* Permissive fallback when `TRow = unknown` (no explicit decorator generic):
|
|
438
|
+
* any string is allowed and the `disabled` predicate's row arg is `any[]`,
|
|
439
|
+
* preserving the prior loose typing for un-annotated call sites.
|
|
440
|
+
*/
|
|
441
|
+
type FlatKey<TRow> = unknown extends TRow ? string : keyof FlatOf<TRow> & string;
|
|
442
|
+
/** Row-shape narrowing for the `disabled` predicate. Falls back to `any` when `TRow = unknown`. */
|
|
443
|
+
type DisabledRowsArg<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? any[] : Pick<FlatOf<TRow>, R[number] & keyof FlatOf<TRow>>[];
|
|
444
|
+
interface NoGate {
|
|
445
|
+
requiredFields?: never;
|
|
446
|
+
disabled?: never;
|
|
447
|
+
onDisabledRows?: never;
|
|
448
|
+
}
|
|
449
|
+
interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
450
|
+
/**
|
|
451
|
+
* Dot-notation field paths the predicate references. SERVER-INTERNAL —
|
|
452
|
+
* never emitted on the `/meta` wire. Consumed verbatim to widen the DB
|
|
453
|
+
* projection so `disabled` always sees the fields it declared.
|
|
454
|
+
*/
|
|
455
|
+
requiredFields: R;
|
|
456
|
+
/**
|
|
457
|
+
* Sync batch gate predicate — returns a parallel array aligned with the
|
|
458
|
+
* input. Per entry: truthy = disabled for the corresponding row; a
|
|
459
|
+
* non-empty string also gives the reason (see
|
|
460
|
+
* {@link TDbActionDisabledVerdict}). The `rows`
|
|
461
|
+
* argument is type-narrowed to `Pick<FlatOf<TRow>, R[number]>[]`; reading
|
|
462
|
+
* a field not listed in `requiredFields` is a compile error.
|
|
463
|
+
*
|
|
464
|
+
* Promise return is NOT permitted — the predicate is consumed in the
|
|
465
|
+
* same tick by the gate and the augmenter.
|
|
466
|
+
*/
|
|
467
|
+
disabled?: (rows: DisabledRowsArg<TRow, R>) => TDbActionDisabledVerdict[];
|
|
468
|
+
/**
|
|
469
|
+
* `'rows'`-level batch policy. Default `'reject'`.
|
|
470
|
+
*
|
|
471
|
+
* - `'reject'`: evaluate every row before throwing; if any row fails, the
|
|
472
|
+
* error body lists ALL failing IDs; handler not invoked.
|
|
473
|
+
* - `'skip'`: filter cached rows + cached IDs to passing-only; zero
|
|
474
|
+
* survivors → reject. Handler runs against the survivors.
|
|
475
|
+
*
|
|
476
|
+
* Ignored for `'row'` and `'table'` level actions.
|
|
477
|
+
*/
|
|
478
|
+
onDisabledRows?: TOnDisabledRows;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Loose gate shape used when `TRow = unknown` (no explicit decorator generic).
|
|
482
|
+
* Preserves the prior un-typed call-site flexibility; the runtime still drops
|
|
483
|
+
* actions where `disabled` is set without `requiredFields`.
|
|
484
|
+
*/
|
|
485
|
+
interface LooseGate {
|
|
486
|
+
requiredFields?: string[];
|
|
487
|
+
disabled?: (rows: any[]) => TDbActionDisabledVerdict[];
|
|
488
|
+
onDisabledRows?: TOnDisabledRows;
|
|
489
|
+
}
|
|
490
|
+
type GateOpts<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? LooseGate : NoGate | WithGate<TRow, R>;
|
|
491
|
+
interface BaseActionOpts extends Partial<Omit<TDbActionInfo$1, "name" | "level" | "processor" | "value" | "disabled" | "formUrl" | "owner" | "idMap" | "queryTarget">> {
|
|
492
|
+
/**
|
|
493
|
+
* Bound table reference. REQUIRED on non-`AsDbReadableController` classes
|
|
494
|
+
* when `disabled` is set OR a `@DbActionRow*` parameter is declared.
|
|
495
|
+
*
|
|
496
|
+
* Silently ignored on `AsDbReadableController` subclasses (which include
|
|
497
|
+
* `AsDbController`) — the bound table from the controller wins.
|
|
498
|
+
*/
|
|
499
|
+
table?: AtscriptDbTable<any>;
|
|
500
|
+
/**
|
|
501
|
+
* `'rows'` level (`@DbActionIDs` / `@DbActionRows`): the most identifiers
|
|
502
|
+
* one request may carry. Above it the request is rejected with 400 before
|
|
503
|
+
* any row is loaded. Default `1000`. Server-internal — never on the wire.
|
|
504
|
+
*
|
|
505
|
+
* @since 0.1.143
|
|
506
|
+
*/
|
|
507
|
+
maxIds?: number;
|
|
508
|
+
/**
|
|
509
|
+
* `'rows'` level only: the action also accepts a query target — a body
|
|
510
|
+
* `{ query: { q, exclude?, expectCount?, maxRows?, dryRun? }, input? }`
|
|
511
|
+
* meaning "every row matching `q`" (the `GET /query` filter plus
|
|
512
|
+
* `$search` / `$index`) under the caller's read scope (`queryTargetScope`)
|
|
513
|
+
* and the action's own gate. `true` = `{ maxRows: 10_000, batchSize:
|
|
514
|
+
* 500 }`. `maxRows` (on the wire as `queryTarget.maxRows`) caps the
|
|
515
|
+
* matched rows; a `@DbActionTarget()` handler gets them in `batchSize`
|
|
516
|
+
* batches, a `@DbActionIDs` / `@DbActionRows` handler at once (then also
|
|
517
|
+
* capped by `maxIds`).
|
|
518
|
+
*
|
|
519
|
+
* @since 0.1.147
|
|
520
|
+
*/
|
|
521
|
+
queryTarget?: TDbQueryTargetOpts;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Options accepted by `@DbAction(name, opts?)`. Generic over `TRow` (the
|
|
525
|
+
* controller's bound atscript type) and `R` (the literal `requiredFields`
|
|
526
|
+
* tuple). Both are inferred at the call site via the decorator's `<TRow>`
|
|
527
|
+
* argument plus `const R` generic.
|
|
528
|
+
*/
|
|
529
|
+
type DbActionOpts<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = BaseActionOpts & GateOpts<TRow, R>;
|
|
530
|
+
interface DbActionsEntryCommonBase {
|
|
531
|
+
label: string;
|
|
532
|
+
level: TDbActionLevel$1;
|
|
533
|
+
icon?: string;
|
|
534
|
+
intent?: TDbActionIntent$1;
|
|
535
|
+
description?: string;
|
|
536
|
+
order?: number;
|
|
537
|
+
default?: boolean;
|
|
538
|
+
/** Mirrors {@link TDbActionInfo.promptText} — singular/plural via tuple. */
|
|
539
|
+
promptText?: string | [string, string];
|
|
540
|
+
/** Mirrors {@link TDbActionInfo.shortcut} — single-character UI hint. */
|
|
541
|
+
shortcut?: string;
|
|
542
|
+
/**
|
|
543
|
+
* Input form the UI collects before invoking the action:
|
|
544
|
+
*
|
|
545
|
+
* - a compiled `.as` interface — registered on THIS controller and served
|
|
546
|
+
* by its own `GET /meta/form/:name`; the wire carries `inputForm: Type.name`.
|
|
547
|
+
* - `{ name, url }` — a form served elsewhere: `name` goes on the wire as
|
|
548
|
+
* `inputForm`, `url` (server-absolute path of the serialized schema, e.g.
|
|
549
|
+
* `"/api/shipping/meta/form/ShipForm"`) as {@link TDbActionInfo.formUrl}.
|
|
550
|
+
*
|
|
551
|
+
* Not allowed with `processor: 'navigate'`. Class-level entries only
|
|
552
|
+
* describe the action — validating `input` is the target handler's job
|
|
553
|
+
* (e.g. its own `@InputForm(Type)` param).
|
|
554
|
+
*
|
|
555
|
+
* @since 0.1.136
|
|
556
|
+
*/
|
|
557
|
+
inputForm?: TAtscriptAnnotatedType | {
|
|
558
|
+
name: string;
|
|
559
|
+
url: string;
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
type DbActionsEntryWithGate<TRow, R extends readonly FlatKey<TRow>[]> = DbActionsEntryCommonBase & GateOpts<TRow, R>;
|
|
563
|
+
/**
|
|
564
|
+
* Class-level dict entry. `value` semantics by processor:
|
|
565
|
+
*
|
|
566
|
+
* - `'navigate'` — REQUIRED, non-empty. URL template (`$1` substituted client-side).
|
|
567
|
+
* - `'backend'` — REQUIRED, non-empty. Full HTTP POST path the UI client invokes.
|
|
568
|
+
* - `'custom'` — `value` is forbidden in the entry; the meta builder fills it
|
|
569
|
+
* with the dict key.
|
|
570
|
+
*/
|
|
571
|
+
type TDbActionsEntry<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = (DbActionsEntryWithGate<TRow, R> & {
|
|
572
|
+
processor: "navigate";
|
|
573
|
+
value: string;
|
|
574
|
+
inputForm?: never;
|
|
575
|
+
}) | (DbActionsEntryWithGate<TRow, R> & {
|
|
576
|
+
processor: "custom";
|
|
577
|
+
value?: never;
|
|
578
|
+
}) | (DbActionsEntryWithGate<TRow, R> & {
|
|
579
|
+
processor: "backend";
|
|
580
|
+
value: string;
|
|
581
|
+
});
|
|
582
|
+
/** Distributes `Omit` across the discriminated union members. */
|
|
583
|
+
type DistributiveOmit<T, K extends keyof T> = T extends T ? Omit<T, K> : never;
|
|
584
|
+
/** Same as {@link TDbActionsEntry} but without the `level` field — used by the level-pinned shortcuts. */
|
|
585
|
+
type TDbActionsEntryUnpinned<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = DistributiveOmit<TDbActionsEntry<TRow, R>, "level">;
|
|
586
|
+
type DbActionsDictBase = Record<string, unknown>;
|
|
587
|
+
type EntryRequiredFields<E, TRow> = E extends {
|
|
588
|
+
requiredFields: infer R;
|
|
589
|
+
} ? R extends readonly FlatKey<TRow>[] ? R : [] : [];
|
|
590
|
+
type ValidatedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntry<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
591
|
+
type ValidatedUnpinnedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntryUnpinned<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region src/actions/discover.d.ts
|
|
594
|
+
/**
|
|
595
|
+
* Pairs the wire-shaped `info` with the original decorator opts / dict entry,
|
|
596
|
+
* so the augmenter can invoke the live `disabled` reference (deliberately
|
|
597
|
+
* absent from the wire `info`).
|
|
598
|
+
*/
|
|
599
|
+
/** A discovered action: its `/meta.actions[]` entry plus the server-internal opts it was declared with. */
|
|
600
|
+
interface TDbActionEnvelope {
|
|
601
|
+
info: TDbActionInfo$1;
|
|
602
|
+
raw: DbActionOpts | TDbActionsEntry;
|
|
603
|
+
}
|
|
604
|
+
/** Lookup helper for `AsReadableController.metaForm()`. */
|
|
605
|
+
declare function getControllerFormType(ctor: Function, name: string): TAtscriptAnnotatedType | undefined;
|
|
606
|
+
/** Discover actions on a controller, memoized per ctor. `info`-only callers map `e => e.info`. */
|
|
607
|
+
declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
608
|
+
/**
|
|
609
|
+
* The `'row'` / `'rows'`-level subset of {@link discoverActions} (since
|
|
610
|
+
* 0.1.145 public) — the actions `$actions` and `GET /meta/actions/:id`
|
|
611
|
+
* consider, in `/meta.actions` order. Memoized per controller class.
|
|
612
|
+
*/
|
|
613
|
+
declare function discoverRowLevelActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
614
|
+
//#endregion
|
|
615
|
+
//#region src/actions/delegation.d.ts
|
|
616
|
+
/** Internal: a source controller's batch `$actions` verdicts for delegated ids. */
|
|
617
|
+
declare const ACTION_VERDICTS: unique symbol;
|
|
618
|
+
/** Internal: a source controller's `GET /meta/actions/:id` answer for one id. */
|
|
619
|
+
declare const AVAILABLE_ACTIONS: unique symbol;
|
|
620
|
+
/** Internal: the subset of delegated action names a source controller lists for the caller. */
|
|
621
|
+
declare const ALLOWED_ACTIONS: unique symbol;
|
|
622
|
+
/** `true` when the controller class declares `@DbActionsFrom` (inherited included). */
|
|
623
|
+
declare function hasActionDelegations(ctor: Function): boolean;
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/actions/scope-context.d.ts
|
|
626
|
+
/**
|
|
627
|
+
* Which surface asks {@link TDbActionScopeContext} — see
|
|
628
|
+
* `AsDbReadableController.actionRowScope`:
|
|
629
|
+
*
|
|
630
|
+
* - `"execute"` — the action gate, about to run the action on `ids`;
|
|
631
|
+
* - `"rows"` — `$actions` on a read (also a view's delegated verdicts);
|
|
632
|
+
* - `"available"` — `GET /meta/actions/:id` (one row).
|
|
633
|
+
*
|
|
634
|
+
* @since 0.1.147
|
|
635
|
+
*/
|
|
636
|
+
type TDbActionScopePurpose = "execute" | "rows" | "available";
|
|
637
|
+
/**
|
|
638
|
+
* 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.
|
|
641
|
+
*
|
|
642
|
+
* @since 0.1.147
|
|
643
|
+
*/
|
|
644
|
+
interface TDbActionScopeContext {
|
|
645
|
+
/** Which surface asks — the action gate, `$actions` on a read, or `GET /meta/actions`. */
|
|
646
|
+
readonly purpose: TDbActionScopePurpose;
|
|
647
|
+
/**
|
|
648
|
+
* The candidates' identities (`preferredId`-shaped), deduped and never
|
|
649
|
+
* empty. ONE array object per evaluation, shared by every action of it —
|
|
650
|
+
* memoize on it (`WeakMap`) when several actions derive the same filter.
|
|
651
|
+
*/
|
|
652
|
+
readonly ids: readonly Record<string, unknown>[];
|
|
653
|
+
/**
|
|
654
|
+
* The candidates' `fields` (plus the identity fields), read straight from
|
|
655
|
+
* the bound readable without any overlay (the candidates already passed
|
|
656
|
+
* it). Memoized per evaluation and field set. Nothing of it reaches the
|
|
657
|
+
* response.
|
|
658
|
+
*/
|
|
659
|
+
loadRows(fields: readonly string[]): Promise<readonly Record<string, unknown>[]>;
|
|
660
|
+
}
|
|
661
|
+
//#endregion
|
|
324
662
|
//#region src/actions/row-scope.d.ts
|
|
325
663
|
/**
|
|
326
664
|
* The key of `AsDbReadableController`'s internal action-overlay method —
|
|
327
|
-
* `rowOverlay()`
|
|
328
|
-
*
|
|
329
|
-
*
|
|
665
|
+
* its `rowOverlay()` (since 0.1.147 without the action's `actionRowScope`,
|
|
666
|
+
* which needs the candidate rows — see {@link ACTION_SCOPE}). A registered
|
|
667
|
+
* symbol: not an overridable seam, and still found when moost-db loads in
|
|
668
|
+
* two module realms (moost-vite SSR).
|
|
330
669
|
*/
|
|
331
670
|
declare const ACTION_OVERLAY: unique symbol;
|
|
671
|
+
/** The controller's internal `actionRowScope` call for candidate rows (since 0.1.147). */
|
|
672
|
+
declare const ACTION_SCOPE: unique symbol;
|
|
673
|
+
/** `true` when the controller overrides `actionRowScope` (since 0.1.147). */
|
|
674
|
+
declare const ACTION_SCOPED: unique symbol;
|
|
332
675
|
//#endregion
|
|
333
676
|
//#region src/meta/field-capabilities.d.ts
|
|
334
677
|
/**
|
|
@@ -459,8 +802,14 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
459
802
|
*
|
|
460
803
|
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
461
804
|
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
805
|
+
*
|
|
806
|
+
* `prefix` (since 0.1.147) is this index's readable's dotted path from the
|
|
807
|
+
* controller when it judges a relational predicate's operand (`"ticket."`):
|
|
808
|
+
* `exists` still receives the LOCAL path, the verdict's `path` and message
|
|
809
|
+
* name the prefixed one. A filter on a navigation path names the predicate
|
|
810
|
+
* alternative (`ticket=$some(status=…)`).
|
|
462
811
|
*/
|
|
463
|
-
check(
|
|
812
|
+
check(local: string, op: TQueryPathOp$1, exists: (path: string) => boolean, predicate?: TFilterPredicate, prefix?: string): TCapabilityVerdict | undefined;
|
|
464
813
|
}
|
|
465
814
|
//#endregion
|
|
466
815
|
//#region src/as-db-readable.controller.d.ts
|
|
@@ -515,13 +864,15 @@ interface TDbFieldVisibility {
|
|
|
515
864
|
/**
|
|
516
865
|
* `hasField(path)` and — when {@link scoped} — a `@db.column.derived`
|
|
517
866
|
* field of the bound readable only while its source path is visible too
|
|
518
|
-
* (a derived copy must not outlive a hidden source)
|
|
867
|
+
* (a derived copy must not outlive a hidden source), a computed view
|
|
868
|
+
* column (`@db.compute`, since 0.1.147) only while every operand and every
|
|
869
|
+
* intermediate computed field it reads through is.
|
|
519
870
|
*/
|
|
520
871
|
readonly isVisible: (path: string) => boolean;
|
|
521
872
|
/**
|
|
522
873
|
* The paths sealed out of `readable`'s read projection for this request:
|
|
523
874
|
* its `@db.writeOnly` fields plus, when {@link scoped}, its derived fields
|
|
524
|
-
* whose source `hasField` hides. `prefix` is `readable`'s path from the
|
|
875
|
+
* whose source `hasField` hides and its computed fields with a hidden operand. `prefix` is `readable`'s path from the
|
|
525
876
|
* controller: `""` for the bound readable, `"rel."` for a `$with` target.
|
|
526
877
|
*/
|
|
527
878
|
readonly sealedFor: (readable: AtscriptDbReadable<any>, prefix?: string) => ReadonlySet<string>;
|
|
@@ -559,6 +910,14 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
559
910
|
*/
|
|
560
911
|
protected get capabilities(): FieldCapabilityIndex;
|
|
561
912
|
private _capabilities?;
|
|
913
|
+
/** The client relational-predicate gate (since 0.1.147), built on first use — see {@link _relationGate}. */
|
|
914
|
+
private _relGate?;
|
|
915
|
+
/**
|
|
916
|
+
* The client's `$with` tree per request, keyed by its controls object —
|
|
917
|
+
* recorded in {@link validateParsed} before {@link validateControls} (see
|
|
918
|
+
* `snapshotClientWith`).
|
|
919
|
+
*/
|
|
920
|
+
private readonly _clientWith;
|
|
562
921
|
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
563
922
|
protected metaCacheKey(): unknown;
|
|
564
923
|
/**
|
|
@@ -571,7 +930,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
571
930
|
protected readonly fieldVisibility: TDbFieldVisibility;
|
|
572
931
|
/** A subclass overrides {@link hasField}: visibility is request-scoped (derived rule, index gate, id options). */
|
|
573
932
|
private readonly _hasFieldOverridden;
|
|
574
|
-
/**
|
|
933
|
+
/**
|
|
934
|
+
* `@db.column.derived` path → its source's logical path, `@db.compute` path
|
|
935
|
+
* → its operands' paths plus the computed fields it reads through, per
|
|
936
|
+
* readable (bound + `$with` targets).
|
|
937
|
+
*/
|
|
575
938
|
private readonly _derivedSources;
|
|
576
939
|
/** The bound readable's entry of {@link _derivedSources}. */
|
|
577
940
|
private readonly _derivedSource;
|
|
@@ -602,6 +965,10 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
602
965
|
private readonly _hasAllowedActions;
|
|
603
966
|
/** `true` when a subclass overrides {@link actionRowScope} (the gate, `$actions` and `/meta/actions` apply it). */
|
|
604
967
|
private readonly _hasActionRowScope;
|
|
968
|
+
/** `true` when the class declares `@DbActionsFrom` (since 0.1.147). */
|
|
969
|
+
private readonly _hasDelegations;
|
|
970
|
+
/** `transformProjection` is overridden (a delegation's id paths are checked against it). */
|
|
971
|
+
private readonly _hasProjectionHook;
|
|
605
972
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
606
973
|
private readonly _quantityRefByPath;
|
|
607
974
|
/** `@db.column.searchable` paths — the `$search` fallback when the adapter has no native search. */
|
|
@@ -624,9 +991,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
624
991
|
get idSource(): IdValidationSource;
|
|
625
992
|
private _collectInvertibleFields;
|
|
626
993
|
private _collectQuantityRefs;
|
|
627
|
-
/**
|
|
994
|
+
/**
|
|
995
|
+
* `readable`'s `@db.column.derived` path → source path and `@db.compute`
|
|
996
|
+
* path → operand paths map, collected once per readable.
|
|
997
|
+
*/
|
|
628
998
|
private _derivedSourcesOf;
|
|
629
|
-
private _collectAnnotated;
|
|
630
999
|
/**
|
|
631
1000
|
* THE field-visibility hook: every gated path consults it before any
|
|
632
1001
|
* capability check (since 0.1.133) — filter keys (inside `$and` / `$or` /
|
|
@@ -652,7 +1021,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
652
1021
|
* visible fields (or ignores the term when there are none). A
|
|
653
1022
|
* `@db.column.derived` field is visible only while its source path is,
|
|
654
1023
|
* and one whose source is hidden is sealed out of every read projection
|
|
655
|
-
* for the request, like a `@db.writeOnly` field.
|
|
1024
|
+
* for the request, like a `@db.writeOnly` field. The same holds for a
|
|
1025
|
+
* computed view column (`@db.compute`) and each of its operands, including
|
|
1026
|
+
* the intermediate computed fields it reads through.
|
|
656
1027
|
*/
|
|
657
1028
|
protected hasField(path: string): boolean;
|
|
658
1029
|
/**
|
|
@@ -723,8 +1094,6 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
723
1094
|
private _writeOnlyOf;
|
|
724
1095
|
/** {@link TDbFieldVisibility.sealedFor}. */
|
|
725
1096
|
private _sealedFor;
|
|
726
|
-
/** The readable a `$with` entry name (`rel` or dotted `rel.sub`) loads from, if resolvable. */
|
|
727
|
-
private _relTarget;
|
|
728
1097
|
/**
|
|
729
1098
|
* Walks a `$with` tree pre-order: `visit(rel, target, path, controls)` for
|
|
730
1099
|
* every entry whose target readable resolves (`path` = the entry's dotted
|
|
@@ -782,6 +1151,15 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
782
1151
|
* nonexistent index (the core's wording).
|
|
783
1152
|
*/
|
|
784
1153
|
private _checkIndexGate;
|
|
1154
|
+
/**
|
|
1155
|
+
* `/meta`'s search surface as THIS request may use it (only when
|
|
1156
|
+
* {@link hasField} is overridden — the rule of the index gate): indexes
|
|
1157
|
+
* reading a hidden field are left out of `searchIndexes`; `searchable` /
|
|
1158
|
+
* `vectorSearchable` / `geoSearchable` turn off when the index a request
|
|
1159
|
+
* naming none would use reads one (`searchable` stays on for the
|
|
1160
|
+
* `@db.column.searchable` fallback when any of its fields is visible).
|
|
1161
|
+
*/
|
|
1162
|
+
private _applyIndexVisibility;
|
|
785
1163
|
/**
|
|
786
1164
|
* Compute an embedding vector from a search term.
|
|
787
1165
|
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
@@ -800,6 +1178,42 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
800
1178
|
* `findById`). Override to scope `/one` differently.
|
|
801
1179
|
*/
|
|
802
1180
|
protected transformOne(filter: FilterExpr): FilterExpr | Promise<FilterExpr>;
|
|
1181
|
+
/**
|
|
1182
|
+
* Rewrites the sub-filter of a CLIENT relational predicate before it runs
|
|
1183
|
+
* — the row overlay of the related table (since 0.1.147). `path` is the
|
|
1184
|
+
* dotted navigation chain from this controller's table: `"ticket"` for
|
|
1185
|
+
* `ticket=$some(…)`, `"ticket.team"` for a predicate nested in its
|
|
1186
|
+
* operand, `"tickets.issues"` for `$with=tickets(issues=$some(…))`.
|
|
1187
|
+
* Default identity.
|
|
1188
|
+
*
|
|
1189
|
+
* Applied to client predicates only (the URL filter and `$with`
|
|
1190
|
+
* sub-filters, on `/query` incl. `$groupBy` and `$count`, `/pages`, `/geo`
|
|
1191
|
+
* and `/one`, and a query target's filter), after the request gate and before {@link transformFilter};
|
|
1192
|
+
* a nested predicate's operand is rewritten before the operand holding it,
|
|
1193
|
+
* and the hook's output is not walked again. Server-side filters
|
|
1194
|
+
* ({@link transformFilter}, {@link transformOne}, {@link actionRowScope})
|
|
1195
|
+
* never pass through it. Return the operand conjoined with the related
|
|
1196
|
+
* rows the caller may see — `$some` then only matches, and `$none` only
|
|
1197
|
+
* excludes, on VISIBLE related rows, exactly as `$with` shows them:
|
|
1198
|
+
*
|
|
1199
|
+
* ```ts
|
|
1200
|
+
* protected transformRelationFilter(path: string, filter: FilterExpr) {
|
|
1201
|
+
* return path === "ticket" ? { $and: [{ teamId: { $in: currentTeams() } }, filter] } : filter
|
|
1202
|
+
* }
|
|
1203
|
+
* ```
|
|
1204
|
+
*
|
|
1205
|
+
* @since 0.1.147
|
|
1206
|
+
*/
|
|
1207
|
+
protected transformRelationFilter(_path: string, filter: FilterExpr): FilterExpr | Promise<FilterExpr>;
|
|
1208
|
+
/** The client relational-predicate gate (since 0.1.147) — one per controller. */
|
|
1209
|
+
private _relationGate;
|
|
1210
|
+
/**
|
|
1211
|
+
* The client filter with every relational predicate operand rewritten by
|
|
1212
|
+
* {@link transformRelationFilter} — and `parsed.controls.$with` replaced by
|
|
1213
|
+
* its rewritten tree (since 0.1.147). Costs nothing unless the hook is
|
|
1214
|
+
* overridden.
|
|
1215
|
+
*/
|
|
1216
|
+
private _relationOverlay;
|
|
803
1217
|
/**
|
|
804
1218
|
* The subset of the row-level action `names` the caller may run (since
|
|
805
1219
|
* 0.1.145) — what `$actions` and `GET /meta/actions/:id` list from. The
|
|
@@ -816,28 +1230,91 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
816
1230
|
/**
|
|
817
1231
|
* The rows the row-level action `actionName` may run on (since 0.1.145),
|
|
818
1232
|
* as an extra row filter; `undefined` or `{}` = no restriction (the
|
|
819
|
-
* default). Enforced by the action gate —
|
|
820
|
-
*
|
|
821
|
-
* same 404 "Row not found for action identifier"
|
|
822
|
-
* reflected in `$actions` and
|
|
823
|
-
* only on rows inside it.
|
|
1233
|
+
* 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"
|
|
1236
|
+
* as a missing one — and reflected in `$actions` and
|
|
1237
|
+
* `GET /meta/actions`, which list the action only on rows inside it.
|
|
1238
|
+
*
|
|
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).
|
|
824
1262
|
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
1263
|
+
* Not overriding it costs nothing; a one-parameter override keeps working.
|
|
1264
|
+
* moost-db always passes `ctx` — it is optional in the signature only so
|
|
1265
|
+
* `super.actionRowScope(name)` calls in existing overrides keep compiling.
|
|
1266
|
+
*
|
|
1267
|
+
* ```ts
|
|
1268
|
+
* protected async actionRowScope(action: string, ctx: TDbActionScopeContext) {
|
|
1269
|
+
* if (action !== "resolve") return undefined
|
|
1270
|
+
* const issues = await ctx.loadRows(["ticketKey"])
|
|
1271
|
+
* const tickets = await ticketTable.findMany({
|
|
1272
|
+
* filter: { key: { $in: issues.map((i) => i.ticketKey) }, teamId: currentTeamId() },
|
|
1273
|
+
* controls: { $select: ["key"] },
|
|
1274
|
+
* })
|
|
1275
|
+
* return { ticketKey: { $in: tickets.map((t) => t.key) } }
|
|
1276
|
+
* }
|
|
1277
|
+
* ```
|
|
1278
|
+
*
|
|
1279
|
+
* The scope may use relational predicates (since 0.1.147) — a server-side
|
|
1280
|
+
* filter, so no `@db.rel.filterable` opt-in and no
|
|
1281
|
+
* {@link transformRelationFilter} apply. On an Issue controller:
|
|
831
1282
|
*
|
|
832
1283
|
* ```ts
|
|
833
1284
|
* protected actionRowScope(action: string) {
|
|
834
|
-
* return action === "
|
|
1285
|
+
* return action === "resolve"
|
|
1286
|
+
* ? { ticket: { $some: { teamId: { $in: currentTeams() }, status: "open" } } }
|
|
1287
|
+
* : undefined
|
|
835
1288
|
* }
|
|
836
1289
|
* ```
|
|
837
1290
|
*
|
|
838
1291
|
* @since 0.1.145
|
|
839
1292
|
*/
|
|
840
|
-
protected actionRowScope(_actionName: string): FilterExpr | undefined | Promise<FilterExpr | undefined>;
|
|
1293
|
+
protected actionRowScope(_actionName: string, _ctx?: TDbActionScopeContext): FilterExpr | undefined | Promise<FilterExpr | undefined>;
|
|
1294
|
+
/**
|
|
1295
|
+
* The read scope a query target (an action request `{ query }`, since
|
|
1296
|
+
* 0.1.147) resolves under, on top of the action's row overlay — so "every
|
|
1297
|
+
* row matching the query" never reaches rows the caller cannot list. A
|
|
1298
|
+
* view resolving a delegated action's query target applies it too.
|
|
1299
|
+
* Default: `transformFilter({})` (the read overlay of `/query`). Throw an
|
|
1300
|
+
* `HttpError` to refuse query targets for the caller (e.g. no read grant).
|
|
1301
|
+
*
|
|
1302
|
+
* Runs as a READ of this controller: it is called in a child of the
|
|
1303
|
+
* action event — of the delegating event for a view resolving a delegated
|
|
1304
|
+
* target — (moost `withControllerContext`, the controller's `query`
|
|
1305
|
+
* handler) after `prepareRequest({ endpoint: "query", controls, filter })`
|
|
1306
|
+
* with the target's own filter and controls — so a permission layer's
|
|
1307
|
+
* per-request state is the READ request's (its read grant, the policy of
|
|
1308
|
+
* the relations its predicates touch), never the action's, and nothing of
|
|
1309
|
+
* it leaks back. The target's query (`q` filter and controls, `exclude`)
|
|
1310
|
+
* is validated in that read context ({@link validateControls},
|
|
1311
|
+
* {@link checkCapabilities}, {@link hasField}), where the client
|
|
1312
|
+
* predicates' {@link transformRelationFilter} also runs: a query target
|
|
1313
|
+
* never filters on, nor counts by, a field the caller can't read.
|
|
1314
|
+
*
|
|
1315
|
+
* @since 0.1.147
|
|
1316
|
+
*/
|
|
1317
|
+
protected queryTargetScope(_action: string): FilterExpr | undefined | Promise<FilterExpr | undefined>;
|
|
841
1318
|
/**
|
|
842
1319
|
* Transform projection before querying.
|
|
843
1320
|
* May return a Promise for async lookups.
|
|
@@ -866,6 +1343,13 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
866
1343
|
private _resolveProjectionForAugmenter;
|
|
867
1344
|
/** Row/rows envelopes narrowed to {@link allowedActions}; no hook call when neither it nor `applyMetaOverlay` is overridden. */
|
|
868
1345
|
private _resolveAugmentEnvelopes;
|
|
1346
|
+
/**
|
|
1347
|
+
* Once per class: `actionRowScope` is overridden (a permission layer always
|
|
1348
|
+
* does) while the controller's OWN row-level actions can't be scoped — the
|
|
1349
|
+
* readable has no identity, so `$actions` withholds every action with a
|
|
1350
|
+
* non-empty row scope. Silent for controllers without own row actions.
|
|
1351
|
+
*/
|
|
1352
|
+
private _warnScopeWithoutIdentity;
|
|
869
1353
|
/**
|
|
870
1354
|
* Returns a widened `$select` only when at least one `requiredFields` entry
|
|
871
1355
|
* is missing; `null` means "no widening needed". A field the request may
|
|
@@ -875,17 +1359,27 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
875
1359
|
private _widenSelectForActions;
|
|
876
1360
|
private _prepareAugmentation;
|
|
877
1361
|
/**
|
|
878
|
-
* {@link actionRowScope} of every
|
|
879
|
-
* `overlay`), grouped by filter object
|
|
880
|
-
*
|
|
1362
|
+
* {@link actionRowScope} of every action in `names` for one evaluation
|
|
1363
|
+
* (`ctx`, in parallel, alongside `overlay`), grouped by filter — object
|
|
1364
|
+
* identity first, then structural equality (`filterKey`), so equal filters
|
|
1365
|
+
* share one query; each group's filter is composed with the row overlay
|
|
1366
|
+
* exactly as the gate composes it.
|
|
881
1367
|
*/
|
|
882
1368
|
private _resolveActionScopeGroups;
|
|
883
|
-
/** Per scoped action, a per-row mask of rows outside its scope (one id-only read per group). */
|
|
884
|
-
private _outOfScopeMasks;
|
|
885
1369
|
/**
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
1370
|
+
* Per action of `names`, a per-row mask (parallel to `rows`) of rows
|
|
1371
|
+
* outside its {@link actionRowScope} — one id-only read per distinct
|
|
1372
|
+
* filter. The hook sees the rows' identities (`purpose`); a row lacking
|
|
1373
|
+
* its identity is in no scope. No candidate → no hook call, every row
|
|
1374
|
+
* masked for every action (fail closed). `undefined` when the hook is not
|
|
1375
|
+
* overridden or `rows` is empty.
|
|
1376
|
+
*/
|
|
1377
|
+
private _scopeMasks;
|
|
1378
|
+
/**
|
|
1379
|
+
* The row filter {@link actionRowScope} returns for `action` and the
|
|
1380
|
+
* candidates of `ctx` — `undefined` when empty or when the hook is not
|
|
1381
|
+
* overridden (no call). THE per-action scope rule: the action gate,
|
|
1382
|
+
* `$actions` and `/meta/actions` all read it here.
|
|
889
1383
|
*/
|
|
890
1384
|
private _actionScope;
|
|
891
1385
|
/**
|
|
@@ -894,12 +1388,35 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
894
1388
|
*/
|
|
895
1389
|
private _gateFields;
|
|
896
1390
|
/**
|
|
897
|
-
* @internal The action gate's overlay (reached by the `@DbAction`
|
|
898
|
-
* interceptor through {@link ACTION_OVERLAY}): {@link rowOverlay}
|
|
899
|
-
* action's {@link actionRowScope}
|
|
900
|
-
*
|
|
1391
|
+
* @internal The action gate's row overlay (reached by the `@DbAction`
|
|
1392
|
+
* interceptor through {@link ACTION_OVERLAY}): {@link rowOverlay}. Since
|
|
1393
|
+
* 0.1.147 the action's {@link actionRowScope} is applied separately to the
|
|
1394
|
+
* loaded candidates ({@link ACTION_SCOPE}).
|
|
1395
|
+
*/
|
|
1396
|
+
[ACTION_OVERLAY](): Promise<FilterExpr | undefined>;
|
|
1397
|
+
/** @internal {@link actionRowScope} for the gate's loaded candidates (since 0.1.147). */
|
|
1398
|
+
[ACTION_SCOPE](action: string, ctx: TDbActionScopeContext): Promise<FilterExpr | undefined>;
|
|
1399
|
+
/** @internal `true` when {@link actionRowScope} is overridden (since 0.1.147). */
|
|
1400
|
+
get [ACTION_SCOPED](): boolean;
|
|
1401
|
+
/**
|
|
1402
|
+
* @internal Resolves a query target (phase 1, since 0.1.147): the `query`
|
|
1403
|
+
* body validated (shape, `$search` / `$index` only, the read's capability
|
|
1404
|
+
* and index gate), then ONE read of `select` ordered by identity —
|
|
1405
|
+
* `filter (+ $search) ∧ overlay ∧ queryTargetScope ∧ ¬exclude`, at most
|
|
1406
|
+
* `cap + 1` rows. More than the cap → 400 `TARGET_TOO_LARGE`; a count
|
|
1407
|
+
* other than `expectCount` → 409 `TARGET_CHANGED`. `load` re-reads rows
|
|
1408
|
+
* of the snapshot that still match the same target (phase 2) — except for
|
|
1409
|
+
* the first batch, which directly follows the snapshot.
|
|
901
1410
|
*/
|
|
902
|
-
[
|
|
1411
|
+
[RESOLVE_TARGET](req: TTargetRequest): Promise<TResolvedTarget>;
|
|
1412
|
+
/**
|
|
1413
|
+
* Runs `fn` as a READ of this controller (since 0.1.147): in a child of
|
|
1414
|
+
* the current event whose controller context is this controller's `query`
|
|
1415
|
+
* handler, after `prepareRequest({ endpoint: "query", controls, filter })` — the
|
|
1416
|
+
* request-scoped state a permission layer builds there (read grant, field
|
|
1417
|
+
* visibility) is the read's and stays in the child.
|
|
1418
|
+
*/
|
|
1419
|
+
private _asRead;
|
|
903
1420
|
/**
|
|
904
1421
|
* `@db.column.searchable` paths, minus anything the adapter can't filter
|
|
905
1422
|
* (JSON storage, encrypted), `@db.writeOnly` fields and navigation
|
|
@@ -980,8 +1497,65 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
980
1497
|
* promise or microtask, when there is no hook or it is synchronous.
|
|
981
1498
|
*/
|
|
982
1499
|
private _finishRows;
|
|
983
|
-
/**
|
|
1500
|
+
/**
|
|
1501
|
+
* `$actions` augmentation (when `prep`) — own actions, then the delegated
|
|
1502
|
+
* ones (`delegated`: per delegation, per row) — then {@link decorateRows}
|
|
1503
|
+
* when implemented.
|
|
1504
|
+
*/
|
|
984
1505
|
private _augmentAndDecorate;
|
|
1506
|
+
/**
|
|
1507
|
+
* The app of the current event, through DI — never the one this
|
|
1508
|
+
* (singleton) instance was constructed in, which may be gone (a re-booted
|
|
1509
|
+
* app, a hot reload).
|
|
1510
|
+
*/
|
|
1511
|
+
private _currentApp;
|
|
1512
|
+
/** The class's `@DbActionsFrom` delegations, validated on first use (per app). */
|
|
1513
|
+
private _delegations;
|
|
1514
|
+
/**
|
|
1515
|
+
* The delegations this request may use: every id path visible
|
|
1516
|
+
* ({@link hasField}) and kept by {@link transformProjection} — a
|
|
1517
|
+
* delegation whose ids the request can't read is dropped.
|
|
1518
|
+
*/
|
|
1519
|
+
private _activeDelegations;
|
|
1520
|
+
/**
|
|
1521
|
+
* Runs `fn` on the delegation's source controller (this event's instance)
|
|
1522
|
+
* evaluated as itself ({@link runAsController}); a 401 / 403 from its
|
|
1523
|
+
* `prepareRequest` (the caller holds no grant there) yields `refused`.
|
|
1524
|
+
*/
|
|
1525
|
+
private _onSource;
|
|
1526
|
+
/** Per row, the source's verdict for the row it maps to (`undefined`: no source id). */
|
|
1527
|
+
private _delegatedRowVerdicts;
|
|
1528
|
+
/** The `/meta.actions` entries of the delegations the caller may run (per its source). */
|
|
1529
|
+
private _delegatedInfos;
|
|
1530
|
+
/**
|
|
1531
|
+
* The cached `/meta` envelope through {@link applyMetaOverlay}, plus —
|
|
1532
|
+
* since 0.1.147 — the `@DbActionsFrom` actions the caller may run as their
|
|
1533
|
+
* source decides (`allowedActions` of the source, evaluated as itself) and,
|
|
1534
|
+
* under an overridden {@link hasField}, the search surface narrowed to the
|
|
1535
|
+
* indexes the request may use (the index gate's rule). Delegated entries
|
|
1536
|
+
* never pass this controller's own `applyMetaOverlay`.
|
|
1537
|
+
*/
|
|
1538
|
+
protected resolveMeta(): TMetaResponse | Promise<TMetaResponse>;
|
|
1539
|
+
/**
|
|
1540
|
+
* The delegated part of `GET /meta/actions…` for the source ids `sourceIdOf`
|
|
1541
|
+
* derives from the request (`undefined`: not derivable by key renaming —
|
|
1542
|
+
* the delegation is left out).
|
|
1543
|
+
*/
|
|
1544
|
+
private _delegatedAvailable;
|
|
1545
|
+
/**
|
|
1546
|
+
* @internal Source side of a delegation: the `$actions` verdicts of `names`
|
|
1547
|
+
* for `ids` (aligned; `undefined` = not found under the row overlay), as
|
|
1548
|
+
* this controller's own `$actions` / `GET /meta/actions` compute them —
|
|
1549
|
+
* its `prepareRequest("availableActions")`, `allowedActions`, row overlay,
|
|
1550
|
+
* `actionRowScope` (`purpose: "rows"`) and `disabled`.
|
|
1551
|
+
*/
|
|
1552
|
+
[ACTION_VERDICTS](ids: Record<string, unknown>[], names: readonly string[]): Promise<Array<TDbAvailableActions$1 | undefined>>;
|
|
1553
|
+
/** @internal Source side of a delegation: `GET /meta/actions` for one id, `names` only. */
|
|
1554
|
+
[AVAILABLE_ACTIONS](id: Record<string, unknown>, names: readonly string[]): Promise<TDbAvailableActions$1>;
|
|
1555
|
+
/** @internal Source side of a delegation: the `names` the caller may run (`allowedActions`). */
|
|
1556
|
+
[ALLOWED_ACTIONS](names: readonly string[]): Promise<readonly string[]>;
|
|
1557
|
+
/** {@link _resolveAugmentEnvelopes} restricted to `names`. */
|
|
1558
|
+
private _envelopesNamed;
|
|
985
1559
|
/**
|
|
986
1560
|
* Shared `query` / `pages` / `geo` pipeline: prepare actions augmentation + read
|
|
987
1561
|
* strategy in parallel, pre-widen $select for `requiredFields`, run
|
|
@@ -1087,18 +1661,75 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
1087
1661
|
* `{ actions: [] }`. {@link prepareRequest} runs first with
|
|
1088
1662
|
* `endpoint: "availableActions"`.
|
|
1089
1663
|
*/
|
|
1090
|
-
availableActionsById(id: string): Promise<TDbAvailableActions>;
|
|
1664
|
+
availableActionsById(id: string): Promise<TDbAvailableActions$1>;
|
|
1091
1665
|
/**
|
|
1092
1666
|
* **GET /meta/actions?field1=val1&…** — {@link availableActionsById} by
|
|
1093
1667
|
* composite key (composite primary key or compound unique index), the
|
|
1094
1668
|
* `/one?…` rules.
|
|
1095
1669
|
*/
|
|
1096
|
-
availableActions(query: Record<string, string>): Promise<TDbAvailableActions | HttpError>;
|
|
1670
|
+
availableActions(query: Record<string, string>): Promise<TDbAvailableActions$1 | HttpError>;
|
|
1671
|
+
/**
|
|
1672
|
+
* **POST /delegated-actions/:name** — a query target for a `@DbActionsFrom`
|
|
1673
|
+
* action whose source action declares `queryTarget` (since 0.1.147); the
|
|
1674
|
+
* action's `/meta` entry points here (`queryTarget.url`). Body
|
|
1675
|
+
* `{ query: { q, exclude?, expectCount?, maxRows?, dryRun? }, input? }`.
|
|
1676
|
+
*
|
|
1677
|
+
* The source must list the action for the caller (its `allowedActions`,
|
|
1678
|
+
* as `$actions` does) — else 403, dry runs included. This controller then
|
|
1679
|
+
* resolves the rows matching `q` under its own read scope (`transformFilter`
|
|
1680
|
+
* ∧ {@link queryTargetScope} ∧ ¬`exclude` — `exclude` entries use this
|
|
1681
|
+
* controller's identifications or the delegation's id paths, and the
|
|
1682
|
+
* source rows they map to are left out even when other view rows map to
|
|
1683
|
+
* them too), maps them to source ids (a row without one is skipped as
|
|
1684
|
+
* `"unmapped"`), then runs the SOURCE's action route on them in batches
|
|
1685
|
+
* inside this request (`MoostHttp.invoke`) — its guards, `prepareRequest`,
|
|
1686
|
+
* row overlay, `actionRowScope` and `disabled` re-check every batch, and
|
|
1687
|
+
* every body reader of the source sees the batch's `{ ids, input }`. Before
|
|
1688
|
+
* each batch the view rows are re-checked against the target: a source id
|
|
1689
|
+
* none of whose view rows still matches is skipped as `"stale"`; ids the
|
|
1690
|
+
* source's gate refuses are skipped with their reasons. A batch failing
|
|
1691
|
+
* once a source handler started (in it or an earlier batch) stops the run:
|
|
1692
|
+
* the answer is the partial summary with `aborted` and every id not run
|
|
1693
|
+
* listed in `failed`. A failure before any source handler started (a 403,
|
|
1694
|
+
* the source's `@InputForm` 400, …) is the request's error — nothing ran,
|
|
1695
|
+
* and every batch carries the same `input`. The
|
|
1696
|
+
* `message` (string) each batch's handler returned is passed on:
|
|
1697
|
+
* `messages` per batch, `message` the distinct ones joined by newlines. A
|
|
1698
|
+
* dry run answers `{ matched }`; otherwise the answer is the run's
|
|
1699
|
+
* {@link TDbActionTargetSummary}. An action of the delegation that takes no
|
|
1700
|
+
* query target answers 400 `TARGET_INVALID`. `prepareRequest` runs first
|
|
1701
|
+
* with `endpoint: "delegatedAction"`. Registered only on controllers
|
|
1702
|
+
* declaring `@DbActionsFrom`.
|
|
1703
|
+
*/
|
|
1704
|
+
runDelegatedOnQuery(): Promise<TDbActionTargetSummary$1 | {
|
|
1705
|
+
matched: number;
|
|
1706
|
+
}>;
|
|
1707
|
+
/**
|
|
1708
|
+
* The id keys (`idMap` fields) of the source rows `exclude` leaves out of a
|
|
1709
|
+
* delegated target — an entry by the id paths names its source row
|
|
1710
|
+
* directly; one by a view identification names the source row of that view
|
|
1711
|
+
* row (read without overlay: excluding can only narrow the run).
|
|
1712
|
+
*/
|
|
1713
|
+
private _excludedSourceKeys;
|
|
1714
|
+
/**
|
|
1715
|
+
* The source ids of `batch` that some view row still maps to under the
|
|
1716
|
+
* target's query (phase-2 re-check); the others are recorded in
|
|
1717
|
+
* `summary.skipped` as `"stale"`.
|
|
1718
|
+
*/
|
|
1719
|
+
private _stillTargeted;
|
|
1097
1720
|
/**
|
|
1098
1721
|
* The row resolves ONCE, like `/one/:id` under {@link rowOverlay}; scoped
|
|
1099
|
-
* actions are then checked on it exactly like
|
|
1722
|
+
* actions are then checked on it (`purpose: "available"`) exactly like
|
|
1723
|
+
* `$actions` rows.
|
|
1100
1724
|
*/
|
|
1101
1725
|
private _availableActions;
|
|
1726
|
+
/**
|
|
1727
|
+
* Per row (`undefined` = not found → no verdict): the actions of
|
|
1728
|
+
* `envelopes` it lists — minus those `masks` put it outside of, and those
|
|
1729
|
+
* whose `disabled` rule refuses it on the fields its gate loads
|
|
1730
|
+
* (`fieldsOf`, parallel to `envelopes`) — with the refusal reasons.
|
|
1731
|
+
*/
|
|
1732
|
+
private _verdicts;
|
|
1102
1733
|
/**
|
|
1103
1734
|
* **GET /meta** — returns table/view metadata for UI.
|
|
1104
1735
|
*
|
|
@@ -1530,166 +2161,6 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
|
|
|
1530
2161
|
private normalizeSort;
|
|
1531
2162
|
}
|
|
1532
2163
|
//#endregion
|
|
1533
|
-
//#region src/actions/types.d.ts
|
|
1534
|
-
/**
|
|
1535
|
-
* One entry of a `disabled` predicate's result. Truthy = the action is
|
|
1536
|
-
* disabled for that row; falsy (`false`, `""`) = enabled. A non-empty string
|
|
1537
|
-
* disables the action AND carries a human-readable reason — surfaced in the
|
|
1538
|
-
* 409 `ActionDisabledError` message/body and in the row's `$disabledReasons`.
|
|
1539
|
-
*
|
|
1540
|
-
* @since 0.1.141 (`string`)
|
|
1541
|
-
*/
|
|
1542
|
-
type TDbActionDisabledVerdict = boolean | string;
|
|
1543
|
-
/** `'rows'`-level batch policy — controls whether failing rows reject or are filtered out. */
|
|
1544
|
-
type TOnDisabledRows = "reject" | "skip";
|
|
1545
|
-
/**
|
|
1546
|
-
* Dot-notation field paths of `TRow`'s flat type. Drives both the runtime
|
|
1547
|
-
* projection widening and the type narrowing of the `disabled` predicate's
|
|
1548
|
-
* row argument. Relations are absent from `FlatOf<T>` — listing a relation
|
|
1549
|
-
* field is therefore a compile error.
|
|
1550
|
-
*
|
|
1551
|
-
* Permissive fallback when `TRow = unknown` (no explicit decorator generic):
|
|
1552
|
-
* any string is allowed and the `disabled` predicate's row arg is `any[]`,
|
|
1553
|
-
* preserving the prior loose typing for un-annotated call sites.
|
|
1554
|
-
*/
|
|
1555
|
-
type FlatKey<TRow> = unknown extends TRow ? string : keyof FlatOf<TRow> & string;
|
|
1556
|
-
/** Row-shape narrowing for the `disabled` predicate. Falls back to `any` when `TRow = unknown`. */
|
|
1557
|
-
type DisabledRowsArg<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? any[] : Pick<FlatOf<TRow>, R[number] & keyof FlatOf<TRow>>[];
|
|
1558
|
-
interface NoGate {
|
|
1559
|
-
requiredFields?: never;
|
|
1560
|
-
disabled?: never;
|
|
1561
|
-
onDisabledRows?: never;
|
|
1562
|
-
}
|
|
1563
|
-
interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
1564
|
-
/**
|
|
1565
|
-
* Dot-notation field paths the predicate references. SERVER-INTERNAL —
|
|
1566
|
-
* never emitted on the `/meta` wire. Consumed verbatim to widen the DB
|
|
1567
|
-
* projection so `disabled` always sees the fields it declared.
|
|
1568
|
-
*/
|
|
1569
|
-
requiredFields: R;
|
|
1570
|
-
/**
|
|
1571
|
-
* Sync batch gate predicate — returns a parallel array aligned with the
|
|
1572
|
-
* input. Per entry: truthy = disabled for the corresponding row; a
|
|
1573
|
-
* non-empty string also gives the reason (see
|
|
1574
|
-
* {@link TDbActionDisabledVerdict}). The `rows`
|
|
1575
|
-
* argument is type-narrowed to `Pick<FlatOf<TRow>, R[number]>[]`; reading
|
|
1576
|
-
* a field not listed in `requiredFields` is a compile error.
|
|
1577
|
-
*
|
|
1578
|
-
* Promise return is NOT permitted — the predicate is consumed in the
|
|
1579
|
-
* same tick by the gate and the augmenter.
|
|
1580
|
-
*/
|
|
1581
|
-
disabled?: (rows: DisabledRowsArg<TRow, R>) => TDbActionDisabledVerdict[];
|
|
1582
|
-
/**
|
|
1583
|
-
* `'rows'`-level batch policy. Default `'reject'`.
|
|
1584
|
-
*
|
|
1585
|
-
* - `'reject'`: evaluate every row before throwing; if any row fails, the
|
|
1586
|
-
* error body lists ALL failing IDs; handler not invoked.
|
|
1587
|
-
* - `'skip'`: filter cached rows + cached IDs to passing-only; zero
|
|
1588
|
-
* survivors → reject. Handler runs against the survivors.
|
|
1589
|
-
*
|
|
1590
|
-
* Ignored for `'row'` and `'table'` level actions.
|
|
1591
|
-
*/
|
|
1592
|
-
onDisabledRows?: TOnDisabledRows;
|
|
1593
|
-
}
|
|
1594
|
-
/**
|
|
1595
|
-
* Loose gate shape used when `TRow = unknown` (no explicit decorator generic).
|
|
1596
|
-
* Preserves the prior un-typed call-site flexibility; the runtime still drops
|
|
1597
|
-
* actions where `disabled` is set without `requiredFields`.
|
|
1598
|
-
*/
|
|
1599
|
-
interface LooseGate {
|
|
1600
|
-
requiredFields?: string[];
|
|
1601
|
-
disabled?: (rows: any[]) => TDbActionDisabledVerdict[];
|
|
1602
|
-
onDisabledRows?: TOnDisabledRows;
|
|
1603
|
-
}
|
|
1604
|
-
type GateOpts<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? LooseGate : NoGate | WithGate<TRow, R>;
|
|
1605
|
-
interface BaseActionOpts extends Partial<Omit<TDbActionInfo$1, "name" | "level" | "processor" | "value" | "disabled" | "formUrl">> {
|
|
1606
|
-
/**
|
|
1607
|
-
* Bound table reference. REQUIRED on non-`AsDbReadableController` classes
|
|
1608
|
-
* when `disabled` is set OR a `@DbActionRow*` parameter is declared.
|
|
1609
|
-
*
|
|
1610
|
-
* Silently ignored on `AsDbReadableController` subclasses (which include
|
|
1611
|
-
* `AsDbController`) — the bound table from the controller wins.
|
|
1612
|
-
*/
|
|
1613
|
-
table?: AtscriptDbTable<any>;
|
|
1614
|
-
/**
|
|
1615
|
-
* `'rows'` level (`@DbActionIDs` / `@DbActionRows`): the most identifiers
|
|
1616
|
-
* one request may carry. Above it the request is rejected with 400 before
|
|
1617
|
-
* any row is loaded. Default `1000`. Server-internal — never on the wire.
|
|
1618
|
-
*
|
|
1619
|
-
* @since 0.1.143
|
|
1620
|
-
*/
|
|
1621
|
-
maxIds?: number;
|
|
1622
|
-
}
|
|
1623
|
-
/**
|
|
1624
|
-
* Options accepted by `@DbAction(name, opts?)`. Generic over `TRow` (the
|
|
1625
|
-
* controller's bound atscript type) and `R` (the literal `requiredFields`
|
|
1626
|
-
* tuple). Both are inferred at the call site via the decorator's `<TRow>`
|
|
1627
|
-
* argument plus `const R` generic.
|
|
1628
|
-
*/
|
|
1629
|
-
type DbActionOpts<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = BaseActionOpts & GateOpts<TRow, R>;
|
|
1630
|
-
interface DbActionsEntryCommonBase {
|
|
1631
|
-
label: string;
|
|
1632
|
-
level: TDbActionLevel$1;
|
|
1633
|
-
icon?: string;
|
|
1634
|
-
intent?: TDbActionIntent$1;
|
|
1635
|
-
description?: string;
|
|
1636
|
-
order?: number;
|
|
1637
|
-
default?: boolean;
|
|
1638
|
-
/** Mirrors {@link TDbActionInfo.promptText} — singular/plural via tuple. */
|
|
1639
|
-
promptText?: string | [string, string];
|
|
1640
|
-
/** Mirrors {@link TDbActionInfo.shortcut} — single-character UI hint. */
|
|
1641
|
-
shortcut?: string;
|
|
1642
|
-
/**
|
|
1643
|
-
* Input form the UI collects before invoking the action:
|
|
1644
|
-
*
|
|
1645
|
-
* - a compiled `.as` interface — registered on THIS controller and served
|
|
1646
|
-
* by its own `GET /meta/form/:name`; the wire carries `inputForm: Type.name`.
|
|
1647
|
-
* - `{ name, url }` — a form served elsewhere: `name` goes on the wire as
|
|
1648
|
-
* `inputForm`, `url` (server-absolute path of the serialized schema, e.g.
|
|
1649
|
-
* `"/api/shipping/meta/form/ShipForm"`) as {@link TDbActionInfo.formUrl}.
|
|
1650
|
-
*
|
|
1651
|
-
* Not allowed with `processor: 'navigate'`. Class-level entries only
|
|
1652
|
-
* describe the action — validating `input` is the target handler's job
|
|
1653
|
-
* (e.g. its own `@InputForm(Type)` param).
|
|
1654
|
-
*
|
|
1655
|
-
* @since 0.1.136
|
|
1656
|
-
*/
|
|
1657
|
-
inputForm?: TAtscriptAnnotatedType | {
|
|
1658
|
-
name: string;
|
|
1659
|
-
url: string;
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
|
-
type DbActionsEntryWithGate<TRow, R extends readonly FlatKey<TRow>[]> = DbActionsEntryCommonBase & GateOpts<TRow, R>;
|
|
1663
|
-
/**
|
|
1664
|
-
* Class-level dict entry. `value` semantics by processor:
|
|
1665
|
-
*
|
|
1666
|
-
* - `'navigate'` — REQUIRED, non-empty. URL template (`$1` substituted client-side).
|
|
1667
|
-
* - `'backend'` — REQUIRED, non-empty. Full HTTP POST path the UI client invokes.
|
|
1668
|
-
* - `'custom'` — `value` is forbidden in the entry; the meta builder fills it
|
|
1669
|
-
* with the dict key.
|
|
1670
|
-
*/
|
|
1671
|
-
type TDbActionsEntry<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = (DbActionsEntryWithGate<TRow, R> & {
|
|
1672
|
-
processor: "navigate";
|
|
1673
|
-
value: string;
|
|
1674
|
-
inputForm?: never;
|
|
1675
|
-
}) | (DbActionsEntryWithGate<TRow, R> & {
|
|
1676
|
-
processor: "custom";
|
|
1677
|
-
value?: never;
|
|
1678
|
-
}) | (DbActionsEntryWithGate<TRow, R> & {
|
|
1679
|
-
processor: "backend";
|
|
1680
|
-
value: string;
|
|
1681
|
-
});
|
|
1682
|
-
/** Distributes `Omit` across the discriminated union members. */
|
|
1683
|
-
type DistributiveOmit<T, K extends keyof T> = T extends T ? Omit<T, K> : never;
|
|
1684
|
-
/** Same as {@link TDbActionsEntry} but without the `level` field — used by the level-pinned shortcuts. */
|
|
1685
|
-
type TDbActionsEntryUnpinned<TRow = unknown, R extends readonly FlatKey<TRow>[] = []> = DistributiveOmit<TDbActionsEntry<TRow, R>, "level">;
|
|
1686
|
-
type DbActionsDictBase = Record<string, unknown>;
|
|
1687
|
-
type EntryRequiredFields<E, TRow> = E extends {
|
|
1688
|
-
requiredFields: infer R;
|
|
1689
|
-
} ? R extends readonly FlatKey<TRow>[] ? R : [] : [];
|
|
1690
|
-
type ValidatedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntry<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
1691
|
-
type ValidatedUnpinnedDict<TRow, D extends DbActionsDictBase> = { [K in keyof D]: TDbActionsEntryUnpinned<TRow, EntryRequiredFields<D[K], TRow>> };
|
|
1692
|
-
//#endregion
|
|
1693
2164
|
//#region src/actions/keys.d.ts
|
|
1694
2165
|
type TDbActionRowMarker = true;
|
|
1695
2166
|
/** Stamped by `@InputForm(FormType)` — the compiled `.as` class + the wire name (`FormType.name`). */
|
|
@@ -1707,6 +2178,18 @@ interface TDbClassActionMeta {
|
|
|
1707
2178
|
name: string;
|
|
1708
2179
|
entry: TDbActionsEntry;
|
|
1709
2180
|
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Class-level entry written by `@DbActionsFrom(source, opts)` (since
|
|
2183
|
+
* 0.1.147): a controller whose row-level actions this controller delegates.
|
|
2184
|
+
*/
|
|
2185
|
+
interface TDbActionsFromMeta {
|
|
2186
|
+
/** Lazy reference to the source controller class. */
|
|
2187
|
+
source: () => Function;
|
|
2188
|
+
/** Source identification field → path in this controller's rows. */
|
|
2189
|
+
idMap?: Record<string, string>;
|
|
2190
|
+
/** Subset of the source's row / rows-level action names (default: all). */
|
|
2191
|
+
actions?: readonly string[];
|
|
2192
|
+
}
|
|
1710
2193
|
/** Param marker kind — informs level inference and ID-resolution shape. */
|
|
1711
2194
|
type TDbActionParamKind = "id" | "ids";
|
|
1712
2195
|
declare module "moost" {
|
|
@@ -1717,12 +2200,14 @@ declare module "moost" {
|
|
|
1717
2200
|
atscript_db_action_row?: TDbActionRowMarker;
|
|
1718
2201
|
atscript_db_action_rows?: TDbActionRowMarker;
|
|
1719
2202
|
atscript_db_endpoint?: TDbRequestEndpoint;
|
|
2203
|
+
atscript_db_actions_from?: TDbActionsFromMeta[];
|
|
1720
2204
|
}
|
|
1721
2205
|
interface TMoostParamsMetadata {
|
|
1722
2206
|
atscript_db_action_param?: TDbActionParamKind;
|
|
1723
2207
|
atscript_db_action_row?: TDbActionRowMarker;
|
|
1724
2208
|
atscript_db_action_rows?: TDbActionRowMarker;
|
|
1725
2209
|
atscript_db_action_input_form?: TDbActionInputFormMeta;
|
|
2210
|
+
atscript_db_action_target?: true;
|
|
1726
2211
|
atscript_type?: TAtscriptAnnotatedType;
|
|
1727
2212
|
}
|
|
1728
2213
|
}
|
|
@@ -1775,6 +2260,8 @@ interface AtscriptDbMeta {
|
|
|
1775
2260
|
* {@link getDbEndpoint}.
|
|
1776
2261
|
*/
|
|
1777
2262
|
atscript_db_endpoint?: TDbRequestEndpoint;
|
|
2263
|
+
/** Class-level — written by `@DbActionsFrom(...)` (since 0.1.147). Decorators accumulate. */
|
|
2264
|
+
atscript_db_actions_from?: TDbActionsFromMeta[];
|
|
1778
2265
|
}
|
|
1779
2266
|
/**
|
|
1780
2267
|
* Param-level metadata written by `@atscript/moost-db`'s param
|
|
@@ -1788,6 +2275,8 @@ interface AtscriptDbParamsMeta {
|
|
|
1788
2275
|
atscript_db_action_row?: true;
|
|
1789
2276
|
/** Param-level marker — written by `@DbActionRows()`. */
|
|
1790
2277
|
atscript_db_action_rows?: true;
|
|
2278
|
+
/** Param-level marker — written by `@DbActionTarget()` (since 0.1.147). */
|
|
2279
|
+
atscript_db_action_target?: true;
|
|
1791
2280
|
/**
|
|
1792
2281
|
* Param-level — written by `@InputForm(FormType)`. Carries the
|
|
1793
2282
|
* compiled `.as` class plus its `.name` so `discoverActions` can both
|
|
@@ -2051,7 +2540,8 @@ declare const UseValidationErrorTransform: () => ClassDecorator & MethodDecorato
|
|
|
2051
2540
|
/**
|
|
2052
2541
|
* Mark a controller method as a database action surfaced via `/meta`. Writes
|
|
2053
2542
|
* `atscript_db_action` metadata and, for every `'row'` / `'rows'` action,
|
|
2054
|
-
* registers a Moost interceptor: the gate
|
|
2543
|
+
* registers a Moost interceptor: the batch gate for a `@DbActionTarget()`
|
|
2544
|
+
* handler (since 0.1.147), the gate when `disabled` is set, else the
|
|
2055
2545
|
* bound-table injector that also verifies the ids against the controller's
|
|
2056
2546
|
* row overlay (since 0.1.143). Either first awaits the controller's
|
|
2057
2547
|
* `prepareRequest({ endpoint: "action", action })` when it defines one —
|
|
@@ -2220,28 +2710,6 @@ declare function InputForm<T extends TAtscriptAnnotatedType & {
|
|
|
2220
2710
|
readonly name: string;
|
|
2221
2711
|
}>(formType?: T, validatorOpts?: Partial<TValidatorOptions>): ParameterDecorator;
|
|
2222
2712
|
//#endregion
|
|
2223
|
-
//#region src/actions/discover.d.ts
|
|
2224
|
-
/**
|
|
2225
|
-
* Pairs the wire-shaped `info` with the original decorator opts / dict entry,
|
|
2226
|
-
* so the augmenter can invoke the live `disabled` reference (deliberately
|
|
2227
|
-
* absent from the wire `info`).
|
|
2228
|
-
*/
|
|
2229
|
-
/** A discovered action: its `/meta.actions[]` entry plus the server-internal opts it was declared with. */
|
|
2230
|
-
interface TDbActionEnvelope {
|
|
2231
|
-
info: TDbActionInfo$1;
|
|
2232
|
-
raw: DbActionOpts | TDbActionsEntry;
|
|
2233
|
-
}
|
|
2234
|
-
/** Lookup helper for `AsReadableController.metaForm()`. */
|
|
2235
|
-
declare function getControllerFormType(ctor: Function, name: string): TAtscriptAnnotatedType | undefined;
|
|
2236
|
-
/** Discover actions on a controller, memoized per ctor. `info`-only callers map `e => e.info`. */
|
|
2237
|
-
declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
2238
|
-
/**
|
|
2239
|
-
* The `'row'` / `'rows'`-level subset of {@link discoverActions} (since
|
|
2240
|
-
* 0.1.145 public) — the actions `$actions` and `GET /meta/actions/:id`
|
|
2241
|
-
* consider, in `/meta.actions` order. Memoized per controller class.
|
|
2242
|
-
*/
|
|
2243
|
-
declare function discoverRowLevelActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
2244
|
-
//#endregion
|
|
2245
2713
|
//#region src/actions/id-cache.d.ts
|
|
2246
2714
|
declare const useDbActionId: import("@wooksjs/event-core").WookComposable<{
|
|
2247
2715
|
load: () => Promise<Record<string, unknown>>;
|
|
@@ -2267,10 +2735,15 @@ declare const useDbActionRows: import("@wooksjs/event-core").WookComposable<{
|
|
|
2267
2735
|
* absent (`'table'`-level).
|
|
2268
2736
|
* - `input` — present only when the action declares an `@InputForm()`
|
|
2269
2737
|
* parameter; carries the form payload the user filled out.
|
|
2738
|
+
* - `query` — a query target instead of `ids` (since 0.1.147): "every row
|
|
2739
|
+
* matching this query", for `'rows'` actions declaring `queryTarget` — see
|
|
2740
|
+
* {@link TDbActionQueryTarget}. Never together with `ids`.
|
|
2270
2741
|
*/
|
|
2271
2742
|
interface DbActionEnvelope {
|
|
2272
2743
|
ids?: unknown;
|
|
2273
2744
|
input?: unknown;
|
|
2745
|
+
/** @since 0.1.147 — validated by the action's gate (`TDbActionQueryTarget`). */
|
|
2746
|
+
query?: unknown;
|
|
2274
2747
|
}
|
|
2275
2748
|
/**
|
|
2276
2749
|
* Cached parse of the action request body. Centralises the shape check so
|
|
@@ -2339,6 +2812,165 @@ declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
|
|
|
2339
2812
|
constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[], reasons?: readonly (string | null | undefined)[]);
|
|
2340
2813
|
}
|
|
2341
2814
|
//#endregion
|
|
2815
|
+
//#region src/actions/action-target-error.d.ts
|
|
2816
|
+
/**
|
|
2817
|
+
* Why a query-targeted action request was refused:
|
|
2818
|
+
*
|
|
2819
|
+
* - `TARGET_INVALID` (400) — malformed `query` (unknown key, a control other
|
|
2820
|
+
* than `$search` / `$index`, `ids` and `query` together, an action that
|
|
2821
|
+
* accepts no query target);
|
|
2822
|
+
* - `TARGET_TOO_LARGE` (400) — more rows match than the cap (`cap`);
|
|
2823
|
+
* - `TARGET_CHANGED` (409) — the match count differs from `expectCount`
|
|
2824
|
+
* (`matched` carries the current count).
|
|
2825
|
+
*
|
|
2826
|
+
* @since 0.1.147
|
|
2827
|
+
*/
|
|
2828
|
+
type TActionTargetErrorCode = "TARGET_INVALID" | "TARGET_TOO_LARGE" | "TARGET_CHANGED";
|
|
2829
|
+
/**
|
|
2830
|
+
* Wire body of {@link ActionTargetError} — the moost `ServerError` envelope
|
|
2831
|
+
* plus the `name` discriminator `@atscript/db-client` maps to its typed error.
|
|
2832
|
+
*
|
|
2833
|
+
* @since 0.1.147
|
|
2834
|
+
*/
|
|
2835
|
+
interface ActionTargetErrorBody {
|
|
2836
|
+
name: "ActionTargetError";
|
|
2837
|
+
message: string;
|
|
2838
|
+
statusCode: 400 | 409;
|
|
2839
|
+
code: TActionTargetErrorCode;
|
|
2840
|
+
action: string;
|
|
2841
|
+
/** `TARGET_CHANGED`: the current match count. */
|
|
2842
|
+
matched?: number;
|
|
2843
|
+
/** `TARGET_TOO_LARGE`: the most rows one request may target. */
|
|
2844
|
+
cap?: number;
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* A refused query target (400 / 409) — see {@link TActionTargetErrorCode}.
|
|
2848
|
+
*
|
|
2849
|
+
* @since 0.1.147
|
|
2850
|
+
*/
|
|
2851
|
+
declare class ActionTargetError extends HttpError<ActionTargetErrorBody> {
|
|
2852
|
+
name: string;
|
|
2853
|
+
constructor(code: TActionTargetErrorCode, action: string, message: string, extra?: {
|
|
2854
|
+
matched?: number;
|
|
2855
|
+
cap?: number;
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
//#endregion
|
|
2859
|
+
//#region src/actions/target.d.ts
|
|
2860
|
+
/**
|
|
2861
|
+
* The rows a `'rows'` action runs on, as the handler surface of
|
|
2862
|
+
* `@DbActionTarget()` / {@link useDbActionTarget} (since 0.1.147) — the same
|
|
2863
|
+
* for an id target (`{ ids }`) and a query target (`{ query }`).
|
|
2864
|
+
*
|
|
2865
|
+
* @since 0.1.147
|
|
2866
|
+
*/
|
|
2867
|
+
interface TDbActionTarget<Row = Record<string, unknown>> {
|
|
2868
|
+
/** `"ids"` — the body listed identifiers; `"query"` — a query target. */
|
|
2869
|
+
readonly kind: "ids" | "query";
|
|
2870
|
+
/** Rows the target resolved to (after `exclude`, before the per-batch gate). */
|
|
2871
|
+
readonly matched: number;
|
|
2872
|
+
/**
|
|
2873
|
+
* The target in batches, each already gated: the row overlay, the
|
|
2874
|
+
* action's `actionRowScope` for the batch, `disabled`, and — for a query
|
|
2875
|
+
* target — the query itself (a row that changed out of it is skipped as
|
|
2876
|
+
* `"stale"`). Rows carry the identity fields and the visible
|
|
2877
|
+
* `requiredFields`. Single pass.
|
|
2878
|
+
*/
|
|
2879
|
+
batches(): AsyncIterable<{
|
|
2880
|
+
ids: Record<string, unknown>[];
|
|
2881
|
+
rows: Row[];
|
|
2882
|
+
}>;
|
|
2883
|
+
/** Reports a row the handler could not process — listed in {@link summary}. */
|
|
2884
|
+
fail(id: Record<string, unknown>, reason: string): void;
|
|
2885
|
+
/** What happened so far: matched, processed, skipped (by the gate) and failed rows. */
|
|
2886
|
+
summary(): TDbActionTargetSummary$1;
|
|
2887
|
+
}
|
|
2888
|
+
/**
|
|
2889
|
+
* The current `'rows'` action's {@link TDbActionTarget} (since 0.1.147).
|
|
2890
|
+
* Available in the handler of every `'rows'` action — `@DbActionTarget()`
|
|
2891
|
+
* handlers process it batch by batch; `@DbActionIDs()` / `@DbActionRows()`
|
|
2892
|
+
* handlers read it for `summary()` (the ids `onDisabledRows: 'skip'`
|
|
2893
|
+
* dropped, with their reasons).
|
|
2894
|
+
*
|
|
2895
|
+
* @since 0.1.147
|
|
2896
|
+
*/
|
|
2897
|
+
declare function useDbActionTarget<Row = Record<string, unknown>>(ctx?: EventContext): TDbActionTarget<Row>;
|
|
2898
|
+
/**
|
|
2899
|
+
* Parameter decorator injecting the action's {@link TDbActionTarget}
|
|
2900
|
+
* (since 0.1.147). Makes the action `'rows'` level and switches its gate to
|
|
2901
|
+
* batch mode: the handler iterates `target.batches()`, each batch gated on
|
|
2902
|
+
* its own (skip semantics — rows failing the gate are skipped and reported
|
|
2903
|
+
* in `summary()`, never rejected). Accepts `{ ids }` bodies, and `{ query }`
|
|
2904
|
+
* bodies when the action declares `queryTarget`. Not combinable with
|
|
2905
|
+
* `@DbActionID*` / `@DbActionRow*`.
|
|
2906
|
+
*
|
|
2907
|
+
* ```ts
|
|
2908
|
+
* @Post("actions/close")
|
|
2909
|
+
* @DbAction<Issue>("close", { label: "Close", queryTarget: true })
|
|
2910
|
+
* async close(@DbActionTarget() target: TDbActionTarget<Issue>) {
|
|
2911
|
+
* for await (const { ids } of target.batches()) await closeIssues(ids)
|
|
2912
|
+
* return target.summary()
|
|
2913
|
+
* }
|
|
2914
|
+
* ```
|
|
2915
|
+
*
|
|
2916
|
+
* @since 0.1.147
|
|
2917
|
+
*/
|
|
2918
|
+
declare function DbActionTarget(): ParameterDecorator;
|
|
2919
|
+
//#endregion
|
|
2920
|
+
//#region src/actions/db-actions-from.decorator.d.ts
|
|
2921
|
+
/** Options of {@link DbActionsFrom}. @since 0.1.147 */
|
|
2922
|
+
interface TDbActionsFromOpts {
|
|
2923
|
+
/**
|
|
2924
|
+
* Source identification field → the path in THIS controller's rows that
|
|
2925
|
+
* carries its value. Its keys must be exactly one identification of the
|
|
2926
|
+
* source (its primary key or a unique index). Default: derived from the
|
|
2927
|
+
* view definition — the view column that plainly maps each of the
|
|
2928
|
+
* source's `preferredId` fields. Required when this controller is not
|
|
2929
|
+
* bound to a view.
|
|
2930
|
+
*/
|
|
2931
|
+
idMap?: Record<string, string>;
|
|
2932
|
+
/** The source's row / rows-level actions to delegate. Default: all of them. */
|
|
2933
|
+
actions?: readonly string[];
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* Lists another controller's row / rows-level actions on THIS controller —
|
|
2937
|
+
* typically a `@ViewController` over the table whose actions the view's
|
|
2938
|
+
* rows stand for (since 0.1.147). The actions stay the source's: its route
|
|
2939
|
+
* runs them (`value`), its gate, permissions, `actionRowScope` and
|
|
2940
|
+
* `disabled` decide; the view only maps its rows to source ids.
|
|
2941
|
+
*
|
|
2942
|
+
* - `/meta.actions` lists them with `owner` (the source's base path) and,
|
|
2943
|
+
* when the ids are renamed, `idMap`; forms come from the source
|
|
2944
|
+
* (`formUrl`).
|
|
2945
|
+
* - `$actions` on the view's rows carries the source's verdict for the row
|
|
2946
|
+
* each view row maps to — what the source's own `GET /meta/actions/:id`
|
|
2947
|
+
* answers for it.
|
|
2948
|
+
* - `GET /meta/actions/:id` on the view answers them when the source id is
|
|
2949
|
+
* the view id renamed (`idMap` paths ⊆ the id used).
|
|
2950
|
+
* - A source action declaring `queryTarget` also takes a query target on the
|
|
2951
|
+
* VIEW (`queryTarget.url`): the view resolves "every view row matching the
|
|
2952
|
+
* query" under its own read scope, maps the rows to source ids and runs the
|
|
2953
|
+
* source's action route on them in batches — the source re-checks every
|
|
2954
|
+
* batch.
|
|
2955
|
+
*
|
|
2956
|
+
* Repeatable (several sources, listed in declaration order). The source is
|
|
2957
|
+
* referenced lazily (no import cycles between controllers) and must be
|
|
2958
|
+
* registered with the app. Only a
|
|
2959
|
+
* controller declaring it gets the `POST {prefix}/delegated-actions/:name`
|
|
2960
|
+
* route (subclasses inherit it).
|
|
2961
|
+
*
|
|
2962
|
+
* ```ts
|
|
2963
|
+
* @ViewController(IssueBoard)
|
|
2964
|
+
* @DbActionsFrom(() => IssueController) // id → id
|
|
2965
|
+
* export class IssueBoardController extends AsDbReadableController<typeof IssueBoard> {}
|
|
2966
|
+
*
|
|
2967
|
+
* @DbActionsFrom(() => IssueController, { idMap: { id: "issueId" }, actions: ["close"] })
|
|
2968
|
+
* ```
|
|
2969
|
+
*
|
|
2970
|
+
* @since 0.1.147
|
|
2971
|
+
*/
|
|
2972
|
+
declare function DbActionsFrom(source: () => Function, opts?: TDbActionsFromOpts): ClassDecorator;
|
|
2973
|
+
//#endregion
|
|
2342
2974
|
//#region src/actions/per-row.d.ts
|
|
2343
2975
|
/**
|
|
2344
2976
|
* Lift a per-row predicate into the batch shape required by
|
|
@@ -2429,4 +3061,37 @@ declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef |
|
|
|
2429
3061
|
*/
|
|
2430
3062
|
declare function applyTerminalRefs(serialized: TSerializedAnnotatedType, runtime: TAtscriptAnnotatedType, options: TSerializeOptions): TSerializedAnnotatedType;
|
|
2431
3063
|
//#endregion
|
|
2432
|
-
|
|
3064
|
+
//#region src/relation-predicates.d.ts
|
|
3065
|
+
/**
|
|
3066
|
+
* Client relational predicates (`nav=$some(…)` / `nav=$none(…)`, since
|
|
3067
|
+
* 0.1.147) at the HTTP layer: the request gate and the
|
|
3068
|
+
* `transformRelationFilter` overlay walk. Server-side filters
|
|
3069
|
+
* (`transformFilter`, `transformOne`, `actionRowScope`) never pass through
|
|
3070
|
+
* here — they are the authorization rule itself.
|
|
3071
|
+
*
|
|
3072
|
+
* Invariant: a client predicate on relation `n` reveals nothing that
|
|
3073
|
+
* `$with=n(<same filter>)` would not reveal under the same controller policy
|
|
3074
|
+
* — same visibility (`hasField` at `n` and `n.*`), the related table's own
|
|
3075
|
+
* capability rules (encrypted, `@db.writeOnly`, JSON storage,
|
|
3076
|
+
* `@db.table.filterable 'manual'`, geo, …) and the same row overlay
|
|
3077
|
+
* (`transformRelationFilter`). On top, the relation must opt in with
|
|
3078
|
+
* `@db.rel.filterable` (a predicate filters the PARENT rows).
|
|
3079
|
+
*/
|
|
3080
|
+
/**
|
|
3081
|
+
* Nesting limit of CLIENT relational predicates per predicate chain (`$with`
|
|
3082
|
+
* hops don't count; server-added predicates are not counted). Below the
|
|
3083
|
+
* core's `REL_FILTER_MAX_DEPTH` so server overlays have headroom.
|
|
3084
|
+
*
|
|
3085
|
+
* @since 0.1.147
|
|
3086
|
+
*/
|
|
3087
|
+
declare const REL_FILTER_CLIENT_MAX_DEPTH = 3;
|
|
3088
|
+
/**
|
|
3089
|
+
* Count limit of CLIENT relational predicates per request — root filter and
|
|
3090
|
+
* `$with` sub-filters together. Below the core's `REL_FILTER_MAX_NODES` so
|
|
3091
|
+
* server overlays have headroom.
|
|
3092
|
+
*
|
|
3093
|
+
* @since 0.1.147
|
|
3094
|
+
*/
|
|
3095
|
+
declare const REL_FILTER_CLIENT_MAX_NODES = 8;
|
|
3096
|
+
//#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 };
|