@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.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; {@link applyMetaOverlay} runs per request so
130
- * subclasses can prune the response by principal.
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, built once per controller.
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
- protected readonly capabilities: FieldCapabilityIndex;
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
- * After the per-path checks the core `$having` rule runs (`checkHavingKeys`:
313
- * aliases or `$groupBy` fields only), so a readable mock and a real table
314
- * answer alike.
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
- /** Validates $with relations against the readable. */
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; {@link applyMetaOverlay} runs per request so
130
- * subclasses can prune the response by principal.
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, built once per controller.
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
- protected readonly capabilities: FieldCapabilityIndex;
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
- * After the per-path checks the core `$having` rule runs (`checkHavingKeys`:
313
- * aliases or `$groupBy` fields only), so a readable mock and a real table
314
- * answer alike.
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
- /** Validates $with relations against the readable. */
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.