@atscript/db 0.1.130 → 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/agg.cjs +19 -5
- package/dist/agg.d.cts +3 -4
- package/dist/agg.d.mts +3 -4
- package/dist/agg.mjs +2 -5
- package/dist/{db-readable-B7eYWS5q.d.cts → buckets-BvoYYQ2Q.d.cts} +1348 -1142
- package/dist/{db-readable-Bn1bV_eC.d.mts → buckets-D2XPrwSC.d.mts} +1348 -1142
- package/dist/{db-error-COrO58t5.mjs → db-error-D5uilS_A.mjs} +13 -1
- package/dist/{db-error-C4JuLcvb.cjs → db-error-DTkkeu5b.cjs} +18 -0
- package/dist/{db-space-C2UCnGHd.d.cts → db-space-BQQEgB3d.d.mts} +2 -2
- package/dist/{db-space-DdIPYD0Q.d.mts → db-space-Cslq5Dux.d.cts} +2 -2
- package/dist/{db-view-hkwQ-fHJ.cjs → db-view-CuFAhzwF.cjs} +487 -123
- package/dist/{db-view-BkF2xEGd.mjs → db-view-DVGWWhA3.mjs} +439 -123
- package/dist/index.cjs +13 -4
- package/dist/index.d.cts +105 -38
- package/dist/index.d.mts +105 -38
- package/dist/index.mjs +5 -5
- package/dist/{nested-writer-CkDo-ZfH.mjs → nested-writer-CqL24ojl.mjs} +1 -1
- package/dist/{nested-writer-DxPhmWFz.cjs → nested-writer-wk1EFUNY.cjs} +1 -1
- package/dist/ops.cjs +1 -1
- package/dist/ops.mjs +1 -1
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +1 -1
- package/dist/rel.d.mts +1 -1
- package/dist/rel.mjs +2 -2
- package/dist/{relation-loader-CUGcxJ18.mjs → relation-loader-B68R1LET.mjs} +1 -1
- package/dist/{relation-loader-C8GOpNYJ.cjs → relation-loader-BD4xANQJ.cjs} +1 -1
- package/dist/sync.cjs +1 -1
- package/dist/sync.d.cts +2 -2
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +1 -1
- package/dist/{validator-lkCJKuoo.cjs → validator-BtZbcLN2.cjs} +1 -1
- package/dist/{validator-wBARmD68.d.cts → validator-CewfnGZj.d.cts} +9 -2
- package/dist/{validator-wBARmD68.d.mts → validator-CewfnGZj.d.mts} +9 -2
- package/dist/{validator-CeD_fqyW.mjs → validator-Ch7UIQl9.mjs} +1 -1
- package/dist/validator.cjs +2 -2
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.mjs +2 -2
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { f as TFieldOps } from "./ops-AqhV7s9o.cjs";
|
|
2
|
-
import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, AggregateFn, AggregateQuery, AggregateQuery as AggregateQuery$1, AggregateResult, FieldOpsFor, FilterExpr, FilterExpr as FilterExpr$1, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
|
|
2
|
+
import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, AggregateFn, AggregateQuery, AggregateQuery as AggregateQuery$1, AggregateResult, BucketUnit, FieldOpsFor, FilterExpr, FilterExpr as FilterExpr$1, ResolvedBucket, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
|
|
3
3
|
import { FlatOf, FlatOf as FlatOf$1, NavPropsOf, NavPropsOf as NavPropsOf$1, OwnPropsOf, OwnPropsOf as OwnPropsOf$1, PrimaryKeyOf, PrimaryKeyOf as PrimaryKeyOf$1, TAtscriptAnnotatedType, TAtscriptDataType, TAtscriptTypeObject, TMetadataMap, TSerializedAnnotatedType, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/query/uniqu-select.d.ts
|
|
@@ -11,6 +11,11 @@ import { FlatOf, FlatOf as FlatOf$1, NavPropsOf, NavPropsOf as NavPropsOf$1, Own
|
|
|
11
11
|
* `controls.$select` is `UniquSelect | undefined`.
|
|
12
12
|
*
|
|
13
13
|
* For exclusion → inclusion inversion, pass `allFields` (physical field names).
|
|
14
|
+
*
|
|
15
|
+
* An array `$select` holds plain field names and computed entries —
|
|
16
|
+
* aggregates (`{ $fn, $field }`, {@link aggregates}) and calendar buckets
|
|
17
|
+
* (`{ $bucket, $field }`, {@link buckets}). Entries arrive normalized
|
|
18
|
+
* (`resolveCalendarBuckets` rejects any other shape before translation).
|
|
14
19
|
*/
|
|
15
20
|
declare class UniquSelect {
|
|
16
21
|
private static readonly UNRESOLVED;
|
|
@@ -19,12 +24,25 @@ declare class UniquSelect {
|
|
|
19
24
|
private _array;
|
|
20
25
|
private _projection;
|
|
21
26
|
private _aggregates;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The calendar buckets of an aggregate `$select`, normalized (canonical
|
|
29
|
+
* zone, week start, alias) with the PHYSICAL source `field` and its
|
|
30
|
+
* descriptor `fd`. `undefined` when there are none. A `$groupBy` key equal
|
|
31
|
+
* to a bucket's `alias` groups by that bucket (aliases never collide with
|
|
32
|
+
* columns). Since 0.1.132.
|
|
33
|
+
*/
|
|
34
|
+
readonly buckets: readonly TResolvedBucket[] | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* @param raw - the `$select` value (field paths already physical).
|
|
37
|
+
* @param allFields - physical field names, for exclusion-form inversion.
|
|
38
|
+
* @param buckets - the resolved calendar buckets of the raw `$select`'s
|
|
39
|
+
* `{ $bucket }` entries (the field mappers supply them — physical `field`,
|
|
40
|
+
* source `fd`).
|
|
41
|
+
*/
|
|
42
|
+
constructor(raw: UniqueryControls["$select"], allFields?: string[], buckets?: readonly TResolvedBucket[]);
|
|
25
43
|
/**
|
|
26
44
|
* Resolved inclusion array of plain field names (strings only).
|
|
27
|
-
*
|
|
45
|
+
* Computed entries (aggregates, calendar buckets) are filtered out.
|
|
28
46
|
* For exclusion form, inverts using `allFields` from constructor.
|
|
29
47
|
*/
|
|
30
48
|
get asArray(): string[] | undefined;
|
|
@@ -42,6 +60,8 @@ declare class UniquSelect {
|
|
|
42
60
|
get aggregates(): AggregateExpr[] | undefined;
|
|
43
61
|
/** Whether the $select contains any AggregateExpr entries. */
|
|
44
62
|
get hasAggregates(): boolean;
|
|
63
|
+
/** The calendar bucket whose alias is `key`, if any — how adapters resolve a `$groupBy` / `$having` key. */
|
|
64
|
+
bucketByAlias(key: string): TResolvedBucket | undefined;
|
|
45
65
|
}
|
|
46
66
|
//#endregion
|
|
47
67
|
//#region src/logger.d.ts
|
|
@@ -54,822 +74,539 @@ interface TGenericLogger {
|
|
|
54
74
|
}
|
|
55
75
|
declare const NoopLogger: TGenericLogger;
|
|
56
76
|
//#endregion
|
|
57
|
-
//#region src/
|
|
58
|
-
/**
|
|
59
|
-
* Finds the nearest ancestor of `path` that belongs to `set`.
|
|
60
|
-
* Used by both the build pipeline (in `_classifyFields`) and
|
|
61
|
-
* runtime reconstruction on the Readable.
|
|
62
|
-
*/
|
|
63
|
-
declare function findAncestorInSet(path: string, set: ReadonlySet<string>): string | undefined;
|
|
64
|
-
/** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
|
|
65
|
-
declare function isGeoPointType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
66
|
-
/**
|
|
67
|
-
* Returns true if the annotated type is acceptable for `@db.index.geo`:
|
|
68
|
-
* the `db.geoPoint` primitive or a structurally identical `number[]`
|
|
69
|
-
* (excluding `db.vector`, which is semantically an embedding).
|
|
70
|
-
*/
|
|
71
|
-
declare function isGeoIndexableType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
77
|
+
//#region src/strategies/field-mapping.d.ts
|
|
72
78
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* and constraint information derived from Atscript annotations. Built lazily
|
|
77
|
-
* on first access via {@link build}, then immutable.
|
|
78
|
-
*
|
|
79
|
-
* This class owns the build pipeline that was previously part of
|
|
80
|
-
* `AtscriptDbReadable._flatten()`. The Readable delegates all metadata
|
|
81
|
-
* access to this class.
|
|
79
|
+
* Strategy for mapping data between logical field shapes and physical storage.
|
|
80
|
+
* Two implementations: {@link DocumentFieldMapper} (nested objects, NoSQL)
|
|
81
|
+
* and `RelationalFieldMapper` (flattened columns, SQL).
|
|
82
82
|
*/
|
|
83
|
-
declare class
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
fieldDescriptors: readonly TDbFieldMeta[];
|
|
87
|
-
primaryKeys: string[];
|
|
88
|
-
preferredId: string[];
|
|
89
|
-
originalMetaIdFields: string[];
|
|
90
|
-
indexes: Map<string, TDbIndex>;
|
|
91
|
-
foreignKeys: Map<string, TDbForeignKey>;
|
|
92
|
-
relations: Map<string, TDbRelation>;
|
|
93
|
-
navFields: Set<string>;
|
|
94
|
-
ignoredFields: Set<string>;
|
|
95
|
-
uniqueProps: Set<string>;
|
|
96
|
-
defaults: Map<string, TDbDefaultValue>;
|
|
97
|
-
columnMap: Map<string, string>;
|
|
98
|
-
dimensions: string[];
|
|
99
|
-
measures: string[];
|
|
100
|
-
/** Logical field name annotated with `@db.column.version`, if any. */
|
|
101
|
-
versionField?: string;
|
|
102
|
-
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
103
|
-
quantityRefByField: Map<string, string>;
|
|
104
|
-
/** Logical paths annotated with `@db.encrypted` — stored as one opaque ciphertext column. */
|
|
105
|
-
encryptedFields: Set<string>;
|
|
106
|
-
pathToPhysical: Map<string, string>;
|
|
107
|
-
physicalToPath: Map<string, string>;
|
|
108
|
-
flattenedParents: Set<string>;
|
|
109
|
-
jsonFields: Set<string>;
|
|
110
|
-
selectExpansion: Map<string, string[]>;
|
|
111
|
-
booleanFields: Set<string>;
|
|
112
|
-
decimalFields: Set<string>;
|
|
113
|
-
allPhysicalFields: string[];
|
|
114
|
-
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
115
|
-
childrenByParent: Map<string, string[]>;
|
|
116
|
-
/** Precomputed parent path → optional child logical paths (replace-strategy null-fill in the patch decomposer). */
|
|
117
|
-
optionalLeavesByLogicalParent: Map<string, string[]>;
|
|
118
|
-
requiresMappings: boolean;
|
|
119
|
-
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
120
|
-
onlyColumnRenames: boolean;
|
|
121
|
-
toStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
122
|
-
fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
123
|
-
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
124
|
-
leafByPhysical: Map<string, TDbFieldMeta>;
|
|
125
|
-
/** Leaf field descriptors indexed by logical path (write/patch/filter paths). */
|
|
126
|
-
leafByLogical: Map<string, TDbFieldMeta>;
|
|
83
|
+
declare abstract class FieldMappingStrategy {
|
|
84
|
+
abstract reconstructFromRead(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
85
|
+
abstract translateQuery(query: Uniquery, meta: TableMetadata): DbQuery;
|
|
127
86
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* adapters only) this is built for every adapter, so the core path guard
|
|
131
|
-
* (`guardPaths`) can answer "does this path have physical storage here?"
|
|
132
|
-
* on nested-object adapters too.
|
|
87
|
+
* The physical path of a logical field path — a `__`-joined column
|
|
88
|
+
* (relational) or a document path with `@db.column` renames applied.
|
|
133
89
|
*/
|
|
134
|
-
|
|
90
|
+
protected abstract physicalPath(logical: string, meta: TableMetadata): string;
|
|
135
91
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* `jsonFields` set — so the path guard can classify JSON descendants on
|
|
139
|
-
* relational adapters. Empty on nested-object adapters (they keep native
|
|
140
|
-
* dotted paths as descriptors).
|
|
92
|
+
* Whether {@link physicalPath} can differ from the logical path for this
|
|
93
|
+
* table; `false` lets the path translations hand their input back as-is.
|
|
141
94
|
*/
|
|
142
|
-
|
|
143
|
-
private _built;
|
|
144
|
-
private _identifications?;
|
|
145
|
-
private _collateMap;
|
|
146
|
-
private _columnFromMap;
|
|
147
|
-
constructor(nestedObjects: boolean);
|
|
148
|
-
get isBuilt(): boolean;
|
|
95
|
+
protected renamesPaths(_meta: TableMetadata): boolean;
|
|
149
96
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
97
|
+
* Translates a grouped query to physical names: the filter and `$having`
|
|
98
|
+
* through {@link translateFilter}, and every field path in `$groupBy`,
|
|
99
|
+
* `$select` (plain and computed `$field`s) and `$sort` through
|
|
100
|
+
* {@link physicalPath}. Computed aliases pass through (a bucket alias never
|
|
101
|
+
* equals a field name).
|
|
152
102
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* 4. Classify fields and build path maps (skipped for nested-objects adapters)
|
|
158
|
-
* 5. `adapter.getMetadataOverrides()` → `_applyOverrides()` (PK/unique/inject adjustments)
|
|
159
|
-
* 6. Build field descriptors (TDbFieldMeta[])
|
|
160
|
-
* 7. Build leaf field indexes (skipped for nested-objects adapters)
|
|
161
|
-
* 8. Finalize indexes (resolve field names to physical)
|
|
162
|
-
* 9. `adapter.onAfterFlatten()` — adapter hook (read-only bookkeeping)
|
|
163
|
-
* 10. Build allPhysicalFields list
|
|
103
|
+
* `buckets` are the query's calendar buckets as the core's normalizer
|
|
104
|
+
* resolved them (`resolveCalendarBuckets` — `AtscriptDbReadable.aggregate`
|
|
105
|
+
* runs it before the guards); they reach adapters with `field` made
|
|
106
|
+
* physical and the source descriptor as `fd`.
|
|
164
107
|
*/
|
|
165
|
-
|
|
108
|
+
translateAggregateQuery(query: AggregateQuery, meta: TableMetadata, buckets: readonly ResolvedBucket[]): DbQuery;
|
|
109
|
+
/** Output aliases of the computed `$select` entries (aggregates and calendar buckets). */
|
|
110
|
+
private computedAliasSet;
|
|
166
111
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
112
|
+
* `$select` with its field paths made physical: array-form names and
|
|
113
|
+
* computed `$field`s (`'*'` kept), or the keys of the object
|
|
114
|
+
* (inclusion / exclusion) form.
|
|
169
115
|
*/
|
|
170
|
-
|
|
116
|
+
protected physicalSelect(select: NonNullable<UniqueryControls["$select"]>, meta: TableMetadata): NonNullable<UniqueryControls["$select"]>;
|
|
117
|
+
/** `$sort` with physical keys; computed `aliases` (grouped queries) pass through. */
|
|
118
|
+
protected physicalSort(sort: NonNullable<DbControls["$sort"]>, meta: TableMetadata, aliases?: ReadonlySet<string>): DbControls["$sort"];
|
|
171
119
|
/**
|
|
172
|
-
*
|
|
120
|
+
* Recursively walks a filter expression, applying `@db.column` key renames
|
|
121
|
+
* (document paths — {@link TableMetadata.documentPath}) and adapter-specific
|
|
122
|
+
* value formatting via `formatFilterValue`.
|
|
123
|
+
*
|
|
124
|
+
* The relational mapper overrides this to use `leafByLogical` for deeper
|
|
125
|
+
* key resolution (flattened nested paths).
|
|
173
126
|
*/
|
|
174
|
-
|
|
127
|
+
translateFilter(filter: FilterExpr, meta: TableMetadata): FilterExpr;
|
|
128
|
+
abstract prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
|
|
129
|
+
abstract translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
175
130
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* from pre-compiled types still fail fast.
|
|
131
|
+
* Reverse-maps `@db.column` renames on a row read from storage.
|
|
132
|
+
* Renames physical keys back to logical names in-place.
|
|
179
133
|
*/
|
|
180
|
-
|
|
181
|
-
/** Build-time diagnostics for `@db.index.geo` (§3 of the geo-index spec). */
|
|
182
|
-
private _validateGeoIndexField;
|
|
183
|
-
private _addIndexField;
|
|
134
|
+
protected reverseColumnRenames(row: Record<string, unknown>, meta: TableMetadata): void;
|
|
184
135
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
136
|
+
* Coerces field values from storage representation to JS types
|
|
137
|
+
* (booleans from 0/1, decimals from number to string).
|
|
187
138
|
*/
|
|
188
|
-
|
|
189
|
-
/** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
|
|
190
|
-
private _flattenedPrefix;
|
|
191
|
-
/** Nearest `@db.encrypted` ancestor of `path` (exclusive), or `undefined`. */
|
|
139
|
+
protected coerceFieldValues(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
192
140
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* adapter keeps them as descriptors (nested-object adapters do) — they are
|
|
196
|
-
* loaded with `$with`, never addressed as columns of this table.
|
|
141
|
+
* Applies adapter-specific fromStorage formatting to a row read from the database.
|
|
142
|
+
* Converts storage representations back to JS values (e.g. Date → epoch ms).
|
|
197
143
|
*/
|
|
198
|
-
|
|
144
|
+
protected applyFromStorageFormatters(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
199
145
|
/**
|
|
200
|
-
*
|
|
201
|
-
* read/write field classification in the RelationalFieldMapper.
|
|
146
|
+
* Sets a value at a dot-notation path, creating intermediate objects as needed.
|
|
202
147
|
*/
|
|
203
|
-
|
|
148
|
+
protected setNestedValue(obj: Record<string, unknown>, dotPath: string, value: unknown): void;
|
|
204
149
|
/**
|
|
205
|
-
*
|
|
206
|
-
* Called once during build() — everything it needs
|
|
207
|
-
* (flatMap, indexes, columnMap, etc.) is already populated.
|
|
150
|
+
* If all children of a flattened parent are null, collapse the parent to null.
|
|
208
151
|
*/
|
|
209
|
-
|
|
152
|
+
protected reconstructNullParent(obj: Record<string, unknown>, parentPath: string, meta: TableMetadata): void;
|
|
210
153
|
/**
|
|
211
|
-
*
|
|
154
|
+
* Applies adapter-specific value formatting to a single filter value.
|
|
155
|
+
* Handles direct values, operator objects ({$gt: v}), and $in/$nin arrays.
|
|
212
156
|
*/
|
|
213
|
-
|
|
214
|
-
private _finalizeIndexes;
|
|
157
|
+
protected formatFilterValue(physicalName: string, value: unknown, meta: TableMetadata): unknown;
|
|
215
158
|
/**
|
|
216
|
-
*
|
|
217
|
-
* (when present) followed by every unique index. Must run BEFORE
|
|
218
|
-
* `_finalizeIndexes` rewrites `index.fields[i].name` from logical to
|
|
219
|
-
* physical, so the field lists stay logical.
|
|
220
|
-
*
|
|
221
|
-
* Sources:
|
|
222
|
-
* - `this.primaryKeys` — composite-aware PK (one identification covering
|
|
223
|
-
* the PK columns).
|
|
224
|
-
* - `this.indexes` — user-declared `@db.index.unique` (single or compound).
|
|
225
|
-
* - `this.uniqueProps` — single-field uniques contributed by adapter
|
|
226
|
-
* overrides (`addUniqueFields`); these aren't reflected in
|
|
227
|
-
* `this.indexes` but still legitimate addressing identifications.
|
|
228
|
-
* Deduped against any single-field unique index already captured.
|
|
159
|
+
* Applies adapter-specific value formatting to prepared (physical-named) data.
|
|
229
160
|
*/
|
|
230
|
-
|
|
231
|
-
/** Legitimate row-identifier shapes — primary key first, then each unique index. */
|
|
232
|
-
getIdentifications(): readonly TIdentification[];
|
|
233
|
-
private _resolvePreferredId;
|
|
234
|
-
}
|
|
235
|
-
//#endregion
|
|
236
|
-
//#region src/types.d.ts
|
|
237
|
-
/** Controls with resolved projection. Used in the adapter interface. */
|
|
238
|
-
interface DbControls extends Omit<UniqueryControls, "$select"> {
|
|
239
|
-
$select?: UniquSelect;
|
|
240
|
-
}
|
|
241
|
-
/** Query object with resolved projection. Passed to adapter methods. */
|
|
242
|
-
interface DbQuery {
|
|
243
|
-
filter: FilterExpr;
|
|
244
|
-
controls: DbControls;
|
|
245
|
-
/** Pre-computed query insights (field → operators). Adapters may use this to apply query-time behaviour (e.g. collation). */
|
|
246
|
-
insights?: UniqueryInsights;
|
|
247
|
-
}
|
|
248
|
-
/** Describes an available search index exposed by a database adapter. */
|
|
249
|
-
interface TSearchIndexInfo {
|
|
250
|
-
/** Index name. Empty string or 'DEFAULT' for the default index. */
|
|
251
|
-
name: string;
|
|
252
|
-
/** Human-readable label for UI display. */
|
|
253
|
-
description?: string;
|
|
254
|
-
/** Index type: text search or vector similarity search. */
|
|
255
|
-
type?: "text" | "vector";
|
|
256
|
-
}
|
|
257
|
-
/** Relation summary in a meta response. */
|
|
258
|
-
interface TRelationInfo {
|
|
259
|
-
name: string;
|
|
260
|
-
direction: "to" | "from" | "via";
|
|
261
|
-
isArray: boolean;
|
|
262
|
-
}
|
|
263
|
-
/** Per-field capability flags in a meta response. */
|
|
264
|
-
interface TFieldMeta {
|
|
265
|
-
sortable: boolean;
|
|
266
|
-
filterable: boolean;
|
|
267
|
-
/** Present (true) when the field is `@db.encrypted` — stored as ciphertext at rest. */
|
|
268
|
-
encrypted?: boolean;
|
|
269
|
-
/** Present (true) when the field carries a `@db.index.geo` geospatial index. */
|
|
270
|
-
geo?: boolean;
|
|
161
|
+
protected formatWriteValues(data: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
271
162
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* present in read responses. UIs render it as a set-only input.
|
|
163
|
+
* Prepares primary key values and strips ignored fields.
|
|
164
|
+
* Shared pre-processing for both document and relational write paths.
|
|
275
165
|
*/
|
|
276
|
-
|
|
166
|
+
protected prepareCommon(data: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): void;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Field mapper for document-oriented adapters (e.g. MongoDB).
|
|
170
|
+
* Nested objects are preserved as-is. Only applies column renames and
|
|
171
|
+
* value coercion.
|
|
172
|
+
*/
|
|
173
|
+
declare class DocumentFieldMapper extends FieldMappingStrategy {
|
|
174
|
+
reconstructFromRead(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
277
175
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* is accepted (`sortable` does). Since 0.1.128.
|
|
176
|
+
* Every field-path position goes through `@db.column` renames
|
|
177
|
+
* ({@link TableMetadata.documentPath}): filter keys, `$select` fields
|
|
178
|
+
* (array, inclusion and exclusion forms) and `$sort` keys.
|
|
282
179
|
*/
|
|
283
|
-
|
|
180
|
+
translateQuery(query: Uniquery, meta: TableMetadata): DbQuery;
|
|
181
|
+
/** A document path with `@db.column` renames ({@link TableMetadata.documentPath}). */
|
|
182
|
+
protected physicalPath(logical: string, meta: TableMetadata): string;
|
|
183
|
+
protected renamesPaths(meta: TableMetadata): boolean;
|
|
184
|
+
prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
|
|
185
|
+
translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
284
186
|
}
|
|
285
|
-
|
|
286
|
-
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/encryption.d.ts
|
|
287
189
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* accepted UniQuery control whitelist for read ops (`[]` for write ops, which
|
|
291
|
-
* take no controls — presence still signals "allowed").
|
|
190
|
+
* Configuration for field-level encryption at rest (`@db.encrypted`).
|
|
191
|
+
* Passed to `DbSpace` via the options bag.
|
|
292
192
|
*/
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
|
|
296
|
-
searchable: boolean;
|
|
297
|
-
vectorSearchable: boolean;
|
|
298
|
-
/** Whether the adapter supports `geoSearch()` AND the table declares a geo index. */
|
|
299
|
-
geoSearchable?: boolean;
|
|
300
|
-
searchIndexes: TSearchIndexInfo[];
|
|
301
|
-
primaryKeys: string[];
|
|
302
|
-
preferredId: string[];
|
|
303
|
-
relations: TRelationInfo[];
|
|
304
|
-
fields: Record<string, TFieldMeta>;
|
|
305
|
-
type: TSerializedAnnotatedType;
|
|
306
|
-
actions: TDbActionInfo[];
|
|
307
|
-
crud: TCrudPermissions;
|
|
193
|
+
interface TDbEncryptionOptions {
|
|
194
|
+
/** Key used for all new writes. */
|
|
195
|
+
defaultKeyId: string;
|
|
308
196
|
/**
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* the
|
|
197
|
+
* Key registry: keyId → 32-byte key (Buffer, or base64/hex/utf8 string).
|
|
198
|
+
* Decryption looks keys up by the keyId recorded in each value's envelope,
|
|
199
|
+
* so old keys stay in the registry for as long as data encrypted with them exists.
|
|
312
200
|
*/
|
|
313
|
-
|
|
201
|
+
keys?: Record<string, string | Buffer>;
|
|
202
|
+
/**
|
|
203
|
+
* Alternative/supplement to `keys`: async resolver (KMS, Vault, env indirection).
|
|
204
|
+
* Called once per keyId, result cached for the process lifetime.
|
|
205
|
+
*/
|
|
206
|
+
resolveKey?: (keyId: string) => Promise<string | Buffer> | string | Buffer;
|
|
207
|
+
/**
|
|
208
|
+
* What to do when a stored value is NOT a valid envelope (pre-existing
|
|
209
|
+
* plaintext rows, e.g. when @db.encrypted is added to a live column).
|
|
210
|
+
* - 'error' (default): fail the read with DbError("ENC_NOT_ENCRYPTED")
|
|
211
|
+
* - 'passthrough': return the raw value as-is (migration window mode);
|
|
212
|
+
* the value gets encrypted on its next write.
|
|
213
|
+
*/
|
|
214
|
+
onUnencrypted?: "error" | "passthrough";
|
|
215
|
+
}
|
|
216
|
+
/** Decryption context — carried into error messages (never the key itself). */
|
|
217
|
+
interface TDecryptContext {
|
|
218
|
+
table?: string;
|
|
219
|
+
field?: string;
|
|
314
220
|
}
|
|
315
|
-
/** Where the action applies on the UI. */
|
|
316
|
-
type TDbActionLevel = "table" | "row" | "rows";
|
|
317
221
|
/**
|
|
318
|
-
*
|
|
222
|
+
* AES-256-GCM envelope encryption service for `@db.encrypted` fields.
|
|
319
223
|
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
224
|
+
* Owned by `DbSpace` and shared across all tables in the space. Values are
|
|
225
|
+
* `JSON.stringify`'d before encryption (type-exact round-trips) and stored as
|
|
226
|
+
* a single ASCII envelope string: `aes1$<keyId>$<iv>$<tag>$<ciphertext>`.
|
|
227
|
+
*
|
|
228
|
+
* Key material is validated eagerly at construction (`ENC_KEY_INVALID`);
|
|
229
|
+
* `resolveKey` lookups are cached per keyId for the process lifetime.
|
|
325
230
|
*/
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
231
|
+
declare class DbEncryption {
|
|
232
|
+
readonly defaultKeyId: string;
|
|
233
|
+
readonly onUnencrypted: "error" | "passthrough";
|
|
234
|
+
private readonly _keys;
|
|
235
|
+
private readonly _resolveKey?;
|
|
236
|
+
private readonly _resolved;
|
|
237
|
+
constructor(options: TDbEncryptionOptions);
|
|
238
|
+
/** True when `value` looks like an encryption envelope produced by this service. */
|
|
239
|
+
isEnvelope(value: unknown): value is string;
|
|
240
|
+
/** Encrypts a JSON-serializable value into an envelope string using the default key. */
|
|
241
|
+
encrypt(value: unknown): Promise<string>;
|
|
242
|
+
/** Decrypts an envelope string back into its plaintext value. */
|
|
243
|
+
decrypt(envelope: string, ctx?: TDecryptContext): Promise<unknown>;
|
|
244
|
+
private _decryptFailed;
|
|
245
|
+
private _getKey;
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/table/db-readable.d.ts
|
|
329
249
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
250
|
+
* Extracts nav prop names from a query's `$with` array.
|
|
251
|
+
* Returns `never` when `$with` is absent → all nav props stripped from response.
|
|
252
|
+
*/
|
|
253
|
+
type ExtractWith<Q> = Q extends {
|
|
254
|
+
controls: {
|
|
255
|
+
$with: Array<{
|
|
256
|
+
name: infer N extends string;
|
|
257
|
+
}>;
|
|
258
|
+
};
|
|
259
|
+
} ? N : never;
|
|
260
|
+
/**
|
|
261
|
+
* Computes the response type for a query:
|
|
262
|
+
* - Strips all nav props from the base DataType
|
|
263
|
+
* - Adds back only the nav props requested via `$with`
|
|
332
264
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
265
|
+
* When no `$with` is provided, result is `Omit<DataType, keyof NavType>`.
|
|
266
|
+
* When `$with: [{ name: 'author' }]`, result includes `author` from DataType.
|
|
267
|
+
* When the query type is not a literal (e.g. a variable typed as `Uniquery`),
|
|
268
|
+
* falls back to `DataType` (all nav props optional, as declared).
|
|
336
269
|
*/
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
270
|
+
type DbResponse<Data, Nav, Q> = [keyof Nav] extends [never] ? Data : string extends keyof Nav ? Data : Omit<Data, keyof Nav & string> & Pick<Data, ExtractWith<Q> & keyof Data & string>;
|
|
271
|
+
/**
|
|
272
|
+
* Resolves the design type from an annotated type.
|
|
273
|
+
* Encapsulates the `kind === ''` check and fallback logic that
|
|
274
|
+
* otherwise trips up every adapter author.
|
|
275
|
+
*
|
|
276
|
+
* For union types (e.g., from flattened `{...} | {...}` objects):
|
|
277
|
+
* - If all members resolve to the same type → returns that type (strong type)
|
|
278
|
+
* - If members disagree → returns `'union'` (out of scope for type management)
|
|
279
|
+
*/
|
|
280
|
+
declare function resolveDesignType(fieldType: TAtscriptAnnotatedType): string;
|
|
281
|
+
/**
|
|
282
|
+
* Shared read-only database abstraction driven by Atscript annotations.
|
|
283
|
+
*
|
|
284
|
+
* Contains all field metadata computation, read operations, query translation,
|
|
285
|
+
* relation loading, and result reconstruction. Extended by both
|
|
286
|
+
* {@link AtscriptDbTable} (adds write operations) and {@link AtscriptDbView}
|
|
287
|
+
* (adds view plan/DDL).
|
|
288
|
+
*/
|
|
289
|
+
declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>, _FlatType = NullableOptional<FlatOf<T>>, A extends BaseDbAdapter = BaseDbAdapter, IdType = PrimaryKeyOf<T>, OwnProps = NullableOptional<OwnPropsOf<T>>, NavType extends Record<string, unknown> = NavPropsOf<T>> {
|
|
290
|
+
protected readonly _type: T;
|
|
291
|
+
protected readonly adapter: A;
|
|
292
|
+
protected readonly logger: TGenericLogger;
|
|
293
|
+
protected readonly _tableResolver?: TTableResolver | undefined;
|
|
294
|
+
/** Resolved table/collection/view name. */
|
|
295
|
+
readonly tableName: string;
|
|
296
|
+
/** Database schema/namespace from `@db.schema` (if set). */
|
|
297
|
+
readonly schema: string | undefined;
|
|
298
|
+
/** Sync method from `@db.sync.method` ('drop' | 'recreate' | undefined). */
|
|
299
|
+
protected readonly _syncMethod: "drop" | "recreate" | undefined;
|
|
300
|
+
/** Previous table/view name from `@db.table.renamed` or `@db.view.renamed`. */
|
|
301
|
+
readonly renamedFrom: string | undefined;
|
|
302
|
+
/** Computed metadata for this table/view. Built lazily on first access. */
|
|
303
|
+
protected readonly _meta: TableMetadata;
|
|
304
|
+
/** Strategy for mapping between logical field shapes and physical storage. */
|
|
305
|
+
protected readonly _fieldMapper: FieldMappingStrategy;
|
|
306
|
+
protected _writeTableResolver?: TWriteTableResolver;
|
|
307
|
+
/** Encryption service for `@db.encrypted` fields — set by `DbSpace` from its options. */
|
|
308
|
+
protected _encryption?: DbEncryption;
|
|
309
|
+
private _metaIdPhysical;
|
|
310
|
+
constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver | undefined);
|
|
348
311
|
/**
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* current selection has exactly one PK) and `[1]` otherwise.
|
|
353
|
-
*
|
|
354
|
-
* Placeholder substitution is UI-resolved, not server-parsed. Conventional
|
|
355
|
-
* placeholders: `$1` for the single PK (singular form) and `$N` for the
|
|
356
|
-
* count (plural form), e.g. `['Delete order $1?', 'Delete $N orders?']`.
|
|
312
|
+
* Sets the encryption service used for `@db.encrypted` fields.
|
|
313
|
+
* Called by `DbSpace` after table/view creation when the space was
|
|
314
|
+
* configured with an `encryption` options block.
|
|
357
315
|
*/
|
|
358
|
-
|
|
316
|
+
setEncryption(encryption: DbEncryption | undefined): void;
|
|
317
|
+
/** Ensures metadata is built. Called before any metadata access. */
|
|
318
|
+
protected _ensureBuilt(): void;
|
|
359
319
|
/**
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* resolution between actions sharing the same key is also up to the UI;
|
|
364
|
-
* the server does no dedup.
|
|
320
|
+
* Built table metadata. Triggers a lazy build on first access — safe to call
|
|
321
|
+
* from peer tables that need this one's relations / nav fields before any
|
|
322
|
+
* operation has run against it directly.
|
|
365
323
|
*/
|
|
366
|
-
|
|
324
|
+
getMetadata(): TableMetadata;
|
|
325
|
+
protected _ensureSearchable(): void;
|
|
326
|
+
/** Engine-agnostic query-time guards (encrypted-field refs, $geoWithin shape). */
|
|
327
|
+
protected _guardQuery(query: Uniquery | undefined): void;
|
|
328
|
+
private _encryptedPathsCache?;
|
|
329
|
+
/** Pre-split `encryptedFields` paths — computed once, reused on every read/write. */
|
|
330
|
+
protected get _encryptedPaths(): Array<{
|
|
331
|
+
path: string;
|
|
332
|
+
segments: string[];
|
|
333
|
+
leaf: string;
|
|
334
|
+
}>;
|
|
367
335
|
/**
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* action's handler ran — the server is authoritative; this field is purely
|
|
374
|
-
* a UI hint.
|
|
336
|
+
* Walks all but the last of `segments` down from `root`, returning the
|
|
337
|
+
* object holding the leaf — or `undefined` when the path is unreachable
|
|
338
|
+
* (a missing, non-object, or array step). With `cloneParents`, every
|
|
339
|
+
* traversed object is shallow-cloned and re-linked so caller-shared
|
|
340
|
+
* nested objects are never mutated.
|
|
375
341
|
*/
|
|
376
|
-
|
|
342
|
+
protected _walkToLeafParent(root: Record<string, unknown>, segments: string[], cloneParents: boolean): Record<string, unknown> | undefined;
|
|
377
343
|
/**
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* `@InputForm(FormType)` parameter. Clients fetch the serialized schema via
|
|
381
|
-
* `GET /meta/form/:name` on the same controller and render a form to
|
|
382
|
-
* collect the `input` field of the action's request envelope.
|
|
344
|
+
* Decrypts `@db.encrypted` fields on reconstructed rows (in place).
|
|
345
|
+
* Non-envelope stored values follow the configured `onUnencrypted` policy.
|
|
383
346
|
*/
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
interface TDbIndexField {
|
|
402
|
-
name: string;
|
|
403
|
-
sort: "asc" | "desc";
|
|
404
|
-
weight?: number;
|
|
347
|
+
protected _decryptRows(rows: Array<Record<string, unknown>>): Promise<void>;
|
|
348
|
+
/** Whether this readable is a view (overridden in AtscriptDbView). */
|
|
349
|
+
get isView(): boolean;
|
|
350
|
+
/** Returns the underlying adapter with its concrete type preserved. */
|
|
351
|
+
getAdapter(): A;
|
|
352
|
+
/** The raw annotated type. */
|
|
353
|
+
get type(): TAtscriptAnnotatedType<TAtscriptTypeObject>;
|
|
354
|
+
/** Lazily-built flat map of all fields (dot-notation paths → annotated types). */
|
|
355
|
+
get flatMap(): Map<string, TAtscriptAnnotatedType>;
|
|
356
|
+
/** All computed indexes from `@db.index.*` annotations. */
|
|
357
|
+
get indexes(): Map<string, TDbIndex>;
|
|
358
|
+
/** Primary key field names from `@meta.id`. */
|
|
359
|
+
get primaryKeys(): readonly string[];
|
|
360
|
+
/** Preferred row identifier field names. Defaults to primary keys. */
|
|
361
|
+
get preferredId(): readonly string[];
|
|
362
|
+
/** Legitimate row-identifier shapes (primary key + every unique index). */
|
|
363
|
+
get identifications(): readonly TIdentification[];
|
|
405
364
|
/**
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*/
|
|
412
|
-
|
|
365
|
+
* Physical column name of the single `@meta.id` field, or `null` when the
|
|
366
|
+
* schema has zero or multiple `@meta.id` fields. Used by adapters to return
|
|
367
|
+
* the user's logical ID instead of the DB-generated one on insert.
|
|
368
|
+
*
|
|
369
|
+
* @internal Adapter-facing surface; not part of the consumer API.
|
|
370
|
+
*/
|
|
371
|
+
get metaIdPhysical(): string | null;
|
|
413
372
|
/**
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* re-resolving the field type. Undefined when the field cannot be resolved.
|
|
373
|
+
* Physical column name of the field annotated with `@db.column.version`, or
|
|
374
|
+
* `undefined` when the table has no version column. Used by adapters and the
|
|
375
|
+
* REST integration to drive optimistic concurrency control (OCC).
|
|
418
376
|
*/
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
|
|
444
|
-
/** Whether this is a composite key (multiple fields). */
|
|
445
|
-
isComposite: boolean;
|
|
446
|
-
}
|
|
447
|
-
/** A legitimate row-identifier shape: primary key or a unique index. */
|
|
448
|
-
interface TIdentification {
|
|
449
|
-
/** Logical (path) field names that form this identifier. */
|
|
450
|
-
fields: readonly string[];
|
|
451
|
-
/** `'primaryKey'` for the PK; the unique-index name otherwise. */
|
|
452
|
-
source: string;
|
|
453
|
-
}
|
|
454
|
-
type TDbStorageType = "column" | "flattened" | "json";
|
|
455
|
-
interface TDbFieldMeta {
|
|
456
|
-
/** The dot-notation path to this field (logical name). */
|
|
457
|
-
path: string;
|
|
458
|
-
/** The annotated type for this field. */
|
|
459
|
-
type: TAtscriptAnnotatedType;
|
|
460
|
-
/** Physical column/field name (from @db.column, __-separated for flattened, or same as path). */
|
|
461
|
-
physicalName: string;
|
|
462
|
-
/** Resolved design type: 'string', 'number', 'boolean', 'object', 'json', etc. */
|
|
463
|
-
designType: string;
|
|
464
|
-
/** Whether the field is optional. */
|
|
465
|
-
optional: boolean;
|
|
466
|
-
/** Whether this field is part of the primary key (@meta.id). */
|
|
467
|
-
isPrimaryKey: boolean;
|
|
468
|
-
/** Whether this field is excluded from the DB (@db.ignore). */
|
|
469
|
-
ignored: boolean;
|
|
470
|
-
/** Default value from @db.default.* */
|
|
471
|
-
defaultValue?: TDbDefaultValue;
|
|
377
|
+
get versionColumn(): string | undefined;
|
|
378
|
+
/** Dimension fields from `@db.column.dimension`. */
|
|
379
|
+
get dimensions(): readonly string[];
|
|
380
|
+
/** Measure fields from `@db.column.measure`. */
|
|
381
|
+
get measures(): readonly string[];
|
|
382
|
+
/** Sync method for structural changes: 'drop' (lossy), 'recreate' (lossless), or undefined (manual). */
|
|
383
|
+
get syncMethod(): "drop" | "recreate" | undefined;
|
|
384
|
+
/** Logical → physical column name mapping from `@db.column`. */
|
|
385
|
+
get columnMap(): ReadonlyMap<string, string>;
|
|
386
|
+
/** Default values from `@db.default.*`. */
|
|
387
|
+
get defaults(): ReadonlyMap<string, TDbDefaultValue>;
|
|
388
|
+
/** Fields excluded from DB via `@db.ignore`. */
|
|
389
|
+
get ignoredFields(): ReadonlySet<string>;
|
|
390
|
+
/** Navigational fields (`@db.rel.to` / `@db.rel.from`) — not stored as columns. */
|
|
391
|
+
get navFields(): ReadonlySet<string>;
|
|
392
|
+
/** Physical field names used to invert exclude-mode `$select` into a SELECT list. */
|
|
393
|
+
get allPhysicalFields(): readonly string[];
|
|
394
|
+
/** Single-field unique index properties. */
|
|
395
|
+
get uniqueProps(): ReadonlySet<string>;
|
|
396
|
+
/** Foreign key constraints from `@db.rel.FK` annotations. */
|
|
397
|
+
get foreignKeys(): ReadonlyMap<string, TDbForeignKey>;
|
|
398
|
+
/** Navigational relation metadata from `@db.rel.to` / `@db.rel.from`. */
|
|
399
|
+
get relations(): ReadonlyMap<string, TDbRelation>;
|
|
400
|
+
/** The underlying database adapter instance. */
|
|
401
|
+
get dbAdapter(): A;
|
|
472
402
|
/**
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* - 'flattened': a leaf scalar from a flattened nested object
|
|
476
|
-
* - 'json': stored as a single JSON column (arrays, @db.json fields)
|
|
403
|
+
* Enables or disables verbose (debug-level) DB call logging for this table/view.
|
|
404
|
+
* When disabled (default), no log strings are constructed — zero overhead.
|
|
477
405
|
*/
|
|
478
|
-
|
|
406
|
+
setVerbose(enabled: boolean): void;
|
|
407
|
+
/** Precomputed logical dot-path → physical column name map. */
|
|
408
|
+
get pathToPhysical(): ReadonlyMap<string, string>;
|
|
409
|
+
/** Precomputed physical column name → logical dot-path map (inverse). */
|
|
410
|
+
get physicalToPath(): ReadonlyMap<string, string>;
|
|
411
|
+
/** Descriptor for the primary ID field(s). */
|
|
412
|
+
getIdDescriptor(): TIdDescriptor;
|
|
479
413
|
/**
|
|
480
|
-
*
|
|
481
|
-
* E.g., for physicalName 'contact__email', this is 'contact.email'.
|
|
482
|
-
* Undefined for non-flattened fields.
|
|
414
|
+
* Pre-computed field metadata for adapter use.
|
|
483
415
|
*/
|
|
484
|
-
|
|
485
|
-
/** Old physical column name from @db.column.renamed (for rename migration). */
|
|
486
|
-
renamedFrom?: string;
|
|
487
|
-
/** Collation from @db.column.collate (e.g. 'nocase', 'binary', 'unicode'). */
|
|
488
|
-
collate?: TDbCollation;
|
|
416
|
+
get fieldDescriptors(): readonly TDbFieldMeta[];
|
|
489
417
|
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
418
|
+
* Resolves whether `path` references a real field — directly via `flatMap`
|
|
419
|
+
* or transitively through a nav relation by recursing into the target
|
|
420
|
+
* table. Defense-in-depth for query-path validation: `flattenAnnotatedType`
|
|
421
|
+
* still truncates real self-referential cycles, so paths like
|
|
422
|
+
* `parent.parent.name` on a self-ref schema would miss `flatMap.has` but
|
|
423
|
+
* remain valid field references on the target.
|
|
424
|
+
*
|
|
425
|
+
* Cycle-safe via a visited set keyed on `<tableName>:<navField>`.
|
|
494
426
|
*/
|
|
495
|
-
|
|
496
|
-
/** Literal currency code from `@db.amount.currency 'EUR'`. */
|
|
497
|
-
currencyCode?: string;
|
|
498
|
-
/** Sibling field path from `@db.amount.currency.ref 'fieldName'`. */
|
|
499
|
-
currencyRefField?: string;
|
|
500
|
-
/** Literal unit-of-measure from `@db.unit 'kg'`. */
|
|
501
|
-
unitCode?: string;
|
|
502
|
-
/** Sibling field path from `@db.unit.ref 'fieldName'`. */
|
|
503
|
-
unitRefField?: string;
|
|
427
|
+
isValidFieldPath(path: string, _visited?: Set<string>): boolean;
|
|
504
428
|
/**
|
|
505
|
-
*
|
|
506
|
-
* Adapters use this in `typeMapper` to produce matching DB types for FK columns
|
|
507
|
-
* (e.g., `typeMapper(field.fkTargetField)` to inherit the target PK's DB type).
|
|
508
|
-
* Undefined for non-FK fields or when the target cannot be resolved.
|
|
429
|
+
* Creates a new validator with custom options.
|
|
509
430
|
*/
|
|
510
|
-
|
|
431
|
+
createValidator(opts?: Partial<TValidatorOptions>): Validator<T, DataType>;
|
|
511
432
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
* The descriptor's `designType` is forced to `'string'` (ciphertext envelope);
|
|
516
|
-
* the declared type stays available via `type` for validation.
|
|
433
|
+
* Finds a single record matching the query.
|
|
434
|
+
* The return type automatically excludes nav props unless they are
|
|
435
|
+
* explicitly requested via `$with`.
|
|
517
436
|
*/
|
|
518
|
-
|
|
437
|
+
findOne<Q extends Uniquery<OwnProps, NavType>>(query: Q): Promise<DbResponse<DataType, NavType, Q> | null>;
|
|
519
438
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
439
|
+
* Finds all records matching the query.
|
|
440
|
+
* The return type automatically excludes nav props unless they are
|
|
441
|
+
* explicitly requested via `$with`.
|
|
522
442
|
*/
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
fields: string[];
|
|
535
|
-
/** Target table name (from the chain ref's type @db.table annotation). */
|
|
536
|
-
targetTable: string;
|
|
537
|
-
/** Target field names on the referenced table. */
|
|
538
|
-
targetFields: string[];
|
|
539
|
-
/** Lazy reference to the target annotated type (for on-demand table resolution). */
|
|
540
|
-
targetTypeRef?: () => TAtscriptAnnotatedType;
|
|
541
|
-
/** Alias grouping FK fields (if any). */
|
|
542
|
-
alias?: string;
|
|
543
|
-
/** Referential action on delete. */
|
|
544
|
-
onDelete?: TDbReferentialAction;
|
|
545
|
-
/** Referential action on update. */
|
|
546
|
-
onUpdate?: TDbReferentialAction;
|
|
547
|
-
}
|
|
548
|
-
/** Describes an existing column in the database (from introspection). */
|
|
549
|
-
interface TExistingColumn {
|
|
550
|
-
name: string;
|
|
551
|
-
type: string;
|
|
552
|
-
notnull: boolean;
|
|
553
|
-
pk: boolean;
|
|
554
|
-
/** Serialized default value (e.g., "'active'", "NULL"). */
|
|
555
|
-
dflt_value?: string;
|
|
556
|
-
}
|
|
557
|
-
/** Result of comparing desired schema against existing database columns. */
|
|
558
|
-
interface TColumnDiff {
|
|
559
|
-
added: TDbFieldMeta[];
|
|
560
|
-
removed: TExistingColumn[];
|
|
561
|
-
renamed: Array<{
|
|
562
|
-
field: TDbFieldMeta;
|
|
563
|
-
oldName: string;
|
|
564
|
-
}>;
|
|
565
|
-
typeChanged: Array<{
|
|
566
|
-
field: TDbFieldMeta;
|
|
567
|
-
existingType: string;
|
|
568
|
-
}>;
|
|
569
|
-
nullableChanged: Array<{
|
|
570
|
-
field: TDbFieldMeta;
|
|
571
|
-
wasNullable: boolean;
|
|
572
|
-
}>;
|
|
573
|
-
defaultChanged: Array<{
|
|
574
|
-
field: TDbFieldMeta;
|
|
575
|
-
oldDefault?: string;
|
|
576
|
-
newDefault?: string;
|
|
577
|
-
}>;
|
|
578
|
-
conflicts: Array<{
|
|
579
|
-
field: TDbFieldMeta;
|
|
580
|
-
oldName: string;
|
|
581
|
-
conflictsWith: string;
|
|
443
|
+
findMany<Q extends Uniquery<OwnProps, NavType>>(query: Q): Promise<Array<DbResponse<DataType, NavType, Q>>>;
|
|
444
|
+
/**
|
|
445
|
+
* Counts records matching the query.
|
|
446
|
+
*/
|
|
447
|
+
count(query?: Uniquery<OwnProps, NavType>): Promise<number>;
|
|
448
|
+
/**
|
|
449
|
+
* Finds records and total count in a single logical call.
|
|
450
|
+
*/
|
|
451
|
+
findManyWithCount<Q extends Uniquery<OwnProps, NavType>>(query: Q): Promise<{
|
|
452
|
+
data: Array<DbResponse<DataType, NavType, Q>>;
|
|
453
|
+
count: number;
|
|
582
454
|
}>;
|
|
583
455
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
456
|
+
* Executes an aggregate query with GROUP BY and aggregate functions.
|
|
457
|
+
*
|
|
458
|
+
* Validates:
|
|
459
|
+
* - `$select` computed entries and calendar buckets (the shared normalizer,
|
|
460
|
+
* `resolveCalendarBuckets`: shapes, unit, zone, alias, grouping)
|
|
461
|
+
* - Plain fields in $select are a subset of $groupBy
|
|
462
|
+
* - When dimensions/measures are defined (strict mode): $groupBy fields
|
|
463
|
+
* must be dimensions (a calendar bucket's source field included),
|
|
464
|
+
* aggregate $field values must be measures (or '*')
|
|
465
|
+
* - the path guard (a bucket source must be a timestamp field) and the
|
|
466
|
+
* adapter's calendar-bucket units (`BUCKET_NOT_SUPPORTED`)
|
|
467
|
+
*
|
|
468
|
+
* Translates field names, delegates to adapter.aggregate(),
|
|
469
|
+
* then reverse-maps and applies fromStorage formatters on results.
|
|
589
470
|
*/
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
|
|
601
|
-
* (outbound: declared on the table that owns it).
|
|
602
|
-
*/
|
|
603
|
-
interface TExistingForeignKey {
|
|
604
|
-
/** Local (referencing) columns, in constraint order. */
|
|
605
|
-
fields: string[];
|
|
606
|
-
/** Referenced table name. */
|
|
607
|
-
targetTable: string;
|
|
608
|
-
/** Referenced columns, in constraint order. */
|
|
609
|
-
targetFields: string[];
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* A live foreign key that REFERENCES a given table (inbound edge), as returned
|
|
613
|
-
* by `BaseDbAdapter.getReferencingForeignKeys(tableName)`.
|
|
614
|
-
*/
|
|
615
|
-
interface TReferencingForeignKey {
|
|
616
|
-
/** The referencing (child) table. */
|
|
617
|
-
table: string;
|
|
618
|
-
/** Referencing columns on `table`, in constraint order. */
|
|
619
|
-
fields: string[];
|
|
620
|
-
/** Referenced columns on the queried table, in constraint order. */
|
|
621
|
-
targetFields: string[];
|
|
622
|
-
}
|
|
623
|
-
/** Kind of a physical database object, as returned by `BaseDbAdapter.getObjectKind`. */
|
|
624
|
-
type TDbObjectKind = "table" | "view" | "materialized";
|
|
625
|
-
/** Options accepted by `BaseDbAdapter.ensureTable`. */
|
|
626
|
-
interface TEnsureTableOptions {
|
|
471
|
+
aggregate(query: AggregateQuery): Promise<Array<Record<string, unknown>>>;
|
|
472
|
+
/** Whether the underlying adapter supports text search. */
|
|
473
|
+
isSearchable(): boolean;
|
|
474
|
+
/** Whether the adapter can filter on a given field (proxies adapter capability). */
|
|
475
|
+
canFilterField(fd: TDbFieldMeta): boolean;
|
|
476
|
+
/** Calendar-bucket units the adapter can group by (proxies adapter capability; empty = none). */
|
|
477
|
+
calendarBucketUnits(): ReadonlySet<BucketUnit>;
|
|
478
|
+
/** Whether the adapter can sort by a given field (proxies adapter capability). */
|
|
479
|
+
canSortField(fd: TDbFieldMeta): boolean;
|
|
480
|
+
/** Returns available search indexes from the adapter. */
|
|
481
|
+
getSearchIndexes(): TSearchIndexInfo[];
|
|
627
482
|
/**
|
|
628
|
-
*
|
|
629
|
-
* CREATE TABLE — the constraints are added afterwards by `syncForeignKeys()`.
|
|
630
|
-
* Schema sync passes the members of a foreign-key cycle so they can be
|
|
631
|
-
* created in any order.
|
|
483
|
+
* Full-text search with query translation and result reconstruction.
|
|
632
484
|
*/
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
/** A single table-level option in unified key-value format. */
|
|
641
|
-
interface TExistingTableOption {
|
|
642
|
-
key: string;
|
|
643
|
-
value: string;
|
|
644
|
-
}
|
|
645
|
-
/** Result of comparing desired table options against existing ones. */
|
|
646
|
-
interface TTableOptionDiff {
|
|
647
|
-
changed: Array<{
|
|
648
|
-
key: string;
|
|
649
|
-
oldValue: string;
|
|
650
|
-
newValue: string; /** Whether applying this change requires dropping and recreating the table. */
|
|
651
|
-
destructive: boolean;
|
|
485
|
+
search<Q extends Uniquery<OwnProps, NavType>>(text: string, query: Q, indexName?: string): Promise<Array<DbResponse<DataType, NavType, Q>>>;
|
|
486
|
+
/**
|
|
487
|
+
* Full-text search with count for paginated search results.
|
|
488
|
+
*/
|
|
489
|
+
searchWithCount<Q extends Uniquery<OwnProps, NavType>>(text: string, query: Q, indexName?: string): Promise<{
|
|
490
|
+
data: Array<DbResponse<DataType, NavType, Q>>;
|
|
491
|
+
count: number;
|
|
652
492
|
}>;
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
493
|
+
/** Whether the underlying adapter supports vector similarity search. */
|
|
494
|
+
isVectorSearchable(): boolean;
|
|
495
|
+
/**
|
|
496
|
+
* Vector similarity search with query translation and result reconstruction.
|
|
497
|
+
*
|
|
498
|
+
* Overloads:
|
|
499
|
+
* - `vectorSearch(vector, query?)` — uses default vector index
|
|
500
|
+
* - `vectorSearch(indexName, vector, query?)` — targets a specific vector index
|
|
501
|
+
*/
|
|
502
|
+
vectorSearch<Q extends Uniquery<OwnProps, NavType>>(vectorOrIndex: number[] | string, maybeVectorOrQuery?: number[] | Q, maybeQuery?: Q): Promise<Array<DbResponse<DataType, NavType, Q>>>;
|
|
503
|
+
/**
|
|
504
|
+
* Vector similarity search with count for paginated results.
|
|
505
|
+
*
|
|
506
|
+
* Overloads:
|
|
507
|
+
* - `vectorSearchWithCount(vector, query?)` — uses default vector index
|
|
508
|
+
* - `vectorSearchWithCount(indexName, vector, query?)` — targets a specific vector index
|
|
509
|
+
*/
|
|
510
|
+
vectorSearchWithCount<Q extends Uniquery<OwnProps, NavType>>(vectorOrIndex: number[] | string, maybeVectorOrQuery?: number[] | Q, maybeQuery?: Q): Promise<{
|
|
511
|
+
data: Array<DbResponse<DataType, NavType, Q>>;
|
|
512
|
+
count: number;
|
|
672
513
|
}>;
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}): Promise<TDbUpdateResult>;
|
|
708
|
-
updateOne(payload: Record<string, unknown>, opts?: {
|
|
709
|
-
maxDepth?: number;
|
|
710
|
-
}): Promise<TDbUpdateResult>;
|
|
711
|
-
bulkUpdate(payloads: Array<Record<string, unknown>>, opts?: {
|
|
712
|
-
maxDepth?: number;
|
|
713
|
-
_depth?: number;
|
|
714
|
-
}): Promise<TDbUpdateResult>;
|
|
715
|
-
findOne(query: unknown): Promise<Record<string, unknown> | null>;
|
|
716
|
-
count(query: {
|
|
717
|
-
filter: Record<string, unknown>;
|
|
718
|
-
}): Promise<number>;
|
|
719
|
-
deleteMany(filter: unknown): Promise<TDbDeleteResult>;
|
|
720
|
-
/** Pre-validate items (type + FK constraints) without inserting them. */
|
|
721
|
-
preValidateItems(items: Array<Record<string, unknown>>, opts?: {
|
|
722
|
-
excludeFkTargetTable?: string;
|
|
723
|
-
}): Promise<void>;
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Callback that resolves an annotated type to a writable table instance.
|
|
727
|
-
* Used for nested creation — inserting related records inline.
|
|
728
|
-
*/
|
|
729
|
-
type TWriteTableResolver = (type: TAtscriptAnnotatedType) => (AtscriptDbTableLike & AtscriptDbWritable) | undefined;
|
|
730
|
-
/**
|
|
731
|
-
* A child table that may need cascade/setNull processing when a parent is deleted.
|
|
732
|
-
* Returned by the cascade resolver.
|
|
733
|
-
*/
|
|
734
|
-
interface TCascadeTarget {
|
|
735
|
-
/** FK on the child table that references the parent being deleted. */
|
|
736
|
-
fk: TDbForeignKey;
|
|
737
|
-
/** Name of the child table that holds this FK. */
|
|
738
|
-
childTable: string;
|
|
739
|
-
/** Delete matching child records (goes through AtscriptDbTable for recursive cascade). */
|
|
740
|
-
deleteMany(filter: Record<string, unknown>): Promise<TDbDeleteResult>;
|
|
741
|
-
/** Update matching child records (for setNull — sets FK fields to null). */
|
|
742
|
-
updateMany(filter: Record<string, unknown>, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
743
|
-
/** Count matching child records (for restrict — check existence before delete). */
|
|
744
|
-
count(filter: Record<string, unknown>): Promise<number>;
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Callback that finds all child tables with FKs pointing to a given parent table.
|
|
748
|
-
* Used by AtscriptDbTable to implement application-level cascade deletes.
|
|
749
|
-
*/
|
|
750
|
-
type TCascadeResolver = (tableName: string) => TCascadeTarget[];
|
|
751
|
-
/**
|
|
752
|
-
* Minimal interface for querying a target table during FK validation.
|
|
753
|
-
* Only `count` is needed — we check if the referenced record exists.
|
|
754
|
-
*/
|
|
755
|
-
interface TFkLookupTarget {
|
|
756
|
-
count(filter: Record<string, unknown>): Promise<number>;
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Callback that resolves a table name to a queryable target for FK validation.
|
|
760
|
-
* Returns undefined if the target table is not registered in the space.
|
|
761
|
-
*/
|
|
762
|
-
type TFkLookupResolver = (tableName: string) => TFkLookupTarget | undefined;
|
|
763
|
-
interface TDbRelation {
|
|
764
|
-
/** Direction: 'to' (FK is local), 'from' (FK is remote), or 'via' (M:N junction). */
|
|
765
|
-
direction: "to" | "from" | "via";
|
|
766
|
-
/** The alias used for pairing (if any). */
|
|
767
|
-
alias?: string;
|
|
768
|
-
/** Target type's annotated type reference. */
|
|
769
|
-
targetType: () => TAtscriptAnnotatedType;
|
|
770
|
-
/** Whether this is an array relation (one-to-many). */
|
|
771
|
-
isArray: boolean;
|
|
772
|
-
/** Junction type reference for 'via' (M:N) relations. */
|
|
773
|
-
viaType?: () => TAtscriptAnnotatedType;
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* Write payload for insert / patch paths: every key optional, and optional
|
|
777
|
-
* columns additionally accept `null` (an explicit NULL — `undefined` means
|
|
778
|
-
* "absent" and is dropped before the row reaches defaults or validation).
|
|
779
|
-
*/
|
|
780
|
-
type DbPatch<D> = { [K in keyof D]?: undefined extends D[K] ? D[K] | null : D[K] } & Record<string, unknown>;
|
|
781
|
-
/**
|
|
782
|
-
* Write payload for full-row replace paths: required keys stay required,
|
|
783
|
-
* optional columns additionally accept `null` (explicit NULL).
|
|
784
|
-
*/
|
|
785
|
-
type DbRow<D> = { [K in keyof D]: undefined extends D[K] ? D[K] | null : D[K] } & Record<string, unknown>;
|
|
786
|
-
/** Built-in write actions a moost-db `AsDbController` endpoint performs. */
|
|
787
|
-
type TDbWriteAction = "insert" | "insertMany" | "replace" | "replaceMany" | "update" | "updateMany";
|
|
788
|
-
/**
|
|
789
|
-
* Context handed to a write {@link TWriteOptions.guard} (since 0.1.128) — and
|
|
790
|
-
* through it to `AsDbController.guardWrite()`. The table invokes the guard
|
|
791
|
-
* exactly once, inside its own transaction, after `undefined`-pruning,
|
|
792
|
-
* defaults and validation and before encryption / nested-relation phases.
|
|
793
|
-
*/
|
|
794
|
-
interface TDbWriteGuardContext<Row = Record<string, unknown>> {
|
|
795
|
-
/** The table method the guard runs for (`insertOne` → `insert`, `insertMany` → `insertMany`, …). */
|
|
796
|
-
readonly action: TDbWriteAction;
|
|
514
|
+
/** Resolves overloaded vector search arguments into canonical form. */
|
|
515
|
+
private _resolveVectorSearchArgs;
|
|
516
|
+
/** Whether the underlying adapter supports geospatial search. */
|
|
517
|
+
isGeoSearchable(): boolean;
|
|
518
|
+
/**
|
|
519
|
+
* Distance-ranked geospatial search (mirrors {@link vectorSearch}).
|
|
520
|
+
* Results are sorted by distance ascending; each row carries a computed
|
|
521
|
+
* `$distance` field (meters from the query point). `$maxDistance` /
|
|
522
|
+
* `$minDistance` (meters) ride in `query.controls`; user `$sort` is rejected.
|
|
523
|
+
*
|
|
524
|
+
* Overloads:
|
|
525
|
+
* - `geoSearch(point, query?)` — uses the table's only geo index
|
|
526
|
+
* - `geoSearch(indexName, point, query?)` — targets a specific geo index
|
|
527
|
+
*/
|
|
528
|
+
geoSearch<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<Array<DbResponse<DataType, NavType, Q> & {
|
|
529
|
+
$distance: number;
|
|
530
|
+
}>>;
|
|
531
|
+
/**
|
|
532
|
+
* Distance-ranked geospatial search with count for paginated results.
|
|
533
|
+
*
|
|
534
|
+
* Overloads:
|
|
535
|
+
* - `geoSearchWithCount(point, query?)` — uses the table's only geo index
|
|
536
|
+
* - `geoSearchWithCount(indexName, point, query?)` — targets a specific geo index
|
|
537
|
+
*/
|
|
538
|
+
geoSearchWithCount<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<{
|
|
539
|
+
data: Array<DbResponse<DataType, NavType, Q> & {
|
|
540
|
+
$distance: number;
|
|
541
|
+
}>;
|
|
542
|
+
count: number;
|
|
543
|
+
}>;
|
|
544
|
+
/** Resolves overloaded geo search arguments into canonical form. */
|
|
545
|
+
private _resolveGeoSearchArgs;
|
|
546
|
+
/** Shared geoSearch validation + query translation. */
|
|
547
|
+
private _prepareGeoSearch;
|
|
797
548
|
/**
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
549
|
+
* Finds a single record by any type-compatible identifier — primary key
|
|
550
|
+
* or single-field unique index.
|
|
551
|
+
* The return type excludes nav props unless `$with` is provided in controls.
|
|
552
|
+
*
|
|
553
|
+
* ```typescript
|
|
554
|
+
* // Without relations — nav props stripped from result
|
|
555
|
+
* const user = await table.findById('123')
|
|
556
|
+
*
|
|
557
|
+
* // With relations — only requested nav props appear
|
|
558
|
+
* const user = await table.findById('123', { controls: { $with: [{ name: 'posts' }] } })
|
|
559
|
+
* ```
|
|
802
560
|
*/
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
561
|
+
findById<Q extends {
|
|
562
|
+
controls?: UniqueryControls<OwnProps, NavType>;
|
|
563
|
+
} = Record<string, never>>(id: IdType, query?: Q): Promise<DbResponse<DataType, NavType, Q> | null>;
|
|
806
564
|
/**
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
565
|
+
* Resolve an id value (scalar or object) into a {@link FilterExpr} using the
|
|
566
|
+
* same identification resolution as {@link findById}. Public so callers can
|
|
567
|
+
* AND-combine the id-filter with a row-level read overlay before issuing
|
|
568
|
+
* `findOne` (avoiding the existence leak that `findById` would cause).
|
|
810
569
|
*/
|
|
811
|
-
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Context handed to a delete {@link TDeleteOptions.guard} (since 0.1.128) —
|
|
815
|
-
* and through it to `AsDbController.guardRemove()`. Runs inside the table's
|
|
816
|
-
* transaction; an id that resolves to no filter never reaches the guard
|
|
817
|
-
* (`deleteOne` answers `{ deletedCount: 0 }`).
|
|
818
|
-
*/
|
|
819
|
-
interface TDbRemoveGuardContext<Row = Record<string, unknown>> {
|
|
820
|
-
/** The id `deleteOne` was called with. */
|
|
821
|
-
readonly id: unknown;
|
|
822
|
-
/** `table.resolveIdFilter(id)` — never null here. */
|
|
823
|
-
readonly filter: FilterExpr;
|
|
824
|
-
/** Lazy, memoised pre-image of the row about to be deleted (`null` when missing). */
|
|
825
|
-
current(): Promise<Row | null>;
|
|
826
|
-
}
|
|
827
|
-
/** A validated-stage write guard — see {@link TWriteOptions.guard}. */
|
|
828
|
-
type TDbWriteGuard<Row = Record<string, unknown>> = (ctx: TDbWriteGuardContext<Row>) => void | Promise<void>;
|
|
829
|
-
/** A validated-stage delete guard — see {@link TDeleteOptions.guard}. */
|
|
830
|
-
type TDbRemoveGuard<Row = Record<string, unknown>> = (ctx: TDbRemoveGuardContext<Row>) => void | Promise<void>;
|
|
831
|
-
/** Options of `AtscriptDbTable.touchMany` (since 0.1.129). */
|
|
832
|
-
interface TTouchManyOptions {
|
|
570
|
+
resolveIdFilter(id: unknown): FilterExpr | null;
|
|
833
571
|
/**
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
* `
|
|
572
|
+
* Resolve an id value into a filter expression.
|
|
573
|
+
*
|
|
574
|
+
* When `preferredId` differs from the PK, scalar ids resolve only against
|
|
575
|
+
* the preferred field (deterministic addressing). Otherwise scalars try PK
|
|
576
|
+
* + every single-field unique index; objects try PK + compound unique
|
|
577
|
+
* indexes.
|
|
837
578
|
*/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
interface TWriteOptions<Row = Record<string, unknown>> {
|
|
842
|
-
/** Nested-relation write recursion limit (default 3). */
|
|
843
|
-
maxDepth?: number;
|
|
579
|
+
protected _resolveIdFilter(id: unknown): FilterExpr | null;
|
|
580
|
+
/** Build a single-key filter from `idObj` over `fields`, or null if any field is missing/incompatible. */
|
|
581
|
+
private _tryCompoundFilter;
|
|
844
582
|
/**
|
|
845
|
-
*
|
|
846
|
-
* table's transaction, after defaults + validation and before encryption
|
|
847
|
-
* and nested-relation phases, with the rows the table is about to write.
|
|
848
|
-
* Rows may be enriched in place — they are validated again afterwards. A
|
|
849
|
-
* throw rolls the transaction back and propagates unchanged. Never runs
|
|
850
|
-
* for the nested re-entries a deep write performs on related tables.
|
|
583
|
+
* Attempts to build a single-field filter `{ field: preparedId }`.
|
|
851
584
|
*/
|
|
852
|
-
|
|
853
|
-
}
|
|
854
|
-
/** Options of `deleteOne`. */
|
|
855
|
-
interface TDeleteOptions<Row = Record<string, unknown>> {
|
|
585
|
+
private _tryFieldFilter;
|
|
856
586
|
/**
|
|
857
|
-
*
|
|
858
|
-
* transaction after the id resolved to a filter and before cascade /
|
|
859
|
-
* delete. A throw rolls the transaction back and propagates unchanged.
|
|
587
|
+
* Public entry point for relation loading. Used by adapters for nested $with delegation.
|
|
860
588
|
*/
|
|
861
|
-
|
|
589
|
+
loadRelations(rows: Array<Record<string, unknown>>, withRelations: WithRelation[]): Promise<void>;
|
|
590
|
+
/**
|
|
591
|
+
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
592
|
+
* Thin wrapper — delegates to relation-loader for shared use with db-table.ts write path.
|
|
593
|
+
*/
|
|
594
|
+
protected _findFKForRelation(relation: TDbRelation): {
|
|
595
|
+
localFields: string[];
|
|
596
|
+
targetFields: string[];
|
|
597
|
+
} | undefined;
|
|
598
|
+
/**
|
|
599
|
+
* Finds a FK on a remote table that points back to this table.
|
|
600
|
+
* Thin wrapper — delegates to relation-loader for shared use with db-table.ts write path.
|
|
601
|
+
*/
|
|
602
|
+
protected _findRemoteFK(targetTable: {
|
|
603
|
+
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
604
|
+
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
862
605
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Adds `null` to every optional property of `O`. Optional columns store SQL
|
|
865
|
-
* NULL / Mongo null, and the runtime validator accepts `null` for optional
|
|
866
|
-
* props — so filter shapes (`{ note: null }`, `{ note: { $ne: null } }`) and
|
|
867
|
-
* row shapes must admit it at the type level too. Homomorphic: keys and
|
|
868
|
-
* required properties are unchanged; applying it twice is a no-op.
|
|
869
|
-
*/
|
|
870
|
-
type NullableOptional<O> = { [K in keyof O]: undefined extends O[K] ? O[K] | null : O[K] };
|
|
871
606
|
//#endregion
|
|
872
607
|
//#region src/base-adapter.d.ts
|
|
608
|
+
/** Every calendar-bucket unit — what an adapter that renders them all returns from `calendarBucketUnits()`. */
|
|
609
|
+
declare const ALL_BUCKET_UNITS: ReadonlySet<BucketUnit>;
|
|
873
610
|
/**
|
|
874
611
|
* Abstract base class for database adapters.
|
|
875
612
|
*
|
|
@@ -1022,6 +759,21 @@ declare abstract class BaseDbAdapter {
|
|
|
1022
759
|
* to generate the value client-side or leave it for the DB.
|
|
1023
760
|
*/
|
|
1024
761
|
nativeDefaultFns(): ReadonlySet<TDbDefaultFn>;
|
|
762
|
+
/**
|
|
763
|
+
* Calendar-bucket units (`{ $bucket, $field }` in an aggregate `$select`)
|
|
764
|
+
* this adapter can group by, over IANA time zones. Empty (the default) =
|
|
765
|
+
* calendar buckets unsupported: the core rejects them with
|
|
766
|
+
* `BUCKET_NOT_SUPPORTED` before dispatch, and moost-db's `/meta` advertises
|
|
767
|
+
* no bucketable field.
|
|
768
|
+
*
|
|
769
|
+
* A set rather than a boolean (like {@link nativeDefaultFns}) so a unit can
|
|
770
|
+
* be adopted adapter by adapter. An adapter that returns a unit must group
|
|
771
|
+
* by the bucket alias in `$groupBy` — see `controls.$select.buckets`
|
|
772
|
+
* (`TResolvedBucket`: physical `field`, source `fd`) — and return the
|
|
773
|
+
* `YYYY-MM-DD` label of the bucket's first local day (null for a null or
|
|
774
|
+
* out-of-range source, uniqu's `bucketLabel` semantics). Since 0.1.132.
|
|
775
|
+
*/
|
|
776
|
+
calendarBucketUnits(): ReadonlySet<BucketUnit>;
|
|
1025
777
|
/**
|
|
1026
778
|
* Whether this adapter enforces foreign key constraints natively.
|
|
1027
779
|
* When `true`, the generic layer skips application-level cascade/setNull
|
|
@@ -1042,6 +794,9 @@ declare abstract class BaseDbAdapter {
|
|
|
1042
794
|
* Used by `AsDbReadableController.buildMetaResponse()` to gate the
|
|
1043
795
|
* `filterable` flag exposed to UIs — the adapter's answer is a hard gate
|
|
1044
796
|
* even when the field carries `@db.column.filterable`.
|
|
797
|
+
*
|
|
798
|
+
* Vetoes value comparison only — a sole-`$exists` entry needs just a stored
|
|
799
|
+
* column (`canFilterLeaf`).
|
|
1045
800
|
*/
|
|
1046
801
|
canFilterField(fd: TDbFieldMeta): boolean;
|
|
1047
802
|
/**
|
|
@@ -1195,8 +950,15 @@ declare abstract class BaseDbAdapter {
|
|
|
1195
950
|
*/
|
|
1196
951
|
getSearchIndexes(): TSearchIndexInfo[];
|
|
1197
952
|
/**
|
|
1198
|
-
* Whether this adapter
|
|
1199
|
-
*
|
|
953
|
+
* Whether this adapter can run TEXT search — `search()`, `searchWithCount()`
|
|
954
|
+
* and the grouped `$search` path all gate on it. Vector capability is a
|
|
955
|
+
* separate predicate, {@link isVectorSearchable}.
|
|
956
|
+
*
|
|
957
|
+
* Default: `true` when {@link getSearchIndexes} lists at least one non-vector
|
|
958
|
+
* index. Vector entries are published there for the index picker, but a
|
|
959
|
+
* vector index answers {@link vectorSearch} and nothing else, so counting one
|
|
960
|
+
* here would claim a capability the adapter does not have. An entry without
|
|
961
|
+
* `type` counts as text — adapters predating the field only listed text.
|
|
1200
962
|
*/
|
|
1201
963
|
isSearchable(): boolean;
|
|
1202
964
|
/**
|
|
@@ -1532,487 +1294,931 @@ declare abstract class BaseDbAdapter {
|
|
|
1532
1294
|
formatValue?(field: TDbFieldMeta): TValueFormatterPair | ((value: unknown) => unknown) | undefined;
|
|
1533
1295
|
}
|
|
1534
1296
|
//#endregion
|
|
1535
|
-
//#region src/
|
|
1297
|
+
//#region src/table/table-metadata.d.ts
|
|
1536
1298
|
/**
|
|
1537
|
-
*
|
|
1538
|
-
*
|
|
1539
|
-
*
|
|
1299
|
+
* Finds the nearest ancestor of `path` that belongs to `set`.
|
|
1300
|
+
* Used by both the build pipeline (in `_classifyFields`) and
|
|
1301
|
+
* runtime reconstruction on the Readable.
|
|
1540
1302
|
*/
|
|
1541
|
-
declare
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1303
|
+
declare function findAncestorInSet(path: string, set: ReadonlySet<string>): string | undefined;
|
|
1304
|
+
/** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
|
|
1305
|
+
declare function isGeoPointType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
1306
|
+
/**
|
|
1307
|
+
* Returns true if the annotated type is acceptable for `@db.index.geo`:
|
|
1308
|
+
* the `db.geoPoint` primitive or a structurally identical `number[]`
|
|
1309
|
+
* (excluding `db.vector`, which is semantically an embedding).
|
|
1310
|
+
*/
|
|
1311
|
+
declare function isGeoIndexableType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
1312
|
+
/**
|
|
1313
|
+
* Computed metadata for a database table or view.
|
|
1314
|
+
*
|
|
1315
|
+
* Contains all field metadata, physical mapping indexes, relation definitions,
|
|
1316
|
+
* and constraint information derived from Atscript annotations. Built lazily
|
|
1317
|
+
* on first access via {@link build}, then immutable.
|
|
1318
|
+
*
|
|
1319
|
+
* This class owns the build pipeline that was previously part of
|
|
1320
|
+
* `AtscriptDbReadable._flatten()`. The Readable delegates all metadata
|
|
1321
|
+
* access to this class.
|
|
1322
|
+
*/
|
|
1323
|
+
declare class TableMetadata {
|
|
1324
|
+
readonly nestedObjects: boolean;
|
|
1325
|
+
flatMap: Map<string, TAtscriptAnnotatedType>;
|
|
1326
|
+
fieldDescriptors: readonly TDbFieldMeta[];
|
|
1327
|
+
primaryKeys: string[];
|
|
1328
|
+
preferredId: string[];
|
|
1329
|
+
originalMetaIdFields: string[];
|
|
1330
|
+
indexes: Map<string, TDbIndex>;
|
|
1331
|
+
foreignKeys: Map<string, TDbForeignKey>;
|
|
1332
|
+
relations: Map<string, TDbRelation>;
|
|
1333
|
+
navFields: Set<string>;
|
|
1334
|
+
ignoredFields: Set<string>;
|
|
1335
|
+
uniqueProps: Set<string>;
|
|
1336
|
+
defaults: Map<string, TDbDefaultValue>;
|
|
1337
|
+
columnMap: Map<string, string>;
|
|
1338
|
+
dimensions: string[];
|
|
1339
|
+
measures: string[];
|
|
1340
|
+
/** Logical field name annotated with `@db.column.version`, if any. */
|
|
1341
|
+
versionField?: string;
|
|
1342
|
+
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
1343
|
+
quantityRefByField: Map<string, string>;
|
|
1344
|
+
/** Logical paths annotated with `@db.encrypted` — stored as one opaque ciphertext column. */
|
|
1345
|
+
encryptedFields: Set<string>;
|
|
1346
|
+
pathToPhysical: Map<string, string>;
|
|
1347
|
+
physicalToPath: Map<string, string>;
|
|
1348
|
+
flattenedParents: Set<string>;
|
|
1349
|
+
jsonFields: Set<string>;
|
|
1350
|
+
selectExpansion: Map<string, string[]>;
|
|
1351
|
+
booleanFields: Set<string>;
|
|
1352
|
+
decimalFields: Set<string>;
|
|
1353
|
+
allPhysicalFields: string[];
|
|
1354
|
+
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
1355
|
+
childrenByParent: Map<string, string[]>;
|
|
1356
|
+
/** Precomputed parent path → optional child logical paths (replace-strategy null-fill in the patch decomposer). */
|
|
1357
|
+
optionalLeavesByLogicalParent: Map<string, string[]>;
|
|
1358
|
+
requiresMappings: boolean;
|
|
1359
|
+
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
1360
|
+
onlyColumnRenames: boolean;
|
|
1361
|
+
toStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
1362
|
+
fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
1363
|
+
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
1364
|
+
leafByPhysical: Map<string, TDbFieldMeta>;
|
|
1365
|
+
/** Leaf field descriptors indexed by logical path (write/patch/filter paths). */
|
|
1366
|
+
leafByLogical: Map<string, TDbFieldMeta>;
|
|
1560
1367
|
/**
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1368
|
+
* Non-ignored field descriptors keyed by logical path, excluding navigation
|
|
1369
|
+
* relations and their descendants. Unlike `leafByLogical` (relational
|
|
1370
|
+
* adapters only) this is built for every adapter, so the core path guard
|
|
1371
|
+
* (`guardPaths`) can answer "does this path have physical storage here?"
|
|
1372
|
+
* on nested-object adapters too.
|
|
1563
1373
|
*/
|
|
1564
|
-
|
|
1374
|
+
descriptorByPath: Map<string, TDbFieldMeta>;
|
|
1565
1375
|
/**
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1376
|
+
* Logical paths stored as a single JSON column (`storage === 'json'`,
|
|
1377
|
+
* non-ignored descriptors). Retained after build — unlike the build-time
|
|
1378
|
+
* `jsonFields` set — so the path guard can classify JSON descendants on
|
|
1379
|
+
* relational adapters. Empty on nested-object adapters (they keep native
|
|
1380
|
+
* dotted paths as descriptors).
|
|
1568
1381
|
*/
|
|
1569
|
-
|
|
1382
|
+
jsonParents: ReadonlySet<string>;
|
|
1570
1383
|
/**
|
|
1571
|
-
*
|
|
1384
|
+
* Logical paths holding a JSON value (`isJsonValueField`: JSON-stored, `json`
|
|
1385
|
+
* or `array` design type; non-ignored, nav-free descriptors) — a timestamp
|
|
1386
|
+
* beneath one is never a calendar-bucket source (`jsonValueAncestor`).
|
|
1572
1387
|
*/
|
|
1573
|
-
|
|
1388
|
+
jsonValueParents: ReadonlySet<string>;
|
|
1389
|
+
/** Every field descriptor's `physicalName` — reserved names a bucket alias may not take. */
|
|
1390
|
+
physicalNames: ReadonlySet<string>;
|
|
1391
|
+
private _built;
|
|
1392
|
+
private _identifications?;
|
|
1393
|
+
private _collateMap;
|
|
1394
|
+
private _columnFromMap;
|
|
1395
|
+
constructor(nestedObjects: boolean);
|
|
1396
|
+
get isBuilt(): boolean;
|
|
1574
1397
|
/**
|
|
1575
|
-
*
|
|
1398
|
+
* Logical field path → its physical path in document storage (nested
|
|
1399
|
+
* objects kept inline). `@db.column` renames apply to the annotated key,
|
|
1400
|
+
* and a document renames the TOP-LEVEL key only — nested keys are stored
|
|
1401
|
+
* as-is — so a dotted path under a renamed top-level object renames its
|
|
1402
|
+
* first segment: `profile.bio` under `@db.column 'prof'` → `prof.bio`.
|
|
1576
1403
|
*/
|
|
1577
|
-
|
|
1404
|
+
documentPath(path: string): string;
|
|
1578
1405
|
/**
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1406
|
+
* Runs the full metadata compilation pipeline. Called once by
|
|
1407
|
+
* `AtscriptDbReadable._ensureBuilt()` on first metadata access.
|
|
1408
|
+
*
|
|
1409
|
+
* Pipeline steps:
|
|
1410
|
+
* 1. `adapter.onBeforeFlatten(type)` — adapter hook
|
|
1411
|
+
* 2. `flattenAnnotatedType()` — collect field tuples, detect nav fields eagerly
|
|
1412
|
+
* 3. Replay non-nav-descendant tuples through annotation scanning + adapter.onFieldScanned
|
|
1413
|
+
* 4. Classify fields and build path maps (skipped for nested-objects adapters)
|
|
1414
|
+
* 5. `adapter.getMetadataOverrides()` → `_applyOverrides()` (PK/unique/inject adjustments)
|
|
1415
|
+
* 6. Build field descriptors (TDbFieldMeta[])
|
|
1416
|
+
* 7. Build leaf field indexes (skipped for nested-objects adapters)
|
|
1417
|
+
* 8. Finalize indexes (resolve field names to physical)
|
|
1418
|
+
* 9. `adapter.onAfterFlatten()` — adapter hook (read-only bookkeeping)
|
|
1419
|
+
* 10. Build allPhysicalFields list
|
|
1581
1420
|
*/
|
|
1582
|
-
|
|
1421
|
+
build(type: TAtscriptAnnotatedType<TAtscriptTypeObject>, adapter: BaseDbAdapter, logger: TGenericLogger): void;
|
|
1583
1422
|
/**
|
|
1584
|
-
* Applies adapter-
|
|
1423
|
+
* Applies adapter-provided metadata overrides atomically.
|
|
1424
|
+
* Processing order: injectFields → removePrimaryKeys → addPrimaryKeys → addUniqueFields.
|
|
1585
1425
|
*/
|
|
1586
|
-
|
|
1426
|
+
private _applyOverrides;
|
|
1587
1427
|
/**
|
|
1588
|
-
*
|
|
1589
|
-
* Shared pre-processing for both document and relational write paths.
|
|
1428
|
+
* Scans `@db.*` and `@meta.id` annotations on a field during flattening.
|
|
1590
1429
|
*/
|
|
1591
|
-
|
|
1592
|
-
}
|
|
1593
|
-
/**
|
|
1594
|
-
* Field mapper for document-oriented adapters (e.g. MongoDB).
|
|
1595
|
-
* Nested objects are preserved as-is. Only applies column renames and
|
|
1596
|
-
* value coercion.
|
|
1597
|
-
*/
|
|
1598
|
-
declare class DocumentFieldMapper extends FieldMappingStrategy {
|
|
1599
|
-
reconstructFromRead(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
1600
|
-
translateQuery(query: Uniquery, meta: TableMetadata): DbQuery;
|
|
1601
|
-
translateAggregateQuery(query: AggregateQuery, meta: TableMetadata): DbQuery;
|
|
1602
|
-
prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
|
|
1603
|
-
translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
1604
|
-
}
|
|
1605
|
-
//#endregion
|
|
1606
|
-
//#region src/encryption.d.ts
|
|
1607
|
-
/**
|
|
1608
|
-
* Configuration for field-level encryption at rest (`@db.encrypted`).
|
|
1609
|
-
* Passed to `DbSpace` via the options bag.
|
|
1610
|
-
*/
|
|
1611
|
-
interface TDbEncryptionOptions {
|
|
1612
|
-
/** Key used for all new writes. */
|
|
1613
|
-
defaultKeyId: string;
|
|
1430
|
+
private _scanGenericAnnotations;
|
|
1614
1431
|
/**
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1432
|
+
* Build-time diagnostics for `@db.encrypted` (§6 of the field-encryption
|
|
1433
|
+
* spec). Mirrors the compile-time AnnotationSpec validation so models built
|
|
1434
|
+
* from pre-compiled types still fail fast.
|
|
1618
1435
|
*/
|
|
1619
|
-
|
|
1436
|
+
private _validateEncryptedField;
|
|
1437
|
+
/** Build-time diagnostics for `@db.index.geo` (§3 of the geo-index spec). */
|
|
1438
|
+
private _validateGeoIndexField;
|
|
1439
|
+
private _addIndexField;
|
|
1620
1440
|
/**
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
1441
|
+
* Classifies each field as column, flattened, json, or parent-object.
|
|
1442
|
+
* Builds the bidirectional pathToPhysical / physicalToPath maps.
|
|
1623
1443
|
*/
|
|
1624
|
-
|
|
1444
|
+
private _classifyFields;
|
|
1445
|
+
/** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
|
|
1446
|
+
private _flattenedPrefix;
|
|
1447
|
+
/** Nearest `@db.encrypted` ancestor of `path` (exclusive), or `undefined`. */
|
|
1625
1448
|
/**
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1630
|
-
* the value gets encrypted on its next write.
|
|
1449
|
+
* Indexes non-ignored descriptors by logical path and retains the JSON-parent
|
|
1450
|
+
* sets (plus every descriptor's physical name). Navigation relations and their descendants are skipped even when the
|
|
1451
|
+
* adapter keeps them as descriptors (nested-object adapters do) — they are
|
|
1452
|
+
* loaded with `$with`, never addressed as columns of this table.
|
|
1631
1453
|
*/
|
|
1632
|
-
|
|
1633
|
-
}
|
|
1634
|
-
/** Decryption context — carried into error messages (never the key itself). */
|
|
1635
|
-
interface TDecryptContext {
|
|
1636
|
-
table?: string;
|
|
1637
|
-
field?: string;
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* AES-256-GCM envelope encryption service for `@db.encrypted` fields.
|
|
1641
|
-
*
|
|
1642
|
-
* Owned by `DbSpace` and shared across all tables in the space. Values are
|
|
1643
|
-
* `JSON.stringify`'d before encryption (type-exact round-trips) and stored as
|
|
1644
|
-
* a single ASCII envelope string: `aes1$<keyId>$<iv>$<tag>$<ciphertext>`.
|
|
1645
|
-
*
|
|
1646
|
-
* Key material is validated eagerly at construction (`ENC_KEY_INVALID`);
|
|
1647
|
-
* `resolveKey` lookups are cached per keyId for the process lifetime.
|
|
1648
|
-
*/
|
|
1649
|
-
declare class DbEncryption {
|
|
1650
|
-
readonly defaultKeyId: string;
|
|
1651
|
-
readonly onUnencrypted: "error" | "passthrough";
|
|
1652
|
-
private readonly _keys;
|
|
1653
|
-
private readonly _resolveKey?;
|
|
1654
|
-
private readonly _resolved;
|
|
1655
|
-
constructor(options: TDbEncryptionOptions);
|
|
1656
|
-
/** True when `value` looks like an encryption envelope produced by this service. */
|
|
1657
|
-
isEnvelope(value: unknown): value is string;
|
|
1658
|
-
/** Encrypts a JSON-serializable value into an envelope string using the default key. */
|
|
1659
|
-
encrypt(value: unknown): Promise<string>;
|
|
1660
|
-
/** Decrypts an envelope string back into its plaintext value. */
|
|
1661
|
-
decrypt(envelope: string, ctx?: TDecryptContext): Promise<unknown>;
|
|
1662
|
-
private _decryptFailed;
|
|
1663
|
-
private _getKey;
|
|
1664
|
-
}
|
|
1665
|
-
//#endregion
|
|
1666
|
-
//#region src/table/db-readable.d.ts
|
|
1667
|
-
/**
|
|
1668
|
-
* Extracts nav prop names from a query's `$with` array.
|
|
1669
|
-
* Returns `never` when `$with` is absent → all nav props stripped from response.
|
|
1670
|
-
*/
|
|
1671
|
-
type ExtractWith<Q> = Q extends {
|
|
1672
|
-
controls: {
|
|
1673
|
-
$with: Array<{
|
|
1674
|
-
name: infer N extends string;
|
|
1675
|
-
}>;
|
|
1676
|
-
};
|
|
1677
|
-
} ? N : never;
|
|
1678
|
-
/**
|
|
1679
|
-
* Computes the response type for a query:
|
|
1680
|
-
* - Strips all nav props from the base DataType
|
|
1681
|
-
* - Adds back only the nav props requested via `$with`
|
|
1682
|
-
*
|
|
1683
|
-
* When no `$with` is provided, result is `Omit<DataType, keyof NavType>`.
|
|
1684
|
-
* When `$with: [{ name: 'author' }]`, result includes `author` from DataType.
|
|
1685
|
-
* When the query type is not a literal (e.g. a variable typed as `Uniquery`),
|
|
1686
|
-
* falls back to `DataType` (all nav props optional, as declared).
|
|
1687
|
-
*/
|
|
1688
|
-
type DbResponse<Data, Nav, Q> = [keyof Nav] extends [never] ? Data : string extends keyof Nav ? Data : Omit<Data, keyof Nav & string> & Pick<Data, ExtractWith<Q> & keyof Data & string>;
|
|
1689
|
-
/**
|
|
1690
|
-
* Resolves the design type from an annotated type.
|
|
1691
|
-
* Encapsulates the `kind === ''` check and fallback logic that
|
|
1692
|
-
* otherwise trips up every adapter author.
|
|
1693
|
-
*
|
|
1694
|
-
* For union types (e.g., from flattened `{...} | {...}` objects):
|
|
1695
|
-
* - If all members resolve to the same type → returns that type (strong type)
|
|
1696
|
-
* - If members disagree → returns `'union'` (out of scope for type management)
|
|
1697
|
-
*/
|
|
1698
|
-
declare function resolveDesignType(fieldType: TAtscriptAnnotatedType): string;
|
|
1699
|
-
/**
|
|
1700
|
-
* Shared read-only database abstraction driven by Atscript annotations.
|
|
1701
|
-
*
|
|
1702
|
-
* Contains all field metadata computation, read operations, query translation,
|
|
1703
|
-
* relation loading, and result reconstruction. Extended by both
|
|
1704
|
-
* {@link AtscriptDbTable} (adds write operations) and {@link AtscriptDbView}
|
|
1705
|
-
* (adds view plan/DDL).
|
|
1706
|
-
*/
|
|
1707
|
-
declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>, _FlatType = NullableOptional<FlatOf<T>>, A extends BaseDbAdapter = BaseDbAdapter, IdType = PrimaryKeyOf<T>, OwnProps = NullableOptional<OwnPropsOf<T>>, NavType extends Record<string, unknown> = NavPropsOf<T>> {
|
|
1708
|
-
protected readonly _type: T;
|
|
1709
|
-
protected readonly adapter: A;
|
|
1710
|
-
protected readonly logger: TGenericLogger;
|
|
1711
|
-
protected readonly _tableResolver?: TTableResolver | undefined;
|
|
1712
|
-
/** Resolved table/collection/view name. */
|
|
1713
|
-
readonly tableName: string;
|
|
1714
|
-
/** Database schema/namespace from `@db.schema` (if set). */
|
|
1715
|
-
readonly schema: string | undefined;
|
|
1716
|
-
/** Sync method from `@db.sync.method` ('drop' | 'recreate' | undefined). */
|
|
1717
|
-
protected readonly _syncMethod: "drop" | "recreate" | undefined;
|
|
1718
|
-
/** Previous table/view name from `@db.table.renamed` or `@db.view.renamed`. */
|
|
1719
|
-
readonly renamedFrom: string | undefined;
|
|
1720
|
-
/** Computed metadata for this table/view. Built lazily on first access. */
|
|
1721
|
-
protected readonly _meta: TableMetadata;
|
|
1722
|
-
/** Strategy for mapping between logical field shapes and physical storage. */
|
|
1723
|
-
protected readonly _fieldMapper: FieldMappingStrategy;
|
|
1724
|
-
protected _writeTableResolver?: TWriteTableResolver;
|
|
1725
|
-
/** Encryption service for `@db.encrypted` fields — set by `DbSpace` from its options. */
|
|
1726
|
-
protected _encryption?: DbEncryption;
|
|
1727
|
-
private _metaIdPhysical;
|
|
1728
|
-
constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver | undefined);
|
|
1454
|
+
private _buildGuardIndexes;
|
|
1729
1455
|
/**
|
|
1730
|
-
*
|
|
1731
|
-
*
|
|
1732
|
-
* configured with an `encryption` options block.
|
|
1456
|
+
* Indexes `fieldDescriptors` into two lookup maps for unified
|
|
1457
|
+
* read/write field classification in the RelationalFieldMapper.
|
|
1733
1458
|
*/
|
|
1734
|
-
|
|
1735
|
-
/** Ensures metadata is built. Called before any metadata access. */
|
|
1736
|
-
protected _ensureBuilt(): void;
|
|
1459
|
+
private _buildLeafIndexes;
|
|
1737
1460
|
/**
|
|
1738
|
-
*
|
|
1739
|
-
*
|
|
1740
|
-
*
|
|
1461
|
+
* Builds field descriptors, physical-name lookup, and value formatters.
|
|
1462
|
+
* Called once during build() — everything it needs
|
|
1463
|
+
* (flatMap, indexes, columnMap, etc.) is already populated.
|
|
1741
1464
|
*/
|
|
1742
|
-
|
|
1743
|
-
protected _ensureSearchable(): void;
|
|
1744
|
-
/** Engine-agnostic query-time guards (encrypted-field refs, $geoWithin shape). */
|
|
1745
|
-
protected _guardQuery(query: Uniquery | undefined): void;
|
|
1746
|
-
private _encryptedPathsCache?;
|
|
1747
|
-
/** Pre-split `encryptedFields` paths — computed once, reused on every read/write. */
|
|
1748
|
-
protected get _encryptedPaths(): Array<{
|
|
1749
|
-
path: string;
|
|
1750
|
-
segments: string[];
|
|
1751
|
-
leaf: string;
|
|
1752
|
-
}>;
|
|
1465
|
+
private _buildFieldDescriptors;
|
|
1753
1466
|
/**
|
|
1754
|
-
*
|
|
1755
|
-
* object holding the leaf — or `undefined` when the path is unreachable
|
|
1756
|
-
* (a missing, non-object, or array step). With `cloneParents`, every
|
|
1757
|
-
* traversed object is shallow-cloned and re-linked so caller-shared
|
|
1758
|
-
* nested objects are never mutated.
|
|
1467
|
+
* Resolves `fkTargetField` for FK fields in field descriptors.
|
|
1759
1468
|
*/
|
|
1760
|
-
|
|
1469
|
+
private _resolveFkTargetFields;
|
|
1470
|
+
private _finalizeIndexes;
|
|
1761
1471
|
/**
|
|
1762
|
-
*
|
|
1763
|
-
*
|
|
1472
|
+
* Captures legitimate row-identifier shapes from the metadata: primary key
|
|
1473
|
+
* (when present) followed by every unique index. Must run BEFORE
|
|
1474
|
+
* `_finalizeIndexes` rewrites `index.fields[i].name` from logical to
|
|
1475
|
+
* physical, so the field lists stay logical.
|
|
1476
|
+
*
|
|
1477
|
+
* Sources:
|
|
1478
|
+
* - `this.primaryKeys` — composite-aware PK (one identification covering
|
|
1479
|
+
* the PK columns).
|
|
1480
|
+
* - `this.indexes` — user-declared `@db.index.unique` (single or compound).
|
|
1481
|
+
* - `this.uniqueProps` — single-field uniques contributed by adapter
|
|
1482
|
+
* overrides (`addUniqueFields`); these aren't reflected in
|
|
1483
|
+
* `this.indexes` but still legitimate addressing identifications.
|
|
1484
|
+
* Deduped against any single-field unique index already captured.
|
|
1764
1485
|
*/
|
|
1765
|
-
|
|
1766
|
-
/**
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
/**
|
|
1781
|
-
|
|
1486
|
+
private _buildIdentifications;
|
|
1487
|
+
/** Legitimate row-identifier shapes — primary key first, then each unique index. */
|
|
1488
|
+
getIdentifications(): readonly TIdentification[];
|
|
1489
|
+
private _resolvePreferredId;
|
|
1490
|
+
}
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/types.d.ts
|
|
1493
|
+
/** Controls with resolved projection. Used in the adapter interface. */
|
|
1494
|
+
interface DbControls extends Omit<UniqueryControls, "$select"> {
|
|
1495
|
+
$select?: UniquSelect;
|
|
1496
|
+
}
|
|
1497
|
+
/** Query object with resolved projection. Passed to adapter methods. */
|
|
1498
|
+
interface DbQuery {
|
|
1499
|
+
filter: FilterExpr;
|
|
1500
|
+
controls: DbControls;
|
|
1501
|
+
/** Pre-computed query insights (field → operators). Adapters may use this to apply query-time behaviour (e.g. collation). */
|
|
1502
|
+
insights?: UniqueryInsights;
|
|
1503
|
+
}
|
|
1504
|
+
/** Describes an available search index exposed by a database adapter. */
|
|
1505
|
+
interface TSearchIndexInfo {
|
|
1506
|
+
/** Index name. Empty string or 'DEFAULT' for the default index. */
|
|
1507
|
+
name: string;
|
|
1508
|
+
/** Human-readable label for UI display. */
|
|
1509
|
+
description?: string;
|
|
1510
|
+
/** Index type: text search or vector similarity search. */
|
|
1511
|
+
type?: "text" | "vector";
|
|
1512
|
+
}
|
|
1513
|
+
/** Relation summary in a meta response. */
|
|
1514
|
+
interface TRelationInfo {
|
|
1515
|
+
name: string;
|
|
1516
|
+
direction: "to" | "from" | "via";
|
|
1517
|
+
isArray: boolean;
|
|
1518
|
+
}
|
|
1519
|
+
/** Per-field capability flags in a meta response. */
|
|
1520
|
+
interface TFieldMeta {
|
|
1521
|
+
sortable: boolean;
|
|
1522
|
+
filterable: boolean;
|
|
1782
1523
|
/**
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
* @internal Adapter-facing surface; not part of the consumer API.
|
|
1524
|
+
* Present only when `filterable` is `false` but narrower predicates still
|
|
1525
|
+
* pass the gate: their operators — `$exists` on a relational adapter's JSON
|
|
1526
|
+
* / array column, `$geoWithin` on a geoPoint of a geo-searchable adapter.
|
|
1527
|
+
* Since 0.1.132.
|
|
1788
1528
|
*/
|
|
1789
|
-
|
|
1529
|
+
filterOps?: string[];
|
|
1530
|
+
/** Present (true) when the field is `@db.encrypted` — stored as ciphertext at rest. */
|
|
1531
|
+
encrypted?: boolean;
|
|
1532
|
+
/** Present (true) when the field carries a `@db.index.geo` geospatial index. */
|
|
1533
|
+
geo?: boolean;
|
|
1790
1534
|
/**
|
|
1791
|
-
*
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1535
|
+
* Present (true) when the field is write-only over HTTP (`@db.writeOnly` or
|
|
1536
|
+
* stamped by a permission overlay): settable in write payloads, never
|
|
1537
|
+
* present in read responses. UIs render it as a set-only input.
|
|
1794
1538
|
*/
|
|
1795
|
-
|
|
1796
|
-
/** Dimension fields from `@db.column.dimension`. */
|
|
1797
|
-
get dimensions(): readonly string[];
|
|
1798
|
-
/** Measure fields from `@db.column.measure`. */
|
|
1799
|
-
get measures(): readonly string[];
|
|
1800
|
-
/** Sync method for structural changes: 'drop' (lossy), 'recreate' (lossless), or undefined (manual). */
|
|
1801
|
-
get syncMethod(): "drop" | "recreate" | undefined;
|
|
1802
|
-
/** Logical → physical column name mapping from `@db.column`. */
|
|
1803
|
-
get columnMap(): ReadonlyMap<string, string>;
|
|
1804
|
-
/** Default values from `@db.default.*`. */
|
|
1805
|
-
get defaults(): ReadonlyMap<string, TDbDefaultValue>;
|
|
1806
|
-
/** Fields excluded from DB via `@db.ignore`. */
|
|
1807
|
-
get ignoredFields(): ReadonlySet<string>;
|
|
1808
|
-
/** Navigational fields (`@db.rel.to` / `@db.rel.from`) — not stored as columns. */
|
|
1809
|
-
get navFields(): ReadonlySet<string>;
|
|
1810
|
-
/** Physical field names used to invert exclude-mode `$select` into a SELECT list. */
|
|
1811
|
-
get allPhysicalFields(): readonly string[];
|
|
1812
|
-
/** Single-field unique index properties. */
|
|
1813
|
-
get uniqueProps(): ReadonlySet<string>;
|
|
1814
|
-
/** Foreign key constraints from `@db.rel.FK` annotations. */
|
|
1815
|
-
get foreignKeys(): ReadonlyMap<string, TDbForeignKey>;
|
|
1816
|
-
/** Navigational relation metadata from `@db.rel.to` / `@db.rel.from`. */
|
|
1817
|
-
get relations(): ReadonlyMap<string, TDbRelation>;
|
|
1818
|
-
/** The underlying database adapter instance. */
|
|
1819
|
-
get dbAdapter(): A;
|
|
1539
|
+
writeOnly?: boolean;
|
|
1820
1540
|
/**
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1541
|
+
* Present (true) when the field is index-backed (explicit `@db.index*`,
|
|
1542
|
+
* primary key or unique field). Advisory only — a hint for UIs that want to
|
|
1543
|
+
* steer users toward cheap sort keys; it never affects whether a `$sort`
|
|
1544
|
+
* is accepted (`sortable` does). Since 0.1.128.
|
|
1823
1545
|
*/
|
|
1824
|
-
|
|
1825
|
-
/** Precomputed logical dot-path → physical column name map. */
|
|
1826
|
-
get pathToPhysical(): ReadonlyMap<string, string>;
|
|
1827
|
-
/** Precomputed physical column name → logical dot-path map (inverse). */
|
|
1828
|
-
get physicalToPath(): ReadonlyMap<string, string>;
|
|
1829
|
-
/** Descriptor for the primary ID field(s). */
|
|
1830
|
-
getIdDescriptor(): TIdDescriptor;
|
|
1546
|
+
indexed?: boolean;
|
|
1831
1547
|
/**
|
|
1832
|
-
*
|
|
1548
|
+
* Present (true) exactly when a calendar bucket over this field passes the
|
|
1549
|
+
* gate: a physically filterable `number.timestamp` field (a dimension, when
|
|
1550
|
+
* the table declares dimensions) on an adapter with calendar buckets
|
|
1551
|
+
* (`bucketUnits`). Since 0.1.132.
|
|
1833
1552
|
*/
|
|
1834
|
-
|
|
1553
|
+
bucketable?: true;
|
|
1554
|
+
}
|
|
1555
|
+
/** Built-in CRUD operation names; map 1:1 to public method names. */
|
|
1556
|
+
type TCrudOp = "query" | "pages" | "one" | "geo" | "insert" | "update" | "replace" | "remove";
|
|
1557
|
+
/**
|
|
1558
|
+
* CRUD permissions advertised in `/meta`. Key absent → operation is denied or
|
|
1559
|
+
* not exposed. Key present → operation is allowed; the `string[]` value is the
|
|
1560
|
+
* accepted UniQuery control whitelist for read ops (`[]` for write ops, which
|
|
1561
|
+
* take no controls — presence still signals "allowed").
|
|
1562
|
+
*/
|
|
1563
|
+
type TCrudPermissions = Partial<Record<TCrudOp, string[]>>;
|
|
1564
|
+
/** Response payload for `GET /meta`. */
|
|
1565
|
+
interface TMetaResponse {
|
|
1566
|
+
searchable: boolean;
|
|
1567
|
+
vectorSearchable: boolean;
|
|
1568
|
+
/** Whether the adapter supports `geoSearch()` AND the table declares a geo index. */
|
|
1569
|
+
geoSearchable?: boolean;
|
|
1570
|
+
searchIndexes: TSearchIndexInfo[];
|
|
1571
|
+
primaryKeys: string[];
|
|
1572
|
+
preferredId: string[];
|
|
1573
|
+
relations: TRelationInfo[];
|
|
1574
|
+
fields: Record<string, TFieldMeta>;
|
|
1575
|
+
type: TSerializedAnnotatedType;
|
|
1576
|
+
actions: TDbActionInfo[];
|
|
1577
|
+
crud: TCrudPermissions;
|
|
1835
1578
|
/**
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1579
|
+
* Physical column name annotated with `@db.column.version`, when the table
|
|
1580
|
+
* opts into optimistic concurrency control (OCC). Absent for tables without
|
|
1581
|
+
* the annotation, i.e. last-write-wins (default) behavior.
|
|
1582
|
+
*/
|
|
1583
|
+
versionColumn?: string;
|
|
1584
|
+
/**
|
|
1585
|
+
* Calendar-bucket units the adapter can group by (`{ $bucket }` in an
|
|
1586
|
+
* aggregate `$select`, URL `bucket(field,unit,…)`); omitted when it has
|
|
1587
|
+
* none. Since 0.1.132.
|
|
1588
|
+
*/
|
|
1589
|
+
bucketUnits?: BucketUnit[];
|
|
1590
|
+
}
|
|
1591
|
+
/** Where the action applies on the UI. */
|
|
1592
|
+
type TDbActionLevel = "table" | "row" | "rows";
|
|
1593
|
+
/**
|
|
1594
|
+
* Semantic intent the UI maps to its own visual language (color, prominence).
|
|
1595
|
+
*
|
|
1596
|
+
* Suggested visual prominence (most → least): `negative` > `warning` > `primary`
|
|
1597
|
+
* > `positive` > `secondary`. Use `negative` for destructive ops (delete, purge),
|
|
1598
|
+
* `warning` for risky-but-non-destructive ops (retry payment, force recompute,
|
|
1599
|
+
* reset state), `primary` for the headline action, `positive` for benign
|
|
1600
|
+
* confirmations (approve, publish), `secondary` for everything else.
|
|
1601
|
+
*/
|
|
1602
|
+
type TDbActionIntent = "positive" | "negative" | "warning" | "primary" | "secondary";
|
|
1603
|
+
/** How the UI client should handle the action when invoked. */
|
|
1604
|
+
type TDbActionProcessor = "backend" | "navigate" | "custom";
|
|
1605
|
+
/**
|
|
1606
|
+
* Single action descriptor in the `/meta` envelope. Flat shape — `processor`
|
|
1607
|
+
* is a string discriminator; `value` is its sibling and is always populated.
|
|
1608
|
+
*
|
|
1609
|
+
* - `processor: 'backend'` — UI POSTs to `value` (full HTTP path).
|
|
1610
|
+
* - `processor: 'navigate'` — UI routes to `value` (URL template; `$1` is the row PK).
|
|
1611
|
+
* - `processor: 'custom'` — UI dispatches `value` as an event name (defaults to action `name`).
|
|
1612
|
+
*/
|
|
1613
|
+
interface TDbActionInfo {
|
|
1614
|
+
name: string;
|
|
1615
|
+
label: string;
|
|
1616
|
+
level: TDbActionLevel;
|
|
1617
|
+
processor: TDbActionProcessor;
|
|
1618
|
+
value: string;
|
|
1619
|
+
icon?: string;
|
|
1620
|
+
intent?: TDbActionIntent;
|
|
1621
|
+
description?: string;
|
|
1622
|
+
order?: number;
|
|
1623
|
+
default?: boolean;
|
|
1624
|
+
/**
|
|
1625
|
+
* Confirmation prompt copy. String form is shown verbatim. Tuple form is
|
|
1626
|
+
* `[singular, plural]`: the UI picks `[0]` when the action will execute
|
|
1627
|
+
* against a single PK (always for `'row'`-level; for `'rows'`-level when the
|
|
1628
|
+
* current selection has exactly one PK) and `[1]` otherwise.
|
|
1842
1629
|
*
|
|
1843
|
-
*
|
|
1630
|
+
* Placeholder substitution is UI-resolved, not server-parsed. Conventional
|
|
1631
|
+
* placeholders: `$1` for the single PK (singular form) and `$N` for the
|
|
1632
|
+
* count (plural form), e.g. `['Delete order $1?', 'Delete $N orders?']`.
|
|
1844
1633
|
*/
|
|
1845
|
-
|
|
1634
|
+
promptText?: string | [string, string];
|
|
1846
1635
|
/**
|
|
1847
|
-
*
|
|
1636
|
+
* Single-character keyboard shortcut hint. The server stores this verbatim
|
|
1637
|
+
* — choice of modifier prefix (Alt+, Ctrl+, bare key) and activation scope
|
|
1638
|
+
* (e.g. only when an actions dropdown is open) are UI/UX concerns. Conflict
|
|
1639
|
+
* resolution between actions sharing the same key is also up to the UI;
|
|
1640
|
+
* the server does no dedup.
|
|
1848
1641
|
*/
|
|
1849
|
-
|
|
1642
|
+
shortcut?: string;
|
|
1850
1643
|
/**
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1853
|
-
*
|
|
1644
|
+
* Stringified gate predicate (`fn.toString()`). Present only for `'row'`
|
|
1645
|
+
* and `'rows'` level actions whose decorator declared a `disabled` function.
|
|
1646
|
+
* The function is the batch shape `(rows: TRow[]) => boolean[]` (sync). The
|
|
1647
|
+
* UI evaluates against a level-specific scope to grey-out / hide the
|
|
1648
|
+
* button. The server has already enforced this predicate before the
|
|
1649
|
+
* action's handler ran — the server is authoritative; this field is purely
|
|
1650
|
+
* a UI hint.
|
|
1854
1651
|
*/
|
|
1855
|
-
|
|
1652
|
+
disabled?: string;
|
|
1856
1653
|
/**
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
1654
|
+
* Name of the `.as` interface the action's `@InputForm()` parameter expects
|
|
1655
|
+
* (the compiled class's `.name`). Present only when the handler declares an
|
|
1656
|
+
* `@InputForm(FormType)` parameter. Clients fetch the serialized schema via
|
|
1657
|
+
* `GET /meta/form/:name` on the same controller and render a form to
|
|
1658
|
+
* collect the `input` field of the action's request envelope.
|
|
1860
1659
|
*/
|
|
1861
|
-
|
|
1660
|
+
inputForm?: string;
|
|
1661
|
+
}
|
|
1662
|
+
interface TDbInsertResult {
|
|
1663
|
+
insertedId: unknown;
|
|
1664
|
+
}
|
|
1665
|
+
interface TDbInsertManyResult {
|
|
1666
|
+
insertedCount: number;
|
|
1667
|
+
insertedIds: unknown[];
|
|
1668
|
+
}
|
|
1669
|
+
interface TDbUpdateResult {
|
|
1670
|
+
matchedCount: number;
|
|
1671
|
+
modifiedCount: number;
|
|
1672
|
+
}
|
|
1673
|
+
interface TDbDeleteResult {
|
|
1674
|
+
deletedCount: number;
|
|
1675
|
+
}
|
|
1676
|
+
type TDbIndexType = "plain" | "unique" | "fulltext" | "geo";
|
|
1677
|
+
interface TDbIndexField {
|
|
1678
|
+
name: string;
|
|
1679
|
+
sort: "asc" | "desc";
|
|
1680
|
+
weight?: number;
|
|
1862
1681
|
/**
|
|
1863
|
-
*
|
|
1682
|
+
* Whether the indexed field is optional (declared `field?:` in the model).
|
|
1683
|
+
* Resolved during index finalization. Adapters use this to make a unique
|
|
1684
|
+
* index "present-only" so multiple value-less rows are tolerated — matching
|
|
1685
|
+
* SQL's `NULLS DISTINCT` default. SQL adapters get this for free and ignore
|
|
1686
|
+
* the flag; MongoDB needs it to emit a partial unique index.
|
|
1864
1687
|
*/
|
|
1865
|
-
|
|
1688
|
+
optional?: boolean;
|
|
1689
|
+
/**
|
|
1690
|
+
* Resolved design type of the field ('string', 'number', 'boolean', …).
|
|
1691
|
+
* Carried alongside {@link optional} so adapters can derive a type-correct
|
|
1692
|
+
* present-only filter (e.g. Mongo's `partialFilterExpression`) without
|
|
1693
|
+
* re-resolving the field type. Undefined when the field cannot be resolved.
|
|
1694
|
+
*/
|
|
1695
|
+
designType?: string;
|
|
1696
|
+
}
|
|
1697
|
+
interface TDbIndex {
|
|
1698
|
+
/** Unique key used for identity/diffing (e.g., "atscript__plain__email") */
|
|
1699
|
+
key: string;
|
|
1700
|
+
/** Human-readable index name. */
|
|
1701
|
+
name: string;
|
|
1702
|
+
/** Index type. */
|
|
1703
|
+
type: TDbIndexType;
|
|
1704
|
+
/** Ordered list of fields in the index. */
|
|
1705
|
+
fields: TDbIndexField[];
|
|
1706
|
+
}
|
|
1707
|
+
type TDbDefaultFn = "increment" | "uuid" | "now";
|
|
1708
|
+
type TDbCollation = "binary" | "nocase" | "unicode";
|
|
1709
|
+
type TDbDefaultValue = {
|
|
1710
|
+
kind: "value";
|
|
1711
|
+
value: string;
|
|
1712
|
+
} | {
|
|
1713
|
+
kind: "fn";
|
|
1714
|
+
fn: TDbDefaultFn;
|
|
1715
|
+
start?: number;
|
|
1716
|
+
};
|
|
1717
|
+
interface TIdDescriptor {
|
|
1718
|
+
/** Field names that form the primary key. */
|
|
1719
|
+
fields: string[];
|
|
1720
|
+
/** Whether this is a composite key (multiple fields). */
|
|
1721
|
+
isComposite: boolean;
|
|
1722
|
+
}
|
|
1723
|
+
/** A legitimate row-identifier shape: primary key or a unique index. */
|
|
1724
|
+
interface TIdentification {
|
|
1725
|
+
/** Logical (path) field names that form this identifier. */
|
|
1726
|
+
fields: readonly string[];
|
|
1727
|
+
/** `'primaryKey'` for the PK; the unique-index name otherwise. */
|
|
1728
|
+
source: string;
|
|
1729
|
+
}
|
|
1730
|
+
type TDbStorageType = "column" | "flattened" | "json";
|
|
1731
|
+
interface TDbFieldMeta {
|
|
1732
|
+
/** The dot-notation path to this field (logical name). */
|
|
1733
|
+
path: string;
|
|
1734
|
+
/** The annotated type for this field. */
|
|
1735
|
+
type: TAtscriptAnnotatedType;
|
|
1736
|
+
/** Physical column/field name (from @db.column, __-separated for flattened, or same as path). */
|
|
1737
|
+
physicalName: string;
|
|
1738
|
+
/** Resolved design type: 'string', 'number', 'boolean', 'object', 'json', etc. */
|
|
1739
|
+
designType: string;
|
|
1740
|
+
/** Whether the field is optional. */
|
|
1741
|
+
optional: boolean;
|
|
1742
|
+
/** Whether this field is part of the primary key (@meta.id). */
|
|
1743
|
+
isPrimaryKey: boolean;
|
|
1744
|
+
/** Whether this field is excluded from the DB (@db.ignore). */
|
|
1745
|
+
ignored: boolean;
|
|
1746
|
+
/** Default value from @db.default.* */
|
|
1747
|
+
defaultValue?: TDbDefaultValue;
|
|
1866
1748
|
/**
|
|
1867
|
-
*
|
|
1749
|
+
* How this field is stored in the database.
|
|
1750
|
+
* - 'column': a standard scalar column (default for primitives)
|
|
1751
|
+
* - 'flattened': a leaf scalar from a flattened nested object
|
|
1752
|
+
* - 'json': stored as a single JSON column (arrays, @db.json fields)
|
|
1868
1753
|
*/
|
|
1869
|
-
|
|
1870
|
-
data: Array<DbResponse<DataType, NavType, Q>>;
|
|
1871
|
-
count: number;
|
|
1872
|
-
}>;
|
|
1754
|
+
storage: TDbStorageType;
|
|
1873
1755
|
/**
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1876
|
-
*
|
|
1877
|
-
* - Plain fields in $select are a subset of $groupBy
|
|
1878
|
-
* - When dimensions/measures are defined (strict mode): $groupBy fields
|
|
1879
|
-
* must be dimensions, aggregate $field values must be measures (or '*')
|
|
1880
|
-
*
|
|
1881
|
-
* Translates field names, delegates to adapter.aggregate(),
|
|
1882
|
-
* then reverse-maps and applies fromStorage formatters on results.
|
|
1756
|
+
* For flattened fields: the dot-notation path (same as `path`).
|
|
1757
|
+
* E.g., for physicalName 'contact__email', this is 'contact.email'.
|
|
1758
|
+
* Undefined for non-flattened fields.
|
|
1883
1759
|
*/
|
|
1884
|
-
|
|
1885
|
-
/**
|
|
1886
|
-
|
|
1887
|
-
/**
|
|
1888
|
-
|
|
1889
|
-
/** Whether the adapter can sort by a given field (proxies adapter capability). */
|
|
1890
|
-
canSortField(fd: TDbFieldMeta): boolean;
|
|
1891
|
-
/** Returns available search indexes from the adapter. */
|
|
1892
|
-
getSearchIndexes(): TSearchIndexInfo[];
|
|
1760
|
+
flattenedFrom?: string;
|
|
1761
|
+
/** Old physical column name from @db.column.renamed (for rename migration). */
|
|
1762
|
+
renamedFrom?: string;
|
|
1763
|
+
/** Collation from @db.column.collate (e.g. 'nocase', 'binary', 'unicode'). */
|
|
1764
|
+
collate?: TDbCollation;
|
|
1893
1765
|
/**
|
|
1894
|
-
*
|
|
1766
|
+
* Whether this field is index-backed: it participates in an explicit index
|
|
1767
|
+
* (@db.index.plain, @db.index.unique, @db.index.fulltext) OR is a primary key
|
|
1768
|
+
* or unique field (which are always index-backed — Mongo `_id`, SQL PK/unique
|
|
1769
|
+
* constraints — even without an explicit `@db.index*`).
|
|
1895
1770
|
*/
|
|
1896
|
-
|
|
1771
|
+
isIndexed?: boolean;
|
|
1772
|
+
/** Literal currency code from `@db.amount.currency 'EUR'`. */
|
|
1773
|
+
currencyCode?: string;
|
|
1774
|
+
/** Sibling field path from `@db.amount.currency.ref 'fieldName'`. */
|
|
1775
|
+
currencyRefField?: string;
|
|
1776
|
+
/** Literal unit-of-measure from `@db.unit 'kg'`. */
|
|
1777
|
+
unitCode?: string;
|
|
1778
|
+
/** Sibling field path from `@db.unit.ref 'fieldName'`. */
|
|
1779
|
+
unitRefField?: string;
|
|
1897
1780
|
/**
|
|
1898
|
-
*
|
|
1781
|
+
* For FK fields: the resolved field metadata of the referenced (target) PK column.
|
|
1782
|
+
* Adapters use this in `typeMapper` to produce matching DB types for FK columns
|
|
1783
|
+
* (e.g., `typeMapper(field.fkTargetField)` to inherit the target PK's DB type).
|
|
1784
|
+
* Undefined for non-FK fields or when the target cannot be resolved.
|
|
1899
1785
|
*/
|
|
1900
|
-
|
|
1901
|
-
data: Array<DbResponse<DataType, NavType, Q>>;
|
|
1902
|
-
count: number;
|
|
1903
|
-
}>;
|
|
1904
|
-
/** Whether the underlying adapter supports vector similarity search. */
|
|
1905
|
-
isVectorSearchable(): boolean;
|
|
1786
|
+
fkTargetField?: TDbFieldMeta;
|
|
1906
1787
|
/**
|
|
1907
|
-
*
|
|
1908
|
-
*
|
|
1909
|
-
*
|
|
1910
|
-
*
|
|
1911
|
-
*
|
|
1788
|
+
* `@db.encrypted` — the value is AES-256-GCM encrypted by the core layer
|
|
1789
|
+
* before reaching the adapter. Adapters must map the column to an unbounded
|
|
1790
|
+
* text type and veto filtering/sorting (`canFilterField`/`canSortField`).
|
|
1791
|
+
* The descriptor's `designType` is forced to `'string'` (ciphertext envelope);
|
|
1792
|
+
* the declared type stays available via `type` for validation.
|
|
1912
1793
|
*/
|
|
1913
|
-
|
|
1794
|
+
encrypted?: boolean;
|
|
1914
1795
|
/**
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
* Overloads:
|
|
1918
|
-
* - `vectorSearchWithCount(vector, query?)` — uses default vector index
|
|
1919
|
-
* - `vectorSearchWithCount(indexName, vector, query?)` — targets a specific vector index
|
|
1796
|
+
* The field's declared type is the `db.geoPoint` primitive (`[lng, lat]` tuple).
|
|
1797
|
+
* Adapters map this to their native geo storage (e.g. MongoDB GeoJSON Point).
|
|
1920
1798
|
*/
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1799
|
+
isGeoPoint?: boolean;
|
|
1800
|
+
}
|
|
1801
|
+
interface TValueFormatterPair {
|
|
1802
|
+
/** Converts a JS value to storage representation (write + filter paths). */
|
|
1803
|
+
toStorage: (value: unknown) => unknown;
|
|
1804
|
+
/** Converts a storage value back to JS representation (read path). */
|
|
1805
|
+
fromStorage: (value: unknown) => unknown;
|
|
1806
|
+
}
|
|
1807
|
+
type TDbReferentialAction = "cascade" | "restrict" | "noAction" | "setNull" | "setDefault";
|
|
1808
|
+
interface TDbForeignKey {
|
|
1809
|
+
/** FK field names on this table (local columns). */
|
|
1810
|
+
fields: string[];
|
|
1811
|
+
/** Target table name (from the chain ref's type @db.table annotation). */
|
|
1812
|
+
targetTable: string;
|
|
1813
|
+
/** Target field names on the referenced table. */
|
|
1814
|
+
targetFields: string[];
|
|
1815
|
+
/** Lazy reference to the target annotated type (for on-demand table resolution). */
|
|
1816
|
+
targetTypeRef?: () => TAtscriptAnnotatedType;
|
|
1817
|
+
/** Alias grouping FK fields (if any). */
|
|
1818
|
+
alias?: string;
|
|
1819
|
+
/** Referential action on delete. */
|
|
1820
|
+
onDelete?: TDbReferentialAction;
|
|
1821
|
+
/** Referential action on update. */
|
|
1822
|
+
onUpdate?: TDbReferentialAction;
|
|
1823
|
+
}
|
|
1824
|
+
/** Describes an existing column in the database (from introspection). */
|
|
1825
|
+
interface TExistingColumn {
|
|
1826
|
+
name: string;
|
|
1827
|
+
type: string;
|
|
1828
|
+
notnull: boolean;
|
|
1829
|
+
pk: boolean;
|
|
1830
|
+
/** Serialized default value (e.g., "'active'", "NULL"). */
|
|
1831
|
+
dflt_value?: string;
|
|
1832
|
+
}
|
|
1833
|
+
/** Result of comparing desired schema against existing database columns. */
|
|
1834
|
+
interface TColumnDiff {
|
|
1835
|
+
added: TDbFieldMeta[];
|
|
1836
|
+
removed: TExistingColumn[];
|
|
1837
|
+
renamed: Array<{
|
|
1838
|
+
field: TDbFieldMeta;
|
|
1839
|
+
oldName: string;
|
|
1840
|
+
}>;
|
|
1841
|
+
typeChanged: Array<{
|
|
1842
|
+
field: TDbFieldMeta;
|
|
1843
|
+
existingType: string;
|
|
1844
|
+
}>;
|
|
1845
|
+
nullableChanged: Array<{
|
|
1846
|
+
field: TDbFieldMeta;
|
|
1847
|
+
wasNullable: boolean;
|
|
1848
|
+
}>;
|
|
1849
|
+
defaultChanged: Array<{
|
|
1850
|
+
field: TDbFieldMeta;
|
|
1851
|
+
oldDefault?: string;
|
|
1852
|
+
newDefault?: string;
|
|
1853
|
+
}>;
|
|
1854
|
+
conflicts: Array<{
|
|
1855
|
+
field: TDbFieldMeta;
|
|
1856
|
+
oldName: string;
|
|
1857
|
+
conflictsWith: string;
|
|
1924
1858
|
}>;
|
|
1925
|
-
/** Resolves overloaded vector search arguments into canonical form. */
|
|
1926
|
-
private _resolveVectorSearchArgs;
|
|
1927
|
-
/** Whether the underlying adapter supports geospatial search. */
|
|
1928
|
-
isGeoSearchable(): boolean;
|
|
1929
1859
|
/**
|
|
1930
|
-
*
|
|
1931
|
-
*
|
|
1932
|
-
*
|
|
1933
|
-
*
|
|
1934
|
-
*
|
|
1935
|
-
* Overloads:
|
|
1936
|
-
* - `geoSearch(point, query?)` — uses the table's only geo index
|
|
1937
|
-
* - `geoSearch(indexName, point, query?)` — targets a specific geo index
|
|
1860
|
+
* The primary-key FIELD SET differs between the live table and the model
|
|
1861
|
+
* (set semantics — a composite-key reorder is not a change, consistent with
|
|
1862
|
+
* the schema hash). Column names are physical; a renamed PK column is
|
|
1863
|
+
* compared under its new name. Only reported when the table exists.
|
|
1864
|
+
* @since 0.1.128
|
|
1938
1865
|
*/
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1866
|
+
primaryKeyChanged?: TPrimaryKeyChange;
|
|
1867
|
+
}
|
|
1868
|
+
/** Old and new primary-key column sets of a table whose key definition moved. */
|
|
1869
|
+
interface TPrimaryKeyChange {
|
|
1870
|
+
/** Physical PK columns currently in the database (after rename mapping). */
|
|
1871
|
+
from: string[];
|
|
1872
|
+
/** Physical PK columns the model declares. */
|
|
1873
|
+
to: string[];
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* A live foreign-key constraint as introspected from the database
|
|
1877
|
+
* (outbound: declared on the table that owns it).
|
|
1878
|
+
*/
|
|
1879
|
+
interface TExistingForeignKey {
|
|
1880
|
+
/** Local (referencing) columns, in constraint order. */
|
|
1881
|
+
fields: string[];
|
|
1882
|
+
/** Referenced table name. */
|
|
1883
|
+
targetTable: string;
|
|
1884
|
+
/** Referenced columns, in constraint order. */
|
|
1885
|
+
targetFields: string[];
|
|
1886
|
+
}
|
|
1887
|
+
/**
|
|
1888
|
+
* A live foreign key that REFERENCES a given table (inbound edge), as returned
|
|
1889
|
+
* by `BaseDbAdapter.getReferencingForeignKeys(tableName)`.
|
|
1890
|
+
*/
|
|
1891
|
+
interface TReferencingForeignKey {
|
|
1892
|
+
/** The referencing (child) table. */
|
|
1893
|
+
table: string;
|
|
1894
|
+
/** Referencing columns on `table`, in constraint order. */
|
|
1895
|
+
fields: string[];
|
|
1896
|
+
/** Referenced columns on the queried table, in constraint order. */
|
|
1897
|
+
targetFields: string[];
|
|
1898
|
+
}
|
|
1899
|
+
/** Kind of a physical database object, as returned by `BaseDbAdapter.getObjectKind`. */
|
|
1900
|
+
type TDbObjectKind = "table" | "view" | "materialized";
|
|
1901
|
+
/** Options accepted by `BaseDbAdapter.ensureTable`. */
|
|
1902
|
+
interface TEnsureTableOptions {
|
|
1942
1903
|
/**
|
|
1943
|
-
*
|
|
1944
|
-
*
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
* - `geoSearchWithCount(indexName, point, query?)` — targets a specific geo index
|
|
1904
|
+
* Table names whose inline FOREIGN KEY constraints must be omitted from
|
|
1905
|
+
* CREATE TABLE — the constraints are added afterwards by `syncForeignKeys()`.
|
|
1906
|
+
* Schema sync passes the members of a foreign-key cycle so they can be
|
|
1907
|
+
* created in any order.
|
|
1948
1908
|
*/
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1909
|
+
deferForeignKeysTo?: ReadonlySet<string>;
|
|
1910
|
+
}
|
|
1911
|
+
/** Result of applying column diff to the database. */
|
|
1912
|
+
interface TSyncColumnResult {
|
|
1913
|
+
added: string[];
|
|
1914
|
+
renamed: string[];
|
|
1915
|
+
}
|
|
1916
|
+
/** A single table-level option in unified key-value format. */
|
|
1917
|
+
interface TExistingTableOption {
|
|
1918
|
+
key: string;
|
|
1919
|
+
value: string;
|
|
1920
|
+
}
|
|
1921
|
+
/** Result of comparing desired table options against existing ones. */
|
|
1922
|
+
interface TTableOptionDiff {
|
|
1923
|
+
changed: Array<{
|
|
1924
|
+
key: string;
|
|
1925
|
+
oldValue: string;
|
|
1926
|
+
newValue: string; /** Whether applying this change requires dropping and recreating the table. */
|
|
1927
|
+
destructive: boolean;
|
|
1928
|
+
}>;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Adapter-provided metadata adjustments applied atomically during the
|
|
1932
|
+
* build pipeline, before field descriptors are built.
|
|
1933
|
+
*
|
|
1934
|
+
* Replaces the old pattern where adapters mutated metadata via
|
|
1935
|
+
* back-references (`this._table.addPrimaryKey()`, etc.).
|
|
1936
|
+
*/
|
|
1937
|
+
interface TMetadataOverrides {
|
|
1938
|
+
/** Fields to add as primary keys. */
|
|
1939
|
+
addPrimaryKeys?: string[];
|
|
1940
|
+
/** Fields to remove from primary keys. */
|
|
1941
|
+
removePrimaryKeys?: string[];
|
|
1942
|
+
/** Fields to register as having a unique constraint. */
|
|
1943
|
+
addUniqueFields?: string[];
|
|
1944
|
+
/** Synthetic fields to inject into flatMap (e.g. MongoDB's `_id`). */
|
|
1945
|
+
injectFields?: Array<{
|
|
1946
|
+
path: string;
|
|
1947
|
+
type: TAtscriptAnnotatedType;
|
|
1954
1948
|
}>;
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Callback that resolves an annotated type to a queryable table instance.
|
|
1952
|
+
* Required for `$with` relation loading — each table needs to query related tables.
|
|
1953
|
+
*
|
|
1954
|
+
* Typically provided by the driver/registry (e.g. `DbSpace.getTable`).
|
|
1955
|
+
*/
|
|
1956
|
+
type TTableResolver = (type: TAtscriptAnnotatedType) => Pick<AtscriptDbTableLike, "findMany" | "loadRelations" | "primaryKeys" | "preferredId" | "relations" | "foreignKeys" | "isValidFieldPath"> | undefined;
|
|
1957
|
+
/** Minimal table interface used by the table resolver. Avoids circular dependency with AtscriptDbTable. */
|
|
1958
|
+
interface AtscriptDbTableLike {
|
|
1959
|
+
findMany(query: unknown): Promise<Array<Record<string, unknown>>>;
|
|
1960
|
+
loadRelations(rows: Array<Record<string, unknown>>, withRelations: WithRelation[]): Promise<void>;
|
|
1961
|
+
primaryKeys: readonly string[];
|
|
1962
|
+
preferredId: readonly string[];
|
|
1963
|
+
relations: ReadonlyMap<string, TDbRelation>;
|
|
1964
|
+
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
1965
|
+
getMetadata(): TableMetadata;
|
|
1966
|
+
isValidFieldPath(path: string, visited?: Set<string>): boolean;
|
|
1967
|
+
}
|
|
1968
|
+
/** Minimal writable table interface for nested creation/update. */
|
|
1969
|
+
interface AtscriptDbWritable {
|
|
1970
|
+
insertOne(payload: Record<string, unknown>, opts?: {
|
|
1971
|
+
maxDepth?: number;
|
|
1972
|
+
}): Promise<TDbInsertResult>;
|
|
1973
|
+
insertMany(payloads: Array<Record<string, unknown>>, opts?: {
|
|
1974
|
+
maxDepth?: number;
|
|
1975
|
+
_depth?: number;
|
|
1976
|
+
}): Promise<TDbInsertManyResult>;
|
|
1977
|
+
replaceOne(payload: Record<string, unknown>, opts?: {
|
|
1978
|
+
maxDepth?: number;
|
|
1979
|
+
}): Promise<TDbUpdateResult>;
|
|
1980
|
+
bulkReplace(payloads: Array<Record<string, unknown>>, opts?: {
|
|
1981
|
+
maxDepth?: number;
|
|
1982
|
+
_depth?: number;
|
|
1983
|
+
}): Promise<TDbUpdateResult>;
|
|
1984
|
+
updateOne(payload: Record<string, unknown>, opts?: {
|
|
1985
|
+
maxDepth?: number;
|
|
1986
|
+
}): Promise<TDbUpdateResult>;
|
|
1987
|
+
bulkUpdate(payloads: Array<Record<string, unknown>>, opts?: {
|
|
1988
|
+
maxDepth?: number;
|
|
1989
|
+
_depth?: number;
|
|
1990
|
+
}): Promise<TDbUpdateResult>;
|
|
1991
|
+
findOne(query: unknown): Promise<Record<string, unknown> | null>;
|
|
1992
|
+
count(query: {
|
|
1993
|
+
filter: Record<string, unknown>;
|
|
1994
|
+
}): Promise<number>;
|
|
1995
|
+
deleteMany(filter: unknown): Promise<TDbDeleteResult>;
|
|
1996
|
+
/** Pre-validate items (type + FK constraints) without inserting them. */
|
|
1997
|
+
preValidateItems(items: Array<Record<string, unknown>>, opts?: {
|
|
1998
|
+
excludeFkTargetTable?: string;
|
|
1999
|
+
}): Promise<void>;
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Callback that resolves an annotated type to a writable table instance.
|
|
2003
|
+
* Used for nested creation — inserting related records inline.
|
|
2004
|
+
*/
|
|
2005
|
+
type TWriteTableResolver = (type: TAtscriptAnnotatedType) => (AtscriptDbTableLike & AtscriptDbWritable) | undefined;
|
|
2006
|
+
/**
|
|
2007
|
+
* A child table that may need cascade/setNull processing when a parent is deleted.
|
|
2008
|
+
* Returned by the cascade resolver.
|
|
2009
|
+
*/
|
|
2010
|
+
interface TCascadeTarget {
|
|
2011
|
+
/** FK on the child table that references the parent being deleted. */
|
|
2012
|
+
fk: TDbForeignKey;
|
|
2013
|
+
/** Name of the child table that holds this FK. */
|
|
2014
|
+
childTable: string;
|
|
2015
|
+
/** Delete matching child records (goes through AtscriptDbTable for recursive cascade). */
|
|
2016
|
+
deleteMany(filter: Record<string, unknown>): Promise<TDbDeleteResult>;
|
|
2017
|
+
/** Update matching child records (for setNull — sets FK fields to null). */
|
|
2018
|
+
updateMany(filter: Record<string, unknown>, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
2019
|
+
/** Count matching child records (for restrict — check existence before delete). */
|
|
2020
|
+
count(filter: Record<string, unknown>): Promise<number>;
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Callback that finds all child tables with FKs pointing to a given parent table.
|
|
2024
|
+
* Used by AtscriptDbTable to implement application-level cascade deletes.
|
|
2025
|
+
*/
|
|
2026
|
+
type TCascadeResolver = (tableName: string) => TCascadeTarget[];
|
|
2027
|
+
/**
|
|
2028
|
+
* Minimal interface for querying a target table during FK validation.
|
|
2029
|
+
* Only `count` is needed — we check if the referenced record exists.
|
|
2030
|
+
*/
|
|
2031
|
+
interface TFkLookupTarget {
|
|
2032
|
+
count(filter: Record<string, unknown>): Promise<number>;
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Callback that resolves a table name to a queryable target for FK validation.
|
|
2036
|
+
* Returns undefined if the target table is not registered in the space.
|
|
2037
|
+
*/
|
|
2038
|
+
type TFkLookupResolver = (tableName: string) => TFkLookupTarget | undefined;
|
|
2039
|
+
interface TDbRelation {
|
|
2040
|
+
/** Direction: 'to' (FK is local), 'from' (FK is remote), or 'via' (M:N junction). */
|
|
2041
|
+
direction: "to" | "from" | "via";
|
|
2042
|
+
/** The alias used for pairing (if any). */
|
|
2043
|
+
alias?: string;
|
|
2044
|
+
/** Target type's annotated type reference. */
|
|
2045
|
+
targetType: () => TAtscriptAnnotatedType;
|
|
2046
|
+
/** Whether this is an array relation (one-to-many). */
|
|
2047
|
+
isArray: boolean;
|
|
2048
|
+
/** Junction type reference for 'via' (M:N) relations. */
|
|
2049
|
+
viaType?: () => TAtscriptAnnotatedType;
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Write payload for insert / patch paths: every key optional, and optional
|
|
2053
|
+
* columns additionally accept `null` (an explicit NULL — `undefined` means
|
|
2054
|
+
* "absent" and is dropped before the row reaches defaults or validation).
|
|
2055
|
+
*/
|
|
2056
|
+
type DbPatch<D> = { [K in keyof D]?: undefined extends D[K] ? D[K] | null : D[K] } & Record<string, unknown>;
|
|
2057
|
+
/**
|
|
2058
|
+
* Write payload for full-row replace paths: required keys stay required,
|
|
2059
|
+
* optional columns additionally accept `null` (explicit NULL).
|
|
2060
|
+
*/
|
|
2061
|
+
type DbRow<D> = { [K in keyof D]: undefined extends D[K] ? D[K] | null : D[K] } & Record<string, unknown>;
|
|
2062
|
+
/** Built-in write actions a moost-db `AsDbController` endpoint performs. */
|
|
2063
|
+
type TDbWriteAction = "insert" | "insertMany" | "replace" | "replaceMany" | "update" | "updateMany";
|
|
2064
|
+
/**
|
|
2065
|
+
* Context handed to a write {@link TWriteOptions.guard} (since 0.1.128) — and
|
|
2066
|
+
* through it to `AsDbController.guardWrite()`. The table invokes the guard
|
|
2067
|
+
* exactly once, inside its own transaction, after `undefined`-pruning,
|
|
2068
|
+
* defaults and validation and before encryption / nested-relation phases.
|
|
2069
|
+
*/
|
|
2070
|
+
interface TDbWriteGuardContext<Row = Record<string, unknown>> {
|
|
2071
|
+
/** The table method the guard runs for (`insertOne` → `insert`, `insertMany` → `insertMany`, …). */
|
|
2072
|
+
readonly action: TDbWriteAction;
|
|
1982
2073
|
/**
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
* the
|
|
1987
|
-
* + every single-field unique index; objects try PK + compound unique
|
|
1988
|
-
* indexes.
|
|
2074
|
+
* insert/replace: validated rows with SDK-side defaults applied (plaintext,
|
|
2075
|
+
* nav data still attached); update: validated patches with the identifying
|
|
2076
|
+
* PK/unique fields present and `$cas` removed. Mutate in place to enrich —
|
|
2077
|
+
* the table re-validates the rows after the guard.
|
|
1989
2078
|
*/
|
|
1990
|
-
|
|
1991
|
-
/**
|
|
1992
|
-
|
|
2079
|
+
readonly rows: Row[];
|
|
2080
|
+
/** Parallel to `rows`: expected version lifted from `$cas`, or `undefined`. */
|
|
2081
|
+
readonly expectedVersions: ReadonlyArray<number | undefined>;
|
|
1993
2082
|
/**
|
|
1994
|
-
*
|
|
2083
|
+
* Lazy, memoised pre-image of `rows[i]` by its identifying filter, read
|
|
2084
|
+
* inside the transaction. `null` when the row is missing OR when it carries
|
|
2085
|
+
* no identifying key yet (e.g. auto-increment inserts) — never throws.
|
|
1995
2086
|
*/
|
|
1996
|
-
|
|
2087
|
+
current(i: number): Promise<Row | null>;
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Context handed to a delete {@link TDeleteOptions.guard} (since 0.1.128) —
|
|
2091
|
+
* and through it to `AsDbController.guardRemove()`. Runs inside the table's
|
|
2092
|
+
* transaction; an id that resolves to no filter never reaches the guard
|
|
2093
|
+
* (`deleteOne` answers `{ deletedCount: 0 }`).
|
|
2094
|
+
*/
|
|
2095
|
+
interface TDbRemoveGuardContext<Row = Record<string, unknown>> {
|
|
2096
|
+
/** The id `deleteOne` was called with. */
|
|
2097
|
+
readonly id: unknown;
|
|
2098
|
+
/** `table.resolveIdFilter(id)` — never null here. */
|
|
2099
|
+
readonly filter: FilterExpr;
|
|
2100
|
+
/** Lazy, memoised pre-image of the row about to be deleted (`null` when missing). */
|
|
2101
|
+
current(): Promise<Row | null>;
|
|
2102
|
+
}
|
|
2103
|
+
/** A validated-stage write guard — see {@link TWriteOptions.guard}. */
|
|
2104
|
+
type TDbWriteGuard<Row = Record<string, unknown>> = (ctx: TDbWriteGuardContext<Row>) => void | Promise<void>;
|
|
2105
|
+
/** A validated-stage delete guard — see {@link TDeleteOptions.guard}. */
|
|
2106
|
+
type TDbRemoveGuard<Row = Record<string, unknown>> = (ctx: TDbRemoveGuardContext<Row>) => void | Promise<void>;
|
|
2107
|
+
/** Options of `AtscriptDbTable.touchMany` (since 0.1.129). */
|
|
2108
|
+
interface TTouchManyOptions {
|
|
1997
2109
|
/**
|
|
1998
|
-
*
|
|
2110
|
+
* `'all'` (default): every key must match its stored version — a stale or
|
|
2111
|
+
* missing row throws `DbError("CAS_MISMATCH")` and no version moves.
|
|
2112
|
+
* `'any'`: bump whatever matches and report the honest counts.
|
|
1999
2113
|
*/
|
|
2000
|
-
|
|
2114
|
+
require?: "all" | "any";
|
|
2115
|
+
}
|
|
2116
|
+
/** Options of `insertOne/Many`, `replaceOne` / `bulkReplace`, `updateOne` / `bulkUpdate`. */
|
|
2117
|
+
interface TWriteOptions<Row = Record<string, unknown>> {
|
|
2118
|
+
/** Nested-relation write recursion limit (default 3). */
|
|
2119
|
+
maxDepth?: number;
|
|
2001
2120
|
/**
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
2121
|
+
* Validated-stage guard (since 0.1.128): invoked exactly once inside the
|
|
2122
|
+
* table's transaction, after defaults + validation and before encryption
|
|
2123
|
+
* and nested-relation phases, with the rows the table is about to write.
|
|
2124
|
+
* Rows may be enriched in place — they are validated again afterwards. A
|
|
2125
|
+
* throw rolls the transaction back and propagates unchanged. Never runs
|
|
2126
|
+
* for the nested re-entries a deep write performs on related tables.
|
|
2004
2127
|
*/
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2128
|
+
guard?: TDbWriteGuard<Row>;
|
|
2129
|
+
}
|
|
2130
|
+
/** Options of `deleteOne`. */
|
|
2131
|
+
interface TDeleteOptions<Row = Record<string, unknown>> {
|
|
2009
2132
|
/**
|
|
2010
|
-
*
|
|
2011
|
-
*
|
|
2133
|
+
* Validated-stage guard (since 0.1.128): invoked inside the table's
|
|
2134
|
+
* transaction after the id resolved to a filter and before cascade /
|
|
2135
|
+
* delete. A throw rolls the transaction back and propagates unchanged.
|
|
2012
2136
|
*/
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2137
|
+
guard?: TDbRemoveGuard<Row>;
|
|
2138
|
+
}
|
|
2139
|
+
/**
|
|
2140
|
+
* Adds `null` to every optional property of `O`. Optional columns store SQL
|
|
2141
|
+
* NULL / Mongo null, and the runtime validator accepts `null` for optional
|
|
2142
|
+
* props — so filter shapes (`{ note: null }`, `{ note: { $ne: null } }`) and
|
|
2143
|
+
* row shapes must admit it at the type level too. Homomorphic: keys and
|
|
2144
|
+
* required properties are unchanged; applying it twice is a no-op.
|
|
2145
|
+
*/
|
|
2146
|
+
type NullableOptional<O> = { [K in keyof O]: undefined extends O[K] ? O[K] | null : O[K] };
|
|
2147
|
+
//#endregion
|
|
2148
|
+
//#region src/query/buckets.d.ts
|
|
2149
|
+
/**
|
|
2150
|
+
* A calendar bucket as adapters receive it (`controls.$select.buckets`): the
|
|
2151
|
+
* normalized uniqu bucket (canonical zone, week start, alias) whose `field` is
|
|
2152
|
+
* the PHYSICAL column / document path, plus the source field's descriptor —
|
|
2153
|
+
* dialects read the storage kind from `fd` (since 0.1.132).
|
|
2154
|
+
*/
|
|
2155
|
+
type TResolvedBucket = ResolvedBucket & {
|
|
2156
|
+
/** Physical column (relational) or document path (nested-object adapters). */field: string; /** Descriptor of the source field (a `number.timestamp` leaf). */
|
|
2157
|
+
fd: TDbFieldMeta;
|
|
2158
|
+
};
|
|
2159
|
+
/**
|
|
2160
|
+
* The names a bucket alias must not shadow: `TableMetadata` provides them, and
|
|
2161
|
+
* moost-db's HTTP gate assembles the same set from its readable and capability
|
|
2162
|
+
* index, so both layers resolve against the same names.
|
|
2163
|
+
*/
|
|
2164
|
+
interface TBucketFieldSource {
|
|
2165
|
+
/** Every logical path of the table type (nested parents and navigation fields included). */
|
|
2166
|
+
flatMap: ReadonlyMap<string, unknown>;
|
|
2167
|
+
/** Every field descriptor's `physicalName` — reserved too. */
|
|
2168
|
+
physicalNames: ReadonlySet<string>;
|
|
2169
|
+
navFields: ReadonlySet<string>;
|
|
2016
2170
|
}
|
|
2171
|
+
/**
|
|
2172
|
+
* The one normalizer of `$select` computed entries (since 0.1.132) — uniqu's
|
|
2173
|
+
* `resolveBuckets` (entry shapes, unit, time zone canonicalization, week
|
|
2174
|
+
* start, alias syntax and uniqueness, "grouped queries only", "must also
|
|
2175
|
+
* appear in $groupBy", string `$groupBy` entries) with the table's names as
|
|
2176
|
+
* the collision set: a bucket alias may not equal a logical path, a physical
|
|
2177
|
+
* column or a navigation field, so a label is never reverse-mapped as a
|
|
2178
|
+
* column.
|
|
2179
|
+
*
|
|
2180
|
+
* Which layer validates what:
|
|
2181
|
+
* - **Shapes** (this normalizer) run FIRST at every entry point — the core's
|
|
2182
|
+
* read path (`guardQuery`), its aggregate path (`AtscriptDbReadable.aggregate`,
|
|
2183
|
+
* which hands the result on to `guardAggregate` and the field mapper), and
|
|
2184
|
+
* moost-db's HTTP gate — so both layers answer with the same wording.
|
|
2185
|
+
* Everything downstream (`collectQueryPaths`, the field mappers,
|
|
2186
|
+
* `UniquSelect`, adapters) assumes normalized input and does not re-check.
|
|
2187
|
+
* - **Schema** (timestamp-typed source, JSON ancestor, encryption, physical
|
|
2188
|
+
* filterability) is the path guard's (`guardPath` op `bucket`), mirrored by
|
|
2189
|
+
* moost-db's capability index; dimensions are the aggregate rules'.
|
|
2190
|
+
* - **Adapter capability** (`calendarBucketUnits()`) is `guardAggregate`'s
|
|
2191
|
+
* (`BUCKET_NOT_SUPPORTED`); SQL builders only re-assert the inlined
|
|
2192
|
+
* literals (defense in depth).
|
|
2193
|
+
*
|
|
2194
|
+
* `aggregate` defaults to "`$groupBy` is non-empty".
|
|
2195
|
+
*
|
|
2196
|
+
* @throws DbError `INVALID_QUERY` carrying every issue (`path` `$select` / `$groupBy`).
|
|
2197
|
+
*/
|
|
2198
|
+
declare function resolveCalendarBuckets(controls: {
|
|
2199
|
+
$select?: unknown;
|
|
2200
|
+
$groupBy?: unknown;
|
|
2201
|
+
} | undefined, fields: TBucketFieldSource, aggregate?: boolean): ResolvedBucket[];
|
|
2202
|
+
/**
|
|
2203
|
+
* Whether a field can be the source of a calendar bucket: a `number` /
|
|
2204
|
+
* `integer` leaf carrying the `timestamp` tag (`number.timestamp`,
|
|
2205
|
+
* `.created`, `.updated`) that is not `@db.encrypted`. The type is the
|
|
2206
|
+
* declaration — no annotation opts a field in. Physical filterability is the
|
|
2207
|
+
* caller's (the core path guard and moost-db's capability index both add it).
|
|
2208
|
+
*/
|
|
2209
|
+
declare function isBucketableField(fd: TDbFieldMeta): boolean;
|
|
2210
|
+
/**
|
|
2211
|
+
* Whether a field holds a JSON value — a `@db.json` object / JSON-stored
|
|
2212
|
+
* column or an array. The members of `TableMetadata.jsonValueParents` (and
|
|
2213
|
+
* moost-db's equivalent set) — see {@link jsonValueAncestor}.
|
|
2214
|
+
*/
|
|
2215
|
+
declare function isJsonValueField(fd: TDbFieldMeta): boolean;
|
|
2216
|
+
/**
|
|
2217
|
+
* The outermost ancestor of `path` in `jsonValueParents` (the paths of the
|
|
2218
|
+
* {@link isJsonValueField} descriptors), or `undefined`. A timestamp inside a
|
|
2219
|
+
* JSON value is never a bucket source — relational adapters cannot address
|
|
2220
|
+
* it and nested-object adapters (which can) must not diverge from them.
|
|
2221
|
+
*/
|
|
2222
|
+
declare function jsonValueAncestor(path: string, jsonValueParents: ReadonlySet<string>): string | undefined;
|
|
2017
2223
|
//#endregion
|
|
2018
|
-
export {
|
|
2224
|
+
export { TDbWriteGuardContext as $, TDbActionIntent as A, isGeoPointType as At, TDbIndexField as B, NoopLogger as Bt, PrimaryKeyOf$1 as C, TypedWithRelation as Ct, TCrudOp as D, TableMetadata as Dt, TColumnDiff as E, WithRelation$1 as Et, TDbDefaultValue as F, resolveDesignType as Ft, TDbReferentialAction as G, TDbInsertManyResult as H, UniquSelect as Ht, TDbDeleteResult as I, DbEncryption as It, TDbRemoveGuardContext as J, TDbRelation as K, TDbFieldMeta as L, TDbEncryptionOptions as Lt, TDbActionProcessor as M, BaseDbAdapter as Mt, TDbCollation as N, AtscriptDbReadable as Nt, TCrudPermissions as O, findAncestorInSet as Ot, TDbDefaultFn as P, DbResponse as Pt, TDbWriteGuard as Q, TDbForeignKey as R, DocumentFieldMapper as Rt, OwnPropsOf$1 as S, TWriteTableResolver as St, TCascadeTarget as T, UniqueryControls$1 as Tt, TDbInsertResult as U, TDbIndexType as V, TGenericLogger as Vt, TDbObjectKind as W, TDbUpdateResult as X, TDbStorageType as Y, TDbWriteAction as Z, FieldOpsFor as _, TTableOptionDiff as _t, jsonValueAncestor as a, TFieldMeta as at, NavPropsOf$1 as b, TValueFormatterPair as bt, AggregateExpr$1 as c, TIdDescriptor as ct, AggregateResult as d, TMetadataOverrides as dt, TDeleteOptions as et, AtscriptDbWritable as f, TPrimaryKeyChange as ft, DbRow as g, TSyncColumnResult as gt, DbQuery as h, TSearchIndexInfo as ht, isJsonValueField as i, TExistingTableOption as it, TDbActionLevel as j, ALL_BUCKET_UNITS as jt, TDbActionInfo as k, isGeoIndexableType as kt, AggregateFn as l, TIdentification as lt, DbPatch as m, TRelationInfo as mt, TResolvedBucket as n, TExistingColumn as nt, resolveCalendarBuckets as o, TFkLookupResolver as ot, DbControls as p, TReferencingForeignKey as pt, TDbRemoveGuard as q, isBucketableField as r, TExistingForeignKey as rt, AggregateControls as s, TFkLookupTarget as st, TBucketFieldSource as t, TEnsureTableOptions as tt, AggregateQuery$1 as u, TMetaResponse as ut, FilterExpr$1 as v, TTableResolver as vt, TCascadeResolver as w, Uniquery$1 as wt, NullableOptional as x, TWriteOptions as xt, FlatOf$1 as y, TTouchManyOptions as yt, TDbIndex as z, FieldMappingStrategy as zt };
|