@atscript/moost-db 0.1.132 → 0.1.134
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 +177 -81
- package/dist/index.d.cts +97 -40
- package/dist/index.d.mts +97 -40
- package/dist/index.mjs +178 -82
- package/package.json +6 -6
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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 { AtscriptDbReadable, AtscriptDbTable, BucketUnit, 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, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
3
|
+
import { AtscriptDbReadable, AtscriptDbTable, BucketUnit, 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, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$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";
|
|
@@ -22,7 +22,9 @@ import { parseUrl } from "@uniqu/url";
|
|
|
22
22
|
*
|
|
23
23
|
* Subclass responsibilities:
|
|
24
24
|
* - Pass the bound interface + logical name + (optional) kind tag through super().
|
|
25
|
-
* - Implement {@link hasField}
|
|
25
|
+
* - Implement {@link hasField} — the field-visibility hook every validated
|
|
26
|
+
* path consults; a path it rejects gets the same `Unknown field` 400 as a
|
|
27
|
+
* nonexistent one, so overriding it hides fields per request.
|
|
26
28
|
* - Register the `/query`, `/pages`, `/one(/:id)` routes with the concrete
|
|
27
29
|
* handlers that match the data source's contract (DB readables route into
|
|
28
30
|
* aggregate/vector/search; value-help controllers just filter/sort/paginate).
|
|
@@ -45,7 +47,11 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
45
47
|
/** Cached serialized form schemas keyed by `FormType.name` — populated lazily by {@link metaForm}. */
|
|
46
48
|
private _formSchemas;
|
|
47
49
|
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
48
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Subclass contract: return `true` if `path` addresses a field that exists
|
|
52
|
+
* AND is visible to the current request — see the DB controller's override
|
|
53
|
+
* for the full list of positions that consult it.
|
|
54
|
+
*/
|
|
49
55
|
protected abstract hasField(path: string): boolean;
|
|
50
56
|
/** Sets @db.http.path on the type metadata from the controller's computed prefix. */
|
|
51
57
|
private _resolveHttpPath;
|
|
@@ -179,6 +185,13 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
179
185
|
protected applyMetaOverlay(meta: TMetaResponse): TMetaResponse | Promise<TMetaResponse>;
|
|
180
186
|
}
|
|
181
187
|
//#endregion
|
|
188
|
+
//#region src/actions/id-validation.d.ts
|
|
189
|
+
/** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
|
|
190
|
+
interface IdValidationSource {
|
|
191
|
+
readonly identifications: readonly TIdentification[];
|
|
192
|
+
readonly fieldDescriptors: readonly TDbFieldMeta[];
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
182
195
|
//#region src/meta/field-capabilities.d.ts
|
|
183
196
|
/**
|
|
184
197
|
* Per-path HTTP capability of a DB readable — the single source that both the
|
|
@@ -195,9 +208,9 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
195
208
|
* `filterable` is the value-comparison verdict, `filterOps` the narrower
|
|
196
209
|
* predicates that still pass where it is `false`.
|
|
197
210
|
*
|
|
198
|
-
* A calendar-bucket source (`bucketable`)
|
|
199
|
-
*
|
|
200
|
-
*
|
|
211
|
+
* A calendar-bucket source (`bucketable`) is the core's `bucketSourceVerdict`
|
|
212
|
+
* (the same function the core path guard runs) under the HTTP-only
|
|
213
|
+
* `@db.writeOnly` veto.
|
|
201
214
|
*/
|
|
202
215
|
interface TFieldCapability {
|
|
203
216
|
/** A value-comparison filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
@@ -215,8 +228,8 @@ interface TFieldCapability {
|
|
|
215
228
|
/** Present when `sortable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
216
229
|
sortReason?: string;
|
|
217
230
|
/**
|
|
218
|
-
* A calendar bucket over this path passes the gate (
|
|
219
|
-
*
|
|
231
|
+
* A calendar bucket over this path passes the gate (¬writeOnly ∧ the core's
|
|
232
|
+
* `bucketSourceVerdict`). Since 0.1.132.
|
|
220
233
|
*/
|
|
221
234
|
bucketable: boolean;
|
|
222
235
|
/** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
@@ -272,14 +285,8 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
272
285
|
private readonly _entries;
|
|
273
286
|
/** Nested-object parents (never listed, always selectable) → their listed leaves. */
|
|
274
287
|
private readonly _objectParents;
|
|
275
|
-
/**
|
|
276
|
-
|
|
277
|
-
* measures), else `undefined` — the core rule: a grouping source, a
|
|
278
|
-
* bucketed field included, must then be a dimension.
|
|
279
|
-
*/
|
|
280
|
-
private readonly _dimensions;
|
|
281
|
-
/** Paths of every JSON-value descriptor (`isJsonValueField`) — see `jsonValueAncestor`. */
|
|
282
|
-
private readonly _jsonValueParents;
|
|
288
|
+
/** What `bucketSourceVerdict` reads of the table (JSON-value parents, dimensions, measures). */
|
|
289
|
+
private readonly _bucketTable;
|
|
283
290
|
/** Listed leaves — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
284
291
|
get leaves(): ReadonlyMap<string, unknown>;
|
|
285
292
|
/** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
@@ -292,16 +299,23 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
292
299
|
isPhysicallyFilterable(path: string): boolean;
|
|
293
300
|
/**
|
|
294
301
|
* Gate check for one path in one position. Returns `undefined` when the
|
|
295
|
-
* path is accepted.
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
302
|
+
* path is accepted.
|
|
303
|
+
*
|
|
304
|
+
* `exists` runs FIRST, for every path (since 0.1.133): it is the
|
|
305
|
+
* controller's `hasField`, the visibility hook subclasses narrow per
|
|
306
|
+
* request (e.g. a projection-scoped viewer). A path it rejects answers
|
|
307
|
+
* `Unknown field "x"` exactly like a nonexistent one — never a capability
|
|
308
|
+
* or navigation hint, which would reveal the field and let a filter or
|
|
309
|
+
* sort on it act as a value oracle. Before 0.1.133 listed leaves and
|
|
310
|
+
* navigation paths skipped it.
|
|
300
311
|
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
312
|
+
* Then: navigation paths (a nav path exists on the target table but is
|
|
313
|
+
* never a column here), a listed leaf's capability, and for other paths
|
|
314
|
+
* the storage classification. Existence also runs BEFORE the JSON /
|
|
315
|
+
* encrypted classification: an untyped descendant of a JSON column
|
|
316
|
+
* (`address.nope`) is reported as `Unknown field`, not as "inside
|
|
317
|
+
* JSON-stored column" — clients pin that wording, so do not "align" it
|
|
318
|
+
* with the core backstop's text.
|
|
305
319
|
*
|
|
306
320
|
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
307
321
|
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
@@ -343,8 +357,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
343
357
|
private _capabilities?;
|
|
344
358
|
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
345
359
|
protected metaCacheKey(): unknown;
|
|
346
|
-
/** Bound once: the
|
|
360
|
+
/** Bound once: the visibility check ({@link hasField}) the gate hands to `capabilities.check`. */
|
|
347
361
|
private readonly _exists;
|
|
362
|
+
/**
|
|
363
|
+
* Id-resolution options (since 0.1.134): `{ isFieldVisible: hasField }`
|
|
364
|
+
* when a subclass overrides {@link hasField}, else `undefined` (the default
|
|
365
|
+
* accepts every real path, so resolution stays unfiltered). A unique index
|
|
366
|
+
* over a hidden field is never an identification.
|
|
367
|
+
*/
|
|
368
|
+
protected readonly _idOpts: TIdResolveOptions | undefined;
|
|
369
|
+
/** Narrowed id sources, one stable object per distinct visible-identification set. */
|
|
370
|
+
private readonly _idSources;
|
|
348
371
|
private readonly _preferredIdSet;
|
|
349
372
|
private readonly _overlayIsNoOp;
|
|
350
373
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
@@ -360,9 +383,34 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
360
383
|
*/
|
|
361
384
|
private readonly _invertibleFields;
|
|
362
385
|
constructor(app: Moost, readable?: AtscriptDbReadable<T>);
|
|
386
|
+
/**
|
|
387
|
+
* The identifications this request may address rows through (since
|
|
388
|
+
* 0.1.134): the readable's own, minus unique indexes over fields
|
|
389
|
+
* {@link hasField} hides. Used by `/one?…`, `DELETE /?…` and action `ids`.
|
|
390
|
+
* Stable per distinct outcome, so per-source caches keyed on it hit.
|
|
391
|
+
*/
|
|
392
|
+
get idSource(): IdValidationSource;
|
|
363
393
|
private _collectInvertibleFields;
|
|
364
394
|
private _collectQuantityRefs;
|
|
365
395
|
private _collectAnnotated;
|
|
396
|
+
/**
|
|
397
|
+
* THE field-visibility hook: every gated path consults it before any
|
|
398
|
+
* capability check (since 0.1.133) — filter keys (inside `$and` / `$or` /
|
|
399
|
+
* `$not`, existence predicates included), `$sort`, `$select`,
|
|
400
|
+
* `$groupBy`, `$having` keys, aggregate and calendar-bucket `$field`s,
|
|
401
|
+
* `$with` relation names and sub-query paths, and the `$search` fallback
|
|
402
|
+
* fields. A path it rejects is answered exactly like a nonexistent one
|
|
403
|
+
* (`Unknown field "x"` / `Unknown relation "x"`), so override it to hide
|
|
404
|
+
* fields per request (read scopes). Since 0.1.134 it also governs row
|
|
405
|
+
* identification — a unique index over a hidden field is not an
|
|
406
|
+
* identification for `/one/:id`, `/one?…`, `DELETE`, a PK-less `PATCH` or
|
|
407
|
+
* an action id (primary key and `preferredId` always are) — and the
|
|
408
|
+
* nested-object 400 hint lists visible leaves only. The default accepts every real path
|
|
409
|
+
* (`isValidFieldPath`). `/meta` does NOT consult it — prune hidden fields
|
|
410
|
+
* there with `applyMetaOverlay`. Native text search and vector search
|
|
411
|
+
* (`$vector` names an index) run inside the engine over its indexes, out of
|
|
412
|
+
* this hook's reach — keep hidden fields out of those indexes.
|
|
413
|
+
*/
|
|
366
414
|
protected hasField(path: string): boolean;
|
|
367
415
|
/**
|
|
368
416
|
* Structural capability gate (since 0.1.128): walks the PARSED query —
|
|
@@ -432,6 +480,14 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
432
480
|
private widenPreferredIdProjection;
|
|
433
481
|
private _widenArrayProjection;
|
|
434
482
|
private _widenMapProjection;
|
|
483
|
+
/**
|
|
484
|
+
* The logical paths an exclusion keeps. A path goes when it, an ancestor
|
|
485
|
+
* or a descendant is excluded: excluding an object parent excludes its
|
|
486
|
+
* whole subtree, and a kept parent would carry an excluded child back
|
|
487
|
+
* (its other leaves stay listed on their own). Before 0.1.134 only the
|
|
488
|
+
* exact paths were dropped, so `$select=-a` still returned `a`'s leaves.
|
|
489
|
+
*/
|
|
490
|
+
private _invertExclusion;
|
|
435
491
|
/**
|
|
436
492
|
* Auto-includes the sibling-ref field whenever its `@db.amount.currency.ref`
|
|
437
493
|
* / `@db.unit.ref` quantity is selected — UI must never get a value without
|
|
@@ -502,7 +558,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
502
558
|
* family (count vs no-count).
|
|
503
559
|
*/
|
|
504
560
|
private _runReadWithActions;
|
|
505
|
-
/**
|
|
561
|
+
/**
|
|
562
|
+
* Pick the first identification (PK or unique index) whose fields are all
|
|
563
|
+
* present in the query. A unique index over a field {@link hasField} hides
|
|
564
|
+
* is not a candidate (since 0.1.134) — `?hidden=x` answers exactly like
|
|
565
|
+
* `?nope=x`, so it cannot probe whether a row with that value exists.
|
|
566
|
+
*/
|
|
506
567
|
protected extractIdShape(query: Record<string, string>): Record<string, unknown> | HttpError;
|
|
507
568
|
/**
|
|
508
569
|
* **GET /query** — returns an array of records or a count.
|
|
@@ -643,14 +704,17 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
643
704
|
* several table operations.
|
|
644
705
|
*/
|
|
645
706
|
protected withTransaction<R>(fn: () => Promise<R>): Promise<R>;
|
|
707
|
+
/** The table write call's trailing options — see {@link _hookArgs}. */
|
|
708
|
+
private readonly _writeArgs;
|
|
709
|
+
/** `deleteOne`'s trailing options — see {@link _hookArgs}. */
|
|
710
|
+
private readonly _removeArgs;
|
|
646
711
|
/**
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
712
|
+
* A table call's trailing options, built once: `guard` only when the guard
|
|
713
|
+
* hook is overridden, `isFieldVisible` only when `hasField` is (an id or a
|
|
714
|
+
* PK-less payload never resolves through a hidden unique key) — else
|
|
715
|
+
* nothing, so an unmodified controller calls the table exactly as before.
|
|
650
716
|
*/
|
|
651
|
-
private
|
|
652
|
-
/** `deleteOne`'s trailing options: `[{ guard }]` only when `guardRemove` is overridden. */
|
|
653
|
-
private _removeArgs;
|
|
717
|
+
private _hookArgs;
|
|
654
718
|
/** Resolves a hook result: `undefined` aborts with `abortMessage`, an `Error` is thrown, anything else passes. */
|
|
655
719
|
private _checkHook;
|
|
656
720
|
/** Runs `onWrite` and re-applies the shape gate to its output (a non-object is a 500 "Not saved"). */
|
|
@@ -1507,13 +1571,6 @@ declare function getControllerFormType(ctor: Function, name: string): TAtscriptA
|
|
|
1507
1571
|
/** Discover actions on a controller, memoized per ctor. `info`-only callers map `e => e.info`. */
|
|
1508
1572
|
declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
1509
1573
|
//#endregion
|
|
1510
|
-
//#region src/actions/id-validation.d.ts
|
|
1511
|
-
/** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
|
|
1512
|
-
interface IdValidationSource {
|
|
1513
|
-
readonly identifications: readonly TIdentification[];
|
|
1514
|
-
readonly fieldDescriptors: readonly TDbFieldMeta[];
|
|
1515
|
-
}
|
|
1516
|
-
//#endregion
|
|
1517
1574
|
//#region src/actions/id-cache.d.ts
|
|
1518
1575
|
declare const useDbActionId: import("@wooksjs/event-core").WookComposable<{
|
|
1519
1576
|
load: () => Promise<Record<string, unknown>>;
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { TAtscriptAnnotatedType, TAtscriptDataType, TSerializeOptions, TSerializ
|
|
|
3
3
|
import { HttpError } from "@moostjs/event-http";
|
|
4
4
|
import { Mate, Moost, TConsoleBase, TMateParamMeta, TMoostMetadata } from "moost";
|
|
5
5
|
import { parseUrl } from "@uniqu/url";
|
|
6
|
-
import { AtscriptDbReadable, AtscriptDbTable, BucketUnit, 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, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
6
|
+
import { AtscriptDbReadable, AtscriptDbTable, BucketUnit, 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, TDbRemoveGuardContext, TDbRemoveGuardContext as TDbRemoveGuardContext$1, TDbWriteAction, TDbWriteAction as TDbWriteAction$1, TDbWriteGuardContext, TDbWriteGuardContext as TDbWriteGuardContext$1, TFilterPredicate, TIdResolveOptions, TIdentification, TMetaResponse, TQueryPathOp, TQueryPathOp as TQueryPathOp$1, TQueryPathRefs, TQueryPathSource, Uniquery, UniqueryControls, collectQueryPaths } from "@atscript/db";
|
|
7
7
|
//#region src/as-readable.controller.d.ts
|
|
8
8
|
/**
|
|
9
9
|
* Abstract base class for read-only HTTP controllers over an Atscript interface.
|
|
@@ -22,7 +22,9 @@ import { AtscriptDbReadable, AtscriptDbTable, BucketUnit, FilterExpr, FlatOf, TC
|
|
|
22
22
|
*
|
|
23
23
|
* Subclass responsibilities:
|
|
24
24
|
* - Pass the bound interface + logical name + (optional) kind tag through super().
|
|
25
|
-
* - Implement {@link hasField}
|
|
25
|
+
* - Implement {@link hasField} — the field-visibility hook every validated
|
|
26
|
+
* path consults; a path it rejects gets the same `Unknown field` 400 as a
|
|
27
|
+
* nonexistent one, so overriding it hides fields per request.
|
|
26
28
|
* - Register the `/query`, `/pages`, `/one(/:id)` routes with the concrete
|
|
27
29
|
* handlers that match the data source's contract (DB readables route into
|
|
28
30
|
* aggregate/vector/search; value-help controllers just filter/sort/paginate).
|
|
@@ -45,7 +47,11 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
45
47
|
/** Cached serialized form schemas keyed by `FormType.name` — populated lazily by {@link metaForm}. */
|
|
46
48
|
private _formSchemas;
|
|
47
49
|
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
48
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Subclass contract: return `true` if `path` addresses a field that exists
|
|
52
|
+
* AND is visible to the current request — see the DB controller's override
|
|
53
|
+
* for the full list of positions that consult it.
|
|
54
|
+
*/
|
|
49
55
|
protected abstract hasField(path: string): boolean;
|
|
50
56
|
/** Sets @db.http.path on the type metadata from the controller's computed prefix. */
|
|
51
57
|
private _resolveHttpPath;
|
|
@@ -179,6 +185,13 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
179
185
|
protected applyMetaOverlay(meta: TMetaResponse): TMetaResponse | Promise<TMetaResponse>;
|
|
180
186
|
}
|
|
181
187
|
//#endregion
|
|
188
|
+
//#region src/actions/id-validation.d.ts
|
|
189
|
+
/** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
|
|
190
|
+
interface IdValidationSource {
|
|
191
|
+
readonly identifications: readonly TIdentification[];
|
|
192
|
+
readonly fieldDescriptors: readonly TDbFieldMeta[];
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
182
195
|
//#region src/meta/field-capabilities.d.ts
|
|
183
196
|
/**
|
|
184
197
|
* Per-path HTTP capability of a DB readable — the single source that both the
|
|
@@ -195,9 +208,9 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
195
208
|
* `filterable` is the value-comparison verdict, `filterOps` the narrower
|
|
196
209
|
* predicates that still pass where it is `false`.
|
|
197
210
|
*
|
|
198
|
-
* A calendar-bucket source (`bucketable`)
|
|
199
|
-
*
|
|
200
|
-
*
|
|
211
|
+
* A calendar-bucket source (`bucketable`) is the core's `bucketSourceVerdict`
|
|
212
|
+
* (the same function the core path guard runs) under the HTTP-only
|
|
213
|
+
* `@db.writeOnly` veto.
|
|
201
214
|
*/
|
|
202
215
|
interface TFieldCapability {
|
|
203
216
|
/** A value-comparison filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
@@ -215,8 +228,8 @@ interface TFieldCapability {
|
|
|
215
228
|
/** Present when `sortable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
216
229
|
sortReason?: string;
|
|
217
230
|
/**
|
|
218
|
-
* A calendar bucket over this path passes the gate (
|
|
219
|
-
*
|
|
231
|
+
* A calendar bucket over this path passes the gate (¬writeOnly ∧ the core's
|
|
232
|
+
* `bucketSourceVerdict`). Since 0.1.132.
|
|
220
233
|
*/
|
|
221
234
|
bucketable: boolean;
|
|
222
235
|
/** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
@@ -272,14 +285,8 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
272
285
|
private readonly _entries;
|
|
273
286
|
/** Nested-object parents (never listed, always selectable) → their listed leaves. */
|
|
274
287
|
private readonly _objectParents;
|
|
275
|
-
/**
|
|
276
|
-
|
|
277
|
-
* measures), else `undefined` — the core rule: a grouping source, a
|
|
278
|
-
* bucketed field included, must then be a dimension.
|
|
279
|
-
*/
|
|
280
|
-
private readonly _dimensions;
|
|
281
|
-
/** Paths of every JSON-value descriptor (`isJsonValueField`) — see `jsonValueAncestor`. */
|
|
282
|
-
private readonly _jsonValueParents;
|
|
288
|
+
/** What `bucketSourceVerdict` reads of the table (JSON-value parents, dimensions, measures). */
|
|
289
|
+
private readonly _bucketTable;
|
|
283
290
|
/** Listed leaves — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
284
291
|
get leaves(): ReadonlyMap<string, unknown>;
|
|
285
292
|
/** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
@@ -292,16 +299,23 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
292
299
|
isPhysicallyFilterable(path: string): boolean;
|
|
293
300
|
/**
|
|
294
301
|
* Gate check for one path in one position. Returns `undefined` when the
|
|
295
|
-
* path is accepted.
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
302
|
+
* path is accepted.
|
|
303
|
+
*
|
|
304
|
+
* `exists` runs FIRST, for every path (since 0.1.133): it is the
|
|
305
|
+
* controller's `hasField`, the visibility hook subclasses narrow per
|
|
306
|
+
* request (e.g. a projection-scoped viewer). A path it rejects answers
|
|
307
|
+
* `Unknown field "x"` exactly like a nonexistent one — never a capability
|
|
308
|
+
* or navigation hint, which would reveal the field and let a filter or
|
|
309
|
+
* sort on it act as a value oracle. Before 0.1.133 listed leaves and
|
|
310
|
+
* navigation paths skipped it.
|
|
300
311
|
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
312
|
+
* Then: navigation paths (a nav path exists on the target table but is
|
|
313
|
+
* never a column here), a listed leaf's capability, and for other paths
|
|
314
|
+
* the storage classification. Existence also runs BEFORE the JSON /
|
|
315
|
+
* encrypted classification: an untyped descendant of a JSON column
|
|
316
|
+
* (`address.nope`) is reported as `Unknown field`, not as "inside
|
|
317
|
+
* JSON-stored column" — clients pin that wording, so do not "align" it
|
|
318
|
+
* with the core backstop's text.
|
|
305
319
|
*
|
|
306
320
|
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
307
321
|
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
@@ -343,8 +357,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
343
357
|
private _capabilities?;
|
|
344
358
|
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
345
359
|
protected metaCacheKey(): unknown;
|
|
346
|
-
/** Bound once: the
|
|
360
|
+
/** Bound once: the visibility check ({@link hasField}) the gate hands to `capabilities.check`. */
|
|
347
361
|
private readonly _exists;
|
|
362
|
+
/**
|
|
363
|
+
* Id-resolution options (since 0.1.134): `{ isFieldVisible: hasField }`
|
|
364
|
+
* when a subclass overrides {@link hasField}, else `undefined` (the default
|
|
365
|
+
* accepts every real path, so resolution stays unfiltered). A unique index
|
|
366
|
+
* over a hidden field is never an identification.
|
|
367
|
+
*/
|
|
368
|
+
protected readonly _idOpts: TIdResolveOptions | undefined;
|
|
369
|
+
/** Narrowed id sources, one stable object per distinct visible-identification set. */
|
|
370
|
+
private readonly _idSources;
|
|
348
371
|
private readonly _preferredIdSet;
|
|
349
372
|
private readonly _overlayIsNoOp;
|
|
350
373
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
@@ -360,9 +383,34 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
360
383
|
*/
|
|
361
384
|
private readonly _invertibleFields;
|
|
362
385
|
constructor(app: Moost, readable?: AtscriptDbReadable<T>);
|
|
386
|
+
/**
|
|
387
|
+
* The identifications this request may address rows through (since
|
|
388
|
+
* 0.1.134): the readable's own, minus unique indexes over fields
|
|
389
|
+
* {@link hasField} hides. Used by `/one?…`, `DELETE /?…` and action `ids`.
|
|
390
|
+
* Stable per distinct outcome, so per-source caches keyed on it hit.
|
|
391
|
+
*/
|
|
392
|
+
get idSource(): IdValidationSource;
|
|
363
393
|
private _collectInvertibleFields;
|
|
364
394
|
private _collectQuantityRefs;
|
|
365
395
|
private _collectAnnotated;
|
|
396
|
+
/**
|
|
397
|
+
* THE field-visibility hook: every gated path consults it before any
|
|
398
|
+
* capability check (since 0.1.133) — filter keys (inside `$and` / `$or` /
|
|
399
|
+
* `$not`, existence predicates included), `$sort`, `$select`,
|
|
400
|
+
* `$groupBy`, `$having` keys, aggregate and calendar-bucket `$field`s,
|
|
401
|
+
* `$with` relation names and sub-query paths, and the `$search` fallback
|
|
402
|
+
* fields. A path it rejects is answered exactly like a nonexistent one
|
|
403
|
+
* (`Unknown field "x"` / `Unknown relation "x"`), so override it to hide
|
|
404
|
+
* fields per request (read scopes). Since 0.1.134 it also governs row
|
|
405
|
+
* identification — a unique index over a hidden field is not an
|
|
406
|
+
* identification for `/one/:id`, `/one?…`, `DELETE`, a PK-less `PATCH` or
|
|
407
|
+
* an action id (primary key and `preferredId` always are) — and the
|
|
408
|
+
* nested-object 400 hint lists visible leaves only. The default accepts every real path
|
|
409
|
+
* (`isValidFieldPath`). `/meta` does NOT consult it — prune hidden fields
|
|
410
|
+
* there with `applyMetaOverlay`. Native text search and vector search
|
|
411
|
+
* (`$vector` names an index) run inside the engine over its indexes, out of
|
|
412
|
+
* this hook's reach — keep hidden fields out of those indexes.
|
|
413
|
+
*/
|
|
366
414
|
protected hasField(path: string): boolean;
|
|
367
415
|
/**
|
|
368
416
|
* Structural capability gate (since 0.1.128): walks the PARSED query —
|
|
@@ -432,6 +480,14 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
432
480
|
private widenPreferredIdProjection;
|
|
433
481
|
private _widenArrayProjection;
|
|
434
482
|
private _widenMapProjection;
|
|
483
|
+
/**
|
|
484
|
+
* The logical paths an exclusion keeps. A path goes when it, an ancestor
|
|
485
|
+
* or a descendant is excluded: excluding an object parent excludes its
|
|
486
|
+
* whole subtree, and a kept parent would carry an excluded child back
|
|
487
|
+
* (its other leaves stay listed on their own). Before 0.1.134 only the
|
|
488
|
+
* exact paths were dropped, so `$select=-a` still returned `a`'s leaves.
|
|
489
|
+
*/
|
|
490
|
+
private _invertExclusion;
|
|
435
491
|
/**
|
|
436
492
|
* Auto-includes the sibling-ref field whenever its `@db.amount.currency.ref`
|
|
437
493
|
* / `@db.unit.ref` quantity is selected — UI must never get a value without
|
|
@@ -502,7 +558,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
502
558
|
* family (count vs no-count).
|
|
503
559
|
*/
|
|
504
560
|
private _runReadWithActions;
|
|
505
|
-
/**
|
|
561
|
+
/**
|
|
562
|
+
* Pick the first identification (PK or unique index) whose fields are all
|
|
563
|
+
* present in the query. A unique index over a field {@link hasField} hides
|
|
564
|
+
* is not a candidate (since 0.1.134) — `?hidden=x` answers exactly like
|
|
565
|
+
* `?nope=x`, so it cannot probe whether a row with that value exists.
|
|
566
|
+
*/
|
|
506
567
|
protected extractIdShape(query: Record<string, string>): Record<string, unknown> | HttpError;
|
|
507
568
|
/**
|
|
508
569
|
* **GET /query** — returns an array of records or a count.
|
|
@@ -643,14 +704,17 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
643
704
|
* several table operations.
|
|
644
705
|
*/
|
|
645
706
|
protected withTransaction<R>(fn: () => Promise<R>): Promise<R>;
|
|
707
|
+
/** The table write call's trailing options — see {@link _hookArgs}. */
|
|
708
|
+
private readonly _writeArgs;
|
|
709
|
+
/** `deleteOne`'s trailing options — see {@link _hookArgs}. */
|
|
710
|
+
private readonly _removeArgs;
|
|
646
711
|
/**
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
712
|
+
* A table call's trailing options, built once: `guard` only when the guard
|
|
713
|
+
* hook is overridden, `isFieldVisible` only when `hasField` is (an id or a
|
|
714
|
+
* PK-less payload never resolves through a hidden unique key) — else
|
|
715
|
+
* nothing, so an unmodified controller calls the table exactly as before.
|
|
650
716
|
*/
|
|
651
|
-
private
|
|
652
|
-
/** `deleteOne`'s trailing options: `[{ guard }]` only when `guardRemove` is overridden. */
|
|
653
|
-
private _removeArgs;
|
|
717
|
+
private _hookArgs;
|
|
654
718
|
/** Resolves a hook result: `undefined` aborts with `abortMessage`, an `Error` is thrown, anything else passes. */
|
|
655
719
|
private _checkHook;
|
|
656
720
|
/** Runs `onWrite` and re-applies the shape gate to its output (a non-object is a 500 "Not saved"). */
|
|
@@ -1507,13 +1571,6 @@ declare function getControllerFormType(ctor: Function, name: string): TAtscriptA
|
|
|
1507
1571
|
/** Discover actions on a controller, memoized per ctor. `info`-only callers map `e => e.info`. */
|
|
1508
1572
|
declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
|
|
1509
1573
|
//#endregion
|
|
1510
|
-
//#region src/actions/id-validation.d.ts
|
|
1511
|
-
/** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
|
|
1512
|
-
interface IdValidationSource {
|
|
1513
|
-
readonly identifications: readonly TIdentification[];
|
|
1514
|
-
readonly fieldDescriptors: readonly TDbFieldMeta[];
|
|
1515
|
-
}
|
|
1516
|
-
//#endregion
|
|
1517
1574
|
//#region src/actions/id-cache.d.ts
|
|
1518
1575
|
declare const useDbActionId: import("@wooksjs/event-core").WookComposable<{
|
|
1519
1576
|
load: () => Promise<Record<string, unknown>>;
|