@atscript/moost-db 0.1.131 → 0.1.132
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 +191 -68
- package/dist/index.d.cts +93 -13
- package/dist/index.d.mts +93 -13
- package/dist/index.mjs +192 -69
- 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, 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, 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, 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";
|
|
@@ -40,6 +40,8 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
40
40
|
private _serializedType?;
|
|
41
41
|
/** Cached full meta response (computed lazily on first meta() call). */
|
|
42
42
|
private _metaResponse?;
|
|
43
|
+
/** {@link metaCacheKey} the cached response was built for. */
|
|
44
|
+
private _metaResponseKey?;
|
|
43
45
|
/** Cached serialized form schemas keyed by `FormType.name` — populated lazily by {@link metaForm}. */
|
|
44
46
|
private _formSchemas;
|
|
45
47
|
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
@@ -126,10 +128,18 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
126
128
|
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
127
129
|
/**
|
|
128
130
|
* **GET /meta** — returns the bound interface's metadata envelope. The
|
|
129
|
-
* static envelope is cached
|
|
130
|
-
* subclasses can prune the
|
|
131
|
+
* static envelope is cached (rebuilt when {@link metaCacheKey} changes);
|
|
132
|
+
* {@link applyMetaOverlay} runs per request so subclasses can prune the
|
|
133
|
+
* response by principal.
|
|
131
134
|
*/
|
|
132
135
|
meta(): Promise<TMetaResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Identity of the inputs the cached `/meta` envelope is built from — a new
|
|
138
|
+
* value rebuilds it. Default: constant (built once). The DB readable
|
|
139
|
+
* controller returns its capability index, which is rebuilt when the
|
|
140
|
+
* adapter's capabilities change (since 0.1.132).
|
|
141
|
+
*/
|
|
142
|
+
protected metaCacheKey(): unknown;
|
|
133
143
|
/**
|
|
134
144
|
* **GET /meta/form/:name** — returns the serialized schema of a form
|
|
135
145
|
* referenced by an action's `inputForm` field. The form name is the
|
|
@@ -180,10 +190,20 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
180
190
|
* `@db.table.filterable / sortable 'manual'` + `@db.column.*`). Policy applies
|
|
181
191
|
* to filters and `$sort` only; `$groupBy`, `$having` keys and aggregate
|
|
182
192
|
* `$field`s use the physical capability alone.
|
|
193
|
+
*
|
|
194
|
+
* A filter entry is judged by its predicate class (the core's `canFilterLeaf`):
|
|
195
|
+
* `filterable` is the value-comparison verdict, `filterOps` the narrower
|
|
196
|
+
* predicates that still pass where it is `false`.
|
|
197
|
+
*
|
|
198
|
+
* A calendar-bucket source (`bucketable`) needs the physical capability, a
|
|
199
|
+
* `number.timestamp` type, dimension status on a strict (dimension / measure
|
|
200
|
+
* declaring) table, and an adapter with calendar-bucket units.
|
|
183
201
|
*/
|
|
184
202
|
interface TFieldCapability {
|
|
185
|
-
/** A filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
203
|
+
/** A value-comparison filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
186
204
|
filterable: boolean;
|
|
205
|
+
/** Present when `filterable` is `false` yet narrower predicates (`$exists`, `$geoWithin`) pass the gate. */
|
|
206
|
+
filterOps?: string[];
|
|
187
207
|
/** A `$sort` on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
188
208
|
sortable: boolean;
|
|
189
209
|
/** The path may appear in `$select`. `@db.writeOnly` fields are selectable — the seal strips them after the gate. */
|
|
@@ -194,6 +214,13 @@ interface TFieldCapability {
|
|
|
194
214
|
filterReason?: string;
|
|
195
215
|
/** Present when `sortable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
196
216
|
sortReason?: string;
|
|
217
|
+
/**
|
|
218
|
+
* A calendar bucket over this path passes the gate (physical ∧ timestamp ∧
|
|
219
|
+
* (¬strict ∨ dimension) ∧ adapter has calendar buckets). Since 0.1.132.
|
|
220
|
+
*/
|
|
221
|
+
bucketable: boolean;
|
|
222
|
+
/** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
223
|
+
bucketReason?: string;
|
|
197
224
|
}
|
|
198
225
|
/** One rejected path: `path` is the offending logical path, `message` the full sentence. */
|
|
199
226
|
interface TCapabilityVerdict {
|
|
@@ -201,13 +228,18 @@ interface TCapabilityVerdict {
|
|
|
201
228
|
message: string;
|
|
202
229
|
}
|
|
203
230
|
/** The readable members the index reads. */
|
|
204
|
-
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField">;
|
|
231
|
+
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "dimensions" | "measures">;
|
|
205
232
|
/**
|
|
206
|
-
* Capability index of one readable
|
|
233
|
+
* Capability index of one readable.
|
|
207
234
|
*
|
|
208
235
|
* - {@link entries} feeds `/meta.fields` (listed leaves in descriptor order);
|
|
209
236
|
* - {@link check} is the request gate: same inputs, same answer.
|
|
210
237
|
*
|
|
238
|
+
* Built from the adapter's capabilities as they are NOW. Some are only known
|
|
239
|
+
* after schema sync (PostgreSQL learns PostGIS there), so owners rebuild the
|
|
240
|
+
* index when {@link adapterSignature} changes (see `AsDbReadableController`'s
|
|
241
|
+
* `capabilities` getter) instead of keeping a constructor-time snapshot.
|
|
242
|
+
*
|
|
211
243
|
* Paths outside the index are classified by the core's `classifyQueryPath`
|
|
212
244
|
* (the same rules the core backstop applies) — navigation path, nested-object
|
|
213
245
|
* parent, JSON descendant (relational adapters), encrypted descendant,
|
|
@@ -224,9 +256,30 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
224
256
|
readonly jsonParents: ReadonlySet<string>;
|
|
225
257
|
/** Descriptors carrying `@db.encrypted` (the ciphertext column on relational adapters). */
|
|
226
258
|
readonly encryptedFields: ReadonlySet<string>;
|
|
259
|
+
/** Every field descriptor's `physicalName` — names a calendar-bucket alias may not take. */
|
|
260
|
+
readonly physicalNames: ReadonlySet<string>;
|
|
261
|
+
/** Calendar-bucket units the adapter groups by, in `BUCKET_UNITS` order (`/meta.bucketUnits`). */
|
|
262
|
+
readonly bucketUnits: readonly BucketUnit[];
|
|
263
|
+
/** The adapter-level capabilities this index was built against — see {@link adapterSignature}. */
|
|
264
|
+
readonly signature: string;
|
|
265
|
+
/**
|
|
266
|
+
* The adapter-level capabilities that can change after construction (geo
|
|
267
|
+
* support, calendar-bucket units): an index whose {@link signature}
|
|
268
|
+
* differs from this is stale. Any new adapter-level input the index reads
|
|
269
|
+
* must be added here.
|
|
270
|
+
*/
|
|
271
|
+
static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits">): string;
|
|
227
272
|
private readonly _entries;
|
|
228
273
|
/** Nested-object parents (never listed, always selectable) → their listed leaves. */
|
|
229
274
|
private readonly _objectParents;
|
|
275
|
+
/**
|
|
276
|
+
* Declared dimensions when the table is strict (declares dimensions or
|
|
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;
|
|
230
283
|
/** Listed leaves — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
231
284
|
get leaves(): ReadonlyMap<string, unknown>;
|
|
232
285
|
/** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
@@ -249,8 +302,11 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
249
302
|
* an untyped descendant of a JSON column (`address.nope`) is reported as
|
|
250
303
|
* `Unknown field`, not as "inside JSON-stored column" — clients pin that
|
|
251
304
|
* wording, so do not "align" it with the core backstop's text.
|
|
305
|
+
*
|
|
306
|
+
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
307
|
+
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
252
308
|
*/
|
|
253
|
-
check(path: string, op: TQueryPathOp$1, exists: (path: string) => boolean): TCapabilityVerdict | undefined;
|
|
309
|
+
check(path: string, op: TQueryPathOp$1, exists: (path: string) => boolean, predicate?: TFilterPredicate): TCapabilityVerdict | undefined;
|
|
254
310
|
}
|
|
255
311
|
//#endregion
|
|
256
312
|
//#region src/as-db-readable.controller.d.ts
|
|
@@ -276,8 +332,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
276
332
|
* Per-path capability index (since 0.1.128): the ONE input both `/meta.fields`
|
|
277
333
|
* and the request gate ({@link checkCapabilities}) are computed from, so
|
|
278
334
|
* metadata and runtime can never diverge.
|
|
279
|
-
|
|
280
|
-
|
|
335
|
+
*
|
|
336
|
+
* Built on first use and rebuilt whenever the adapter-level capabilities
|
|
337
|
+
* change (`FieldCapabilityIndex.adapterSignature`: geo support, calendar
|
|
338
|
+
* buckets) — PostgreSQL learns PostGIS only during schema sync, which may
|
|
339
|
+
* run after this controller is constructed, so a constructor-time snapshot
|
|
340
|
+
* would keep advertising (and gating) the pre-sync answer (since 0.1.132).
|
|
341
|
+
*/
|
|
342
|
+
protected get capabilities(): FieldCapabilityIndex;
|
|
343
|
+
private _capabilities?;
|
|
344
|
+
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
345
|
+
protected metaCacheKey(): unknown;
|
|
281
346
|
/** Bound once: the field-existence check the gate hands to `capabilities.check`. */
|
|
282
347
|
private readonly _exists;
|
|
283
348
|
private readonly _preferredIdSet;
|
|
@@ -309,14 +374,29 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
309
374
|
*
|
|
310
375
|
* Rejections use the structured envelope `{ message, statusCode: 400,
|
|
311
376
|
* errors: [{ path, message }] }` — `path` is the offending logical path.
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
377
|
+
*
|
|
378
|
+
* Expects normalized `$select` computed entries ({@link checkComputedSelect}
|
|
379
|
+
* ran first); a bucket's source is checked like any other path (op
|
|
380
|
+
* `bucket`). After the per-path checks the core `$having` rule runs
|
|
381
|
+
* (`checkHavingKeys`: aliases or `$groupBy` fields only), so a readable mock
|
|
382
|
+
* and a real table answer alike.
|
|
315
383
|
*/
|
|
316
384
|
protected checkCapabilities(parsed: {
|
|
317
385
|
filter?: FilterExpr;
|
|
318
386
|
controls?: object;
|
|
319
387
|
}): HttpError | undefined;
|
|
388
|
+
/**
|
|
389
|
+
* The core's shared normalizer of `$select` computed entries
|
|
390
|
+
* (`resolveCalendarBuckets`) as a 400 with the core's wording and `path`
|
|
391
|
+
* (`$select` / `$groupBy`): entry shapes, calendar-bucket unit / zone /
|
|
392
|
+
* week start / alias, "grouped queries only", "must also appear in
|
|
393
|
+
* $groupBy", alias collisions with this table's fields. Runs once per
|
|
394
|
+
* request, before {@link checkCapabilities}: at the head of
|
|
395
|
+
* {@link validateParsed} — ahead of the controls DTO, which would otherwise
|
|
396
|
+
* answer a bucket in a non-grouped query with a generic type mismatch — or
|
|
397
|
+
* explicitly on the endpoint that skips it (`geo`).
|
|
398
|
+
*/
|
|
399
|
+
protected checkComputedSelect(controls: object | undefined): HttpError | undefined;
|
|
320
400
|
/**
|
|
321
401
|
* Root-path existence moved into {@link checkCapabilities}; the insights map
|
|
322
402
|
* only serves `$with` sub-controls here — the URL parser flattens
|
|
@@ -324,7 +404,7 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
324
404
|
* resolved against the target table through `isValidFieldPath`.
|
|
325
405
|
*/
|
|
326
406
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
327
|
-
/**
|
|
407
|
+
/** {@link checkComputedSelect} (before the controls DTO), then $with relations against the readable. */
|
|
328
408
|
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
329
409
|
/**
|
|
330
410
|
* Compute an embedding vector from a search term.
|
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, 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, 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, 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.
|
|
@@ -40,6 +40,8 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
40
40
|
private _serializedType?;
|
|
41
41
|
/** Cached full meta response (computed lazily on first meta() call). */
|
|
42
42
|
private _metaResponse?;
|
|
43
|
+
/** {@link metaCacheKey} the cached response was built for. */
|
|
44
|
+
private _metaResponseKey?;
|
|
43
45
|
/** Cached serialized form schemas keyed by `FormType.name` — populated lazily by {@link metaForm}. */
|
|
44
46
|
private _formSchemas;
|
|
45
47
|
constructor(boundType: T, controllerName: string, app: Moost, kindTag?: string);
|
|
@@ -126,10 +128,18 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
126
128
|
protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
|
|
127
129
|
/**
|
|
128
130
|
* **GET /meta** — returns the bound interface's metadata envelope. The
|
|
129
|
-
* static envelope is cached
|
|
130
|
-
* subclasses can prune the
|
|
131
|
+
* static envelope is cached (rebuilt when {@link metaCacheKey} changes);
|
|
132
|
+
* {@link applyMetaOverlay} runs per request so subclasses can prune the
|
|
133
|
+
* response by principal.
|
|
131
134
|
*/
|
|
132
135
|
meta(): Promise<TMetaResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Identity of the inputs the cached `/meta` envelope is built from — a new
|
|
138
|
+
* value rebuilds it. Default: constant (built once). The DB readable
|
|
139
|
+
* controller returns its capability index, which is rebuilt when the
|
|
140
|
+
* adapter's capabilities change (since 0.1.132).
|
|
141
|
+
*/
|
|
142
|
+
protected metaCacheKey(): unknown;
|
|
133
143
|
/**
|
|
134
144
|
* **GET /meta/form/:name** — returns the serialized schema of a form
|
|
135
145
|
* referenced by an action's `inputForm` field. The form name is the
|
|
@@ -180,10 +190,20 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
180
190
|
* `@db.table.filterable / sortable 'manual'` + `@db.column.*`). Policy applies
|
|
181
191
|
* to filters and `$sort` only; `$groupBy`, `$having` keys and aggregate
|
|
182
192
|
* `$field`s use the physical capability alone.
|
|
193
|
+
*
|
|
194
|
+
* A filter entry is judged by its predicate class (the core's `canFilterLeaf`):
|
|
195
|
+
* `filterable` is the value-comparison verdict, `filterOps` the narrower
|
|
196
|
+
* predicates that still pass where it is `false`.
|
|
197
|
+
*
|
|
198
|
+
* A calendar-bucket source (`bucketable`) needs the physical capability, a
|
|
199
|
+
* `number.timestamp` type, dimension status on a strict (dimension / measure
|
|
200
|
+
* declaring) table, and an adapter with calendar-bucket units.
|
|
183
201
|
*/
|
|
184
202
|
interface TFieldCapability {
|
|
185
|
-
/** A filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
203
|
+
/** A value-comparison filter on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
186
204
|
filterable: boolean;
|
|
205
|
+
/** Present when `filterable` is `false` yet narrower predicates (`$exists`, `$geoWithin`) pass the gate. */
|
|
206
|
+
filterOps?: string[];
|
|
187
207
|
/** A `$sort` on this path passes the gate (adapter ∧ ¬writeOnly ∧ ¬encrypted ∧ policy). */
|
|
188
208
|
sortable: boolean;
|
|
189
209
|
/** The path may appear in `$select`. `@db.writeOnly` fields are selectable — the seal strips them after the gate. */
|
|
@@ -194,6 +214,13 @@ interface TFieldCapability {
|
|
|
194
214
|
filterReason?: string;
|
|
195
215
|
/** Present when `sortable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
196
216
|
sortReason?: string;
|
|
217
|
+
/**
|
|
218
|
+
* A calendar bucket over this path passes the gate (physical ∧ timestamp ∧
|
|
219
|
+
* (¬strict ∨ dimension) ∧ adapter has calendar buckets). Since 0.1.132.
|
|
220
|
+
*/
|
|
221
|
+
bucketable: boolean;
|
|
222
|
+
/** Present when `bucketable` is `false` — the reason clause appended to the HTTP 400 message. */
|
|
223
|
+
bucketReason?: string;
|
|
197
224
|
}
|
|
198
225
|
/** One rejected path: `path` is the offending logical path, `message` the full sentence. */
|
|
199
226
|
interface TCapabilityVerdict {
|
|
@@ -201,13 +228,18 @@ interface TCapabilityVerdict {
|
|
|
201
228
|
message: string;
|
|
202
229
|
}
|
|
203
230
|
/** The readable members the index reads. */
|
|
204
|
-
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField">;
|
|
231
|
+
type TCapabilityReadable = Pick<AtscriptDbReadable, "type" | "fieldDescriptors" | "flatMap" | "navFields" | "relations" | "ignoredFields" | "canFilterField" | "canSortField" | "isGeoSearchable" | "calendarBucketUnits" | "dimensions" | "measures">;
|
|
205
232
|
/**
|
|
206
|
-
* Capability index of one readable
|
|
233
|
+
* Capability index of one readable.
|
|
207
234
|
*
|
|
208
235
|
* - {@link entries} feeds `/meta.fields` (listed leaves in descriptor order);
|
|
209
236
|
* - {@link check} is the request gate: same inputs, same answer.
|
|
210
237
|
*
|
|
238
|
+
* Built from the adapter's capabilities as they are NOW. Some are only known
|
|
239
|
+
* after schema sync (PostgreSQL learns PostGIS there), so owners rebuild the
|
|
240
|
+
* index when {@link adapterSignature} changes (see `AsDbReadableController`'s
|
|
241
|
+
* `capabilities` getter) instead of keeping a constructor-time snapshot.
|
|
242
|
+
*
|
|
211
243
|
* Paths outside the index are classified by the core's `classifyQueryPath`
|
|
212
244
|
* (the same rules the core backstop applies) — navigation path, nested-object
|
|
213
245
|
* parent, JSON descendant (relational adapters), encrypted descendant,
|
|
@@ -224,9 +256,30 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
224
256
|
readonly jsonParents: ReadonlySet<string>;
|
|
225
257
|
/** Descriptors carrying `@db.encrypted` (the ciphertext column on relational adapters). */
|
|
226
258
|
readonly encryptedFields: ReadonlySet<string>;
|
|
259
|
+
/** Every field descriptor's `physicalName` — names a calendar-bucket alias may not take. */
|
|
260
|
+
readonly physicalNames: ReadonlySet<string>;
|
|
261
|
+
/** Calendar-bucket units the adapter groups by, in `BUCKET_UNITS` order (`/meta.bucketUnits`). */
|
|
262
|
+
readonly bucketUnits: readonly BucketUnit[];
|
|
263
|
+
/** The adapter-level capabilities this index was built against — see {@link adapterSignature}. */
|
|
264
|
+
readonly signature: string;
|
|
265
|
+
/**
|
|
266
|
+
* The adapter-level capabilities that can change after construction (geo
|
|
267
|
+
* support, calendar-bucket units): an index whose {@link signature}
|
|
268
|
+
* differs from this is stale. Any new adapter-level input the index reads
|
|
269
|
+
* must be added here.
|
|
270
|
+
*/
|
|
271
|
+
static adapterSignature(source: Pick<TCapabilityReadable, "isGeoSearchable" | "calendarBucketUnits">): string;
|
|
227
272
|
private readonly _entries;
|
|
228
273
|
/** Nested-object parents (never listed, always selectable) → their listed leaves. */
|
|
229
274
|
private readonly _objectParents;
|
|
275
|
+
/**
|
|
276
|
+
* Declared dimensions when the table is strict (declares dimensions or
|
|
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;
|
|
230
283
|
/** Listed leaves — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
231
284
|
get leaves(): ReadonlyMap<string, unknown>;
|
|
232
285
|
/** Nested-object parents — the {@link TQueryPathSource} view for `classifyQueryPath`. */
|
|
@@ -249,8 +302,11 @@ declare class FieldCapabilityIndex implements TQueryPathSource {
|
|
|
249
302
|
* an untyped descendant of a JSON column (`address.nope`) is reported as
|
|
250
303
|
* `Unknown field`, not as "inside JSON-stored column" — clients pin that
|
|
251
304
|
* wording, so do not "align" it with the core backstop's text.
|
|
305
|
+
*
|
|
306
|
+
* `predicate` is a filter entry's class (`collectQueryPaths` records it per
|
|
307
|
+
* occurrence); it only matters for `op === "filter"` on a listed leaf.
|
|
252
308
|
*/
|
|
253
|
-
check(path: string, op: TQueryPathOp$1, exists: (path: string) => boolean): TCapabilityVerdict | undefined;
|
|
309
|
+
check(path: string, op: TQueryPathOp$1, exists: (path: string) => boolean, predicate?: TFilterPredicate): TCapabilityVerdict | undefined;
|
|
254
310
|
}
|
|
255
311
|
//#endregion
|
|
256
312
|
//#region src/as-db-readable.controller.d.ts
|
|
@@ -276,8 +332,17 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
276
332
|
* Per-path capability index (since 0.1.128): the ONE input both `/meta.fields`
|
|
277
333
|
* and the request gate ({@link checkCapabilities}) are computed from, so
|
|
278
334
|
* metadata and runtime can never diverge.
|
|
279
|
-
|
|
280
|
-
|
|
335
|
+
*
|
|
336
|
+
* Built on first use and rebuilt whenever the adapter-level capabilities
|
|
337
|
+
* change (`FieldCapabilityIndex.adapterSignature`: geo support, calendar
|
|
338
|
+
* buckets) — PostgreSQL learns PostGIS only during schema sync, which may
|
|
339
|
+
* run after this controller is constructed, so a constructor-time snapshot
|
|
340
|
+
* would keep advertising (and gating) the pre-sync answer (since 0.1.132).
|
|
341
|
+
*/
|
|
342
|
+
protected get capabilities(): FieldCapabilityIndex;
|
|
343
|
+
private _capabilities?;
|
|
344
|
+
/** `/meta` is a projection of {@link capabilities}: a rebuilt index rebuilds the cached envelope. */
|
|
345
|
+
protected metaCacheKey(): unknown;
|
|
281
346
|
/** Bound once: the field-existence check the gate hands to `capabilities.check`. */
|
|
282
347
|
private readonly _exists;
|
|
283
348
|
private readonly _preferredIdSet;
|
|
@@ -309,14 +374,29 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
309
374
|
*
|
|
310
375
|
* Rejections use the structured envelope `{ message, statusCode: 400,
|
|
311
376
|
* errors: [{ path, message }] }` — `path` is the offending logical path.
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
377
|
+
*
|
|
378
|
+
* Expects normalized `$select` computed entries ({@link checkComputedSelect}
|
|
379
|
+
* ran first); a bucket's source is checked like any other path (op
|
|
380
|
+
* `bucket`). After the per-path checks the core `$having` rule runs
|
|
381
|
+
* (`checkHavingKeys`: aliases or `$groupBy` fields only), so a readable mock
|
|
382
|
+
* and a real table answer alike.
|
|
315
383
|
*/
|
|
316
384
|
protected checkCapabilities(parsed: {
|
|
317
385
|
filter?: FilterExpr;
|
|
318
386
|
controls?: object;
|
|
319
387
|
}): HttpError | undefined;
|
|
388
|
+
/**
|
|
389
|
+
* The core's shared normalizer of `$select` computed entries
|
|
390
|
+
* (`resolveCalendarBuckets`) as a 400 with the core's wording and `path`
|
|
391
|
+
* (`$select` / `$groupBy`): entry shapes, calendar-bucket unit / zone /
|
|
392
|
+
* week start / alias, "grouped queries only", "must also appear in
|
|
393
|
+
* $groupBy", alias collisions with this table's fields. Runs once per
|
|
394
|
+
* request, before {@link checkCapabilities}: at the head of
|
|
395
|
+
* {@link validateParsed} — ahead of the controls DTO, which would otherwise
|
|
396
|
+
* answer a bucket in a non-grouped query with a generic type mismatch — or
|
|
397
|
+
* explicitly on the endpoint that skips it (`geo`).
|
|
398
|
+
*/
|
|
399
|
+
protected checkComputedSelect(controls: object | undefined): HttpError | undefined;
|
|
320
400
|
/**
|
|
321
401
|
* Root-path existence moved into {@link checkCapabilities}; the insights map
|
|
322
402
|
* only serves `$with` sub-controls here — the URL parser flattens
|
|
@@ -324,7 +404,7 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
324
404
|
* resolved against the target table through `isValidFieldPath`.
|
|
325
405
|
*/
|
|
326
406
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
327
|
-
/**
|
|
407
|
+
/** {@link checkComputedSelect} (before the controls DTO), then $with relations against the readable. */
|
|
328
408
|
protected validateParsed(parsed: Uniquery, type: "query" | "pages" | "getOne"): HttpError | undefined;
|
|
329
409
|
/**
|
|
330
410
|
* Compute an embedding vector from a search term.
|