@atscript/db 0.1.103 → 0.1.104
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/{db-readable-mhPp-MPv.d.cts → db-readable-BepVc21V.d.cts} +196 -3
- package/dist/{db-readable-CXdHBtF6.d.mts → db-readable-Ds01ezjj.d.mts} +196 -3
- package/dist/{db-space-BYyVZnL1.d.mts → db-space-0U_4PiNS.d.mts} +33 -4
- package/dist/{db-space-BhOc9_OO.d.cts → db-space-CWhIEC7E.d.cts} +33 -4
- package/dist/{db-view-GcbegVBD.cjs → db-view-Bw_hlrWw.cjs} +477 -6
- package/dist/{db-view-Sq4wCceR.mjs → db-view-TyzB5VFb.mjs} +442 -7
- package/dist/index.cjs +162 -5
- package/dist/index.d.cts +34 -4
- package/dist/index.d.mts +34 -4
- package/dist/index.mjs +156 -6
- package/dist/plugin.cjs +48 -0
- package/dist/plugin.mjs +48 -0
- package/dist/rel.d.cts +1 -1
- package/dist/rel.d.mts +1 -1
- package/dist/sync.cjs +2 -1
- package/dist/sync.d.cts +4 -2
- package/dist/sync.d.mts +4 -2
- package/dist/sync.mjs +2 -1
- package/dist/{validator-f43UcYiY.cjs → validator-BSk8lPH1.cjs} +15 -0
- package/dist/{validator-bLsSgi0N.mjs → validator-C7plt6Kf.mjs} +15 -0
- package/dist/validator.cjs +1 -1
- package/dist/validator.mjs +1 -1
- package/package.json +1 -1
|
@@ -55,6 +55,14 @@ interface TGenericLogger {
|
|
|
55
55
|
declare const NoopLogger: TGenericLogger;
|
|
56
56
|
//#endregion
|
|
57
57
|
//#region src/table/table-metadata.d.ts
|
|
58
|
+
/** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
|
|
59
|
+
declare function isGeoPointType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Returns true if the annotated type is acceptable for `@db.index.geo`:
|
|
62
|
+
* the `db.geoPoint` primitive or a structurally identical `number[]`
|
|
63
|
+
* (excluding `db.vector`, which is semantically an embedding).
|
|
64
|
+
*/
|
|
65
|
+
declare function isGeoIndexableType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
58
66
|
/**
|
|
59
67
|
* Computed metadata for a database table or view.
|
|
60
68
|
*
|
|
@@ -87,6 +95,8 @@ declare class TableMetadata {
|
|
|
87
95
|
versionField?: string;
|
|
88
96
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
89
97
|
quantityRefByField: Map<string, string>;
|
|
98
|
+
/** Logical paths annotated with `@db.encrypted` — stored as one opaque ciphertext column. */
|
|
99
|
+
encryptedFields: Set<string>;
|
|
90
100
|
pathToPhysical: Map<string, string>;
|
|
91
101
|
physicalToPath: Map<string, string>;
|
|
92
102
|
flattenedParents: Set<string>;
|
|
@@ -140,6 +150,14 @@ declare class TableMetadata {
|
|
|
140
150
|
* Scans `@db.*` and `@meta.id` annotations on a field during flattening.
|
|
141
151
|
*/
|
|
142
152
|
private _scanGenericAnnotations;
|
|
153
|
+
/**
|
|
154
|
+
* Build-time diagnostics for `@db.encrypted` (§6 of the field-encryption
|
|
155
|
+
* spec). Mirrors the compile-time AnnotationSpec validation so models built
|
|
156
|
+
* from pre-compiled types still fail fast.
|
|
157
|
+
*/
|
|
158
|
+
private _validateEncryptedField;
|
|
159
|
+
/** Build-time diagnostics for `@db.index.geo` (§3 of the geo-index spec). */
|
|
160
|
+
private _validateGeoIndexField;
|
|
143
161
|
private _addIndexField;
|
|
144
162
|
/**
|
|
145
163
|
* Classifies each field as column, flattened, json, or parent-object.
|
|
@@ -216,9 +234,13 @@ interface TRelationInfo {
|
|
|
216
234
|
interface TFieldMeta {
|
|
217
235
|
sortable: boolean;
|
|
218
236
|
filterable: boolean;
|
|
237
|
+
/** Present (true) when the field is `@db.encrypted` — stored as ciphertext at rest. */
|
|
238
|
+
encrypted?: boolean;
|
|
239
|
+
/** Present (true) when the field carries a `@db.index.geo` geospatial index. */
|
|
240
|
+
geo?: boolean;
|
|
219
241
|
}
|
|
220
242
|
/** Built-in CRUD operation names; map 1:1 to public method names. */
|
|
221
|
-
type TCrudOp = "query" | "pages" | "one" | "insert" | "update" | "replace" | "remove";
|
|
243
|
+
type TCrudOp = "query" | "pages" | "one" | "geo" | "insert" | "update" | "replace" | "remove";
|
|
222
244
|
/**
|
|
223
245
|
* CRUD permissions advertised in `/meta`. Key absent → operation is denied or
|
|
224
246
|
* not exposed. Key present → operation is allowed; the `string[]` value is the
|
|
@@ -230,6 +252,8 @@ type TCrudPermissions = Partial<Record<TCrudOp, string[]>>;
|
|
|
230
252
|
interface TMetaResponse {
|
|
231
253
|
searchable: boolean;
|
|
232
254
|
vectorSearchable: boolean;
|
|
255
|
+
/** Whether the adapter supports `geoSearch()` AND the table declares a geo index. */
|
|
256
|
+
geoSearchable?: boolean;
|
|
233
257
|
searchIndexes: TSearchIndexInfo[];
|
|
234
258
|
primaryKeys: string[];
|
|
235
259
|
preferredId: string[];
|
|
@@ -330,7 +354,7 @@ interface TDbUpdateResult {
|
|
|
330
354
|
interface TDbDeleteResult {
|
|
331
355
|
deletedCount: number;
|
|
332
356
|
}
|
|
333
|
-
type TDbIndexType = "plain" | "unique" | "fulltext";
|
|
357
|
+
type TDbIndexType = "plain" | "unique" | "fulltext" | "geo";
|
|
334
358
|
interface TDbIndexField {
|
|
335
359
|
name: string;
|
|
336
360
|
sort: "asc" | "desc";
|
|
@@ -436,6 +460,19 @@ interface TDbFieldMeta {
|
|
|
436
460
|
* Undefined for non-FK fields or when the target cannot be resolved.
|
|
437
461
|
*/
|
|
438
462
|
fkTargetField?: TDbFieldMeta;
|
|
463
|
+
/**
|
|
464
|
+
* `@db.encrypted` — the value is AES-256-GCM encrypted by the core layer
|
|
465
|
+
* before reaching the adapter. Adapters must map the column to an unbounded
|
|
466
|
+
* text type and veto filtering/sorting (`canFilterField`/`canSortField`).
|
|
467
|
+
* The descriptor's `designType` is forced to `'string'` (ciphertext envelope);
|
|
468
|
+
* the declared type stays available via `type` for validation.
|
|
469
|
+
*/
|
|
470
|
+
encrypted?: boolean;
|
|
471
|
+
/**
|
|
472
|
+
* The field's declared type is the `db.geoPoint` primitive (`[lng, lat]` tuple).
|
|
473
|
+
* Adapters map this to their native geo storage (e.g. MongoDB GeoJSON Point).
|
|
474
|
+
*/
|
|
475
|
+
isGeoPoint?: boolean;
|
|
439
476
|
}
|
|
440
477
|
interface TValueFormatterPair {
|
|
441
478
|
/** Converts a JS value to storage representation (write + filter paths). */
|
|
@@ -906,6 +943,14 @@ declare abstract class BaseDbAdapter {
|
|
|
906
943
|
dropIndex(name: string): Promise<void>;
|
|
907
944
|
prefix?: string;
|
|
908
945
|
shouldSkipType?(type: TDbIndex["type"]): boolean;
|
|
946
|
+
/**
|
|
947
|
+
* Index types declared on the model but not supported by this adapter —
|
|
948
|
+
* warns and skips (models stay portable; sync never errors on these).
|
|
949
|
+
*/
|
|
950
|
+
warnUnsupportedTypes?: {
|
|
951
|
+
adapter: string;
|
|
952
|
+
types: ReadonlyArray<TDbIndex["type"]>;
|
|
953
|
+
};
|
|
909
954
|
}): Promise<void>;
|
|
910
955
|
/**
|
|
911
956
|
* Returns available search indexes for this adapter.
|
|
@@ -960,6 +1005,32 @@ declare abstract class BaseDbAdapter {
|
|
|
960
1005
|
data: Array<Record<string, unknown>>;
|
|
961
1006
|
count: number;
|
|
962
1007
|
}>;
|
|
1008
|
+
/**
|
|
1009
|
+
* Whether this adapter supports geospatial search (`geoSearch()` and the
|
|
1010
|
+
* `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
|
|
1011
|
+
*/
|
|
1012
|
+
isGeoSearchable(): boolean;
|
|
1013
|
+
/**
|
|
1014
|
+
* Distance-ranked geospatial search. Results sorted by distance ascending;
|
|
1015
|
+
* each row carries a `$distance` field (meters from the query point).
|
|
1016
|
+
* `$maxDistance` / `$minDistance` (meters) ride in `query.controls`.
|
|
1017
|
+
*
|
|
1018
|
+
* @param point - `[lng, lat]` query point (GeoJSON coordinate order).
|
|
1019
|
+
* @param query - Filter, select, skip/limit, $maxDistance/$minDistance.
|
|
1020
|
+
* @param indexName - Optional geo index to target (multi-geo documents).
|
|
1021
|
+
*/
|
|
1022
|
+
geoSearch(_point: [number, number], _query: DbQuery, _indexName?: string): Promise<Array<Record<string, unknown>>>;
|
|
1023
|
+
/**
|
|
1024
|
+
* Distance-ranked geospatial search with count (for paginated results).
|
|
1025
|
+
*
|
|
1026
|
+
* @param point - `[lng, lat]` query point (GeoJSON coordinate order).
|
|
1027
|
+
* @param query - Filter, select, skip/limit, $maxDistance/$minDistance.
|
|
1028
|
+
* @param indexName - Optional geo index to target (multi-geo documents).
|
|
1029
|
+
*/
|
|
1030
|
+
geoSearchWithCount(_point: [number, number], _query: DbQuery, _indexName?: string): Promise<{
|
|
1031
|
+
data: Array<Record<string, unknown>>;
|
|
1032
|
+
count: number;
|
|
1033
|
+
}>;
|
|
963
1034
|
/**
|
|
964
1035
|
* Fetches records and total count in one call.
|
|
965
1036
|
* Default: two parallel calls. Adapters may override for single-query optimization.
|
|
@@ -1207,6 +1278,66 @@ declare class DocumentFieldMapper extends FieldMappingStrategy {
|
|
|
1207
1278
|
translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
1208
1279
|
}
|
|
1209
1280
|
//#endregion
|
|
1281
|
+
//#region src/encryption.d.ts
|
|
1282
|
+
/**
|
|
1283
|
+
* Configuration for field-level encryption at rest (`@db.encrypted`).
|
|
1284
|
+
* Passed to `DbSpace` via the options bag.
|
|
1285
|
+
*/
|
|
1286
|
+
interface TDbEncryptionOptions {
|
|
1287
|
+
/** Key used for all new writes. */
|
|
1288
|
+
defaultKeyId: string;
|
|
1289
|
+
/**
|
|
1290
|
+
* Key registry: keyId → 32-byte key (Buffer, or base64/hex/utf8 string).
|
|
1291
|
+
* Decryption looks keys up by the keyId recorded in each value's envelope,
|
|
1292
|
+
* so old keys stay in the registry for as long as data encrypted with them exists.
|
|
1293
|
+
*/
|
|
1294
|
+
keys?: Record<string, string | Buffer>;
|
|
1295
|
+
/**
|
|
1296
|
+
* Alternative/supplement to `keys`: async resolver (KMS, Vault, env indirection).
|
|
1297
|
+
* Called once per keyId, result cached for the process lifetime.
|
|
1298
|
+
*/
|
|
1299
|
+
resolveKey?: (keyId: string) => Promise<string | Buffer> | string | Buffer;
|
|
1300
|
+
/**
|
|
1301
|
+
* What to do when a stored value is NOT a valid envelope (pre-existing
|
|
1302
|
+
* plaintext rows, e.g. when @db.encrypted is added to a live column).
|
|
1303
|
+
* - 'error' (default): fail the read with DbError("ENC_NOT_ENCRYPTED")
|
|
1304
|
+
* - 'passthrough': return the raw value as-is (migration window mode);
|
|
1305
|
+
* the value gets encrypted on its next write.
|
|
1306
|
+
*/
|
|
1307
|
+
onUnencrypted?: "error" | "passthrough";
|
|
1308
|
+
}
|
|
1309
|
+
/** Decryption context — carried into error messages (never the key itself). */
|
|
1310
|
+
interface TDecryptContext {
|
|
1311
|
+
table?: string;
|
|
1312
|
+
field?: string;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* AES-256-GCM envelope encryption service for `@db.encrypted` fields.
|
|
1316
|
+
*
|
|
1317
|
+
* Owned by `DbSpace` and shared across all tables in the space. Values are
|
|
1318
|
+
* `JSON.stringify`'d before encryption (type-exact round-trips) and stored as
|
|
1319
|
+
* a single ASCII envelope string: `aes1$<keyId>$<iv>$<tag>$<ciphertext>`.
|
|
1320
|
+
*
|
|
1321
|
+
* Key material is validated eagerly at construction (`ENC_KEY_INVALID`);
|
|
1322
|
+
* `resolveKey` lookups are cached per keyId for the process lifetime.
|
|
1323
|
+
*/
|
|
1324
|
+
declare class DbEncryption {
|
|
1325
|
+
readonly defaultKeyId: string;
|
|
1326
|
+
readonly onUnencrypted: "error" | "passthrough";
|
|
1327
|
+
private readonly _keys;
|
|
1328
|
+
private readonly _resolveKey?;
|
|
1329
|
+
private readonly _resolved;
|
|
1330
|
+
constructor(options: TDbEncryptionOptions);
|
|
1331
|
+
/** True when `value` looks like an encryption envelope produced by this service. */
|
|
1332
|
+
isEnvelope(value: unknown): value is string;
|
|
1333
|
+
/** Encrypts a JSON-serializable value into an envelope string using the default key. */
|
|
1334
|
+
encrypt(value: unknown): Promise<string>;
|
|
1335
|
+
/** Decrypts an envelope string back into its plaintext value. */
|
|
1336
|
+
decrypt(envelope: string, ctx?: TDecryptContext): Promise<unknown>;
|
|
1337
|
+
private _decryptFailed;
|
|
1338
|
+
private _getKey;
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1210
1341
|
//#region src/table/db-readable.d.ts
|
|
1211
1342
|
/**
|
|
1212
1343
|
* Extracts nav prop names from a query's `$with` array.
|
|
@@ -1266,8 +1397,16 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1266
1397
|
/** Strategy for mapping between logical field shapes and physical storage. */
|
|
1267
1398
|
protected readonly _fieldMapper: FieldMappingStrategy;
|
|
1268
1399
|
protected _writeTableResolver?: TWriteTableResolver;
|
|
1400
|
+
/** Encryption service for `@db.encrypted` fields — set by `DbSpace` from its options. */
|
|
1401
|
+
protected _encryption?: DbEncryption;
|
|
1269
1402
|
private _metaIdPhysical;
|
|
1270
1403
|
constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver | undefined);
|
|
1404
|
+
/**
|
|
1405
|
+
* Sets the encryption service used for `@db.encrypted` fields.
|
|
1406
|
+
* Called by `DbSpace` after table/view creation when the space was
|
|
1407
|
+
* configured with an `encryption` options block.
|
|
1408
|
+
*/
|
|
1409
|
+
setEncryption(encryption: DbEncryption | undefined): void;
|
|
1271
1410
|
/** Ensures metadata is built. Called before any metadata access. */
|
|
1272
1411
|
protected _ensureBuilt(): void;
|
|
1273
1412
|
/**
|
|
@@ -1277,6 +1416,28 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1277
1416
|
*/
|
|
1278
1417
|
getMetadata(): TableMetadata;
|
|
1279
1418
|
protected _ensureSearchable(): void;
|
|
1419
|
+
/** Engine-agnostic query-time guards (encrypted-field refs, $geoWithin shape). */
|
|
1420
|
+
protected _guardQuery(query: Uniquery | undefined): void;
|
|
1421
|
+
private _encryptedPathsCache?;
|
|
1422
|
+
/** Pre-split `encryptedFields` paths — computed once, reused on every read/write. */
|
|
1423
|
+
protected get _encryptedPaths(): Array<{
|
|
1424
|
+
path: string;
|
|
1425
|
+
segments: string[];
|
|
1426
|
+
leaf: string;
|
|
1427
|
+
}>;
|
|
1428
|
+
/**
|
|
1429
|
+
* Walks all but the last of `segments` down from `root`, returning the
|
|
1430
|
+
* object holding the leaf — or `undefined` when the path is unreachable
|
|
1431
|
+
* (a missing, non-object, or array step). With `cloneParents`, every
|
|
1432
|
+
* traversed object is shallow-cloned and re-linked so caller-shared
|
|
1433
|
+
* nested objects are never mutated.
|
|
1434
|
+
*/
|
|
1435
|
+
protected _walkToLeafParent(root: Record<string, unknown>, segments: string[], cloneParents: boolean): Record<string, unknown> | undefined;
|
|
1436
|
+
/**
|
|
1437
|
+
* Decrypts `@db.encrypted` fields on reconstructed rows (in place).
|
|
1438
|
+
* Non-envelope stored values follow the configured `onUnencrypted` policy.
|
|
1439
|
+
*/
|
|
1440
|
+
protected _decryptRows(rows: Array<Record<string, unknown>>): Promise<void>;
|
|
1280
1441
|
/** Whether this readable is a view (overridden in AtscriptDbView). */
|
|
1281
1442
|
get isView(): boolean;
|
|
1282
1443
|
/** Returns the underlying adapter with its concrete type preserved. */
|
|
@@ -1438,6 +1599,38 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1438
1599
|
}>;
|
|
1439
1600
|
/** Resolves overloaded vector search arguments into canonical form. */
|
|
1440
1601
|
private _resolveVectorSearchArgs;
|
|
1602
|
+
/** Whether the underlying adapter supports geospatial search. */
|
|
1603
|
+
isGeoSearchable(): boolean;
|
|
1604
|
+
/**
|
|
1605
|
+
* Distance-ranked geospatial search (mirrors {@link vectorSearch}).
|
|
1606
|
+
* Results are sorted by distance ascending; each row carries a computed
|
|
1607
|
+
* `$distance` field (meters from the query point). `$maxDistance` /
|
|
1608
|
+
* `$minDistance` (meters) ride in `query.controls`; user `$sort` is rejected.
|
|
1609
|
+
*
|
|
1610
|
+
* Overloads:
|
|
1611
|
+
* - `geoSearch(point, query?)` — uses the table's only geo index
|
|
1612
|
+
* - `geoSearch(indexName, point, query?)` — targets a specific geo index
|
|
1613
|
+
*/
|
|
1614
|
+
geoSearch<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<Array<DbResponse<DataType, NavType, Q> & {
|
|
1615
|
+
$distance: number;
|
|
1616
|
+
}>>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Distance-ranked geospatial search with count for paginated results.
|
|
1619
|
+
*
|
|
1620
|
+
* Overloads:
|
|
1621
|
+
* - `geoSearchWithCount(point, query?)` — uses the table's only geo index
|
|
1622
|
+
* - `geoSearchWithCount(indexName, point, query?)` — targets a specific geo index
|
|
1623
|
+
*/
|
|
1624
|
+
geoSearchWithCount<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<{
|
|
1625
|
+
data: Array<DbResponse<DataType, NavType, Q> & {
|
|
1626
|
+
$distance: number;
|
|
1627
|
+
}>;
|
|
1628
|
+
count: number;
|
|
1629
|
+
}>;
|
|
1630
|
+
/** Resolves overloaded geo search arguments into canonical form. */
|
|
1631
|
+
private _resolveGeoSearchArgs;
|
|
1632
|
+
/** Shared geoSearch validation + query translation. */
|
|
1633
|
+
private _prepareGeoSearch;
|
|
1441
1634
|
/**
|
|
1442
1635
|
* Finds a single record by any type-compatible identifier — primary key
|
|
1443
1636
|
* or single-field unique index.
|
|
@@ -1497,4 +1690,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1497
1690
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
1498
1691
|
}
|
|
1499
1692
|
//#endregion
|
|
1500
|
-
export {
|
|
1693
|
+
export { TIdentification as $, TDbActionLevel as A, TDbIndexType as B, TCascadeResolver as C, TCrudPermissions as D, TCrudOp as E, TDbDeleteResult as F, TDbStorageType as G, TDbInsertResult as H, TDbFieldMeta as I, TExistingTableOption as J, TDbUpdateResult as K, TDbForeignKey as L, TDbCollation as M, TDbDefaultFn as N, TDbActionInfo as O, TDbDefaultValue as P, TIdDescriptor as Q, TDbIndex as R, PrimaryKeyOf$1 as S, TColumnDiff as T, TDbReferentialAction as U, TDbInsertManyResult as V, TDbRelation as W, TFkLookupResolver as X, TFieldMeta as Y, TFkLookupTarget as Z, FieldOpsFor as _, TGenericLogger as _t, TDbEncryptionOptions as a, TTableOptionDiff as at, NavPropsOf$1 as b, BaseDbAdapter as c, TWriteTableResolver as ct, AggregateFn as d, UniqueryControls$1 as dt, TMetaResponse as et, AggregateQuery$1 as f, WithRelation$1 as ft, DbQuery as g, NoopLogger as gt, DbControls as h, isGeoPointType as ht, DbEncryption as i, TSyncColumnResult as it, TDbActionProcessor as j, TDbActionIntent as k, AggregateControls as l, TypedWithRelation as lt, AtscriptDbWritable as m, isGeoIndexableType as mt, DbResponse as n, TRelationInfo as nt, DocumentFieldMapper as o, TTableResolver as ot, AggregateResult as p, TableMetadata as pt, TExistingColumn as q, resolveDesignType as r, TSearchIndexInfo as rt, FieldMappingStrategy as s, TValueFormatterPair as st, AtscriptDbReadable as t, TMetadataOverrides as tt, AggregateExpr$1 as u, Uniquery$1 as ut, FilterExpr$1 as v, UniquSelect as vt, TCascadeTarget as w, OwnPropsOf$1 as x, FlatOf$1 as y, TDbIndexField as z };
|
|
@@ -55,6 +55,14 @@ interface TGenericLogger {
|
|
|
55
55
|
declare const NoopLogger: TGenericLogger;
|
|
56
56
|
//#endregion
|
|
57
57
|
//#region src/table/table-metadata.d.ts
|
|
58
|
+
/** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
|
|
59
|
+
declare function isGeoPointType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Returns true if the annotated type is acceptable for `@db.index.geo`:
|
|
62
|
+
* the `db.geoPoint` primitive or a structurally identical `number[]`
|
|
63
|
+
* (excluding `db.vector`, which is semantically an embedding).
|
|
64
|
+
*/
|
|
65
|
+
declare function isGeoIndexableType(fieldType: TAtscriptAnnotatedType): boolean;
|
|
58
66
|
/**
|
|
59
67
|
* Computed metadata for a database table or view.
|
|
60
68
|
*
|
|
@@ -87,6 +95,8 @@ declare class TableMetadata {
|
|
|
87
95
|
versionField?: string;
|
|
88
96
|
/** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
|
|
89
97
|
quantityRefByField: Map<string, string>;
|
|
98
|
+
/** Logical paths annotated with `@db.encrypted` — stored as one opaque ciphertext column. */
|
|
99
|
+
encryptedFields: Set<string>;
|
|
90
100
|
pathToPhysical: Map<string, string>;
|
|
91
101
|
physicalToPath: Map<string, string>;
|
|
92
102
|
flattenedParents: Set<string>;
|
|
@@ -140,6 +150,14 @@ declare class TableMetadata {
|
|
|
140
150
|
* Scans `@db.*` and `@meta.id` annotations on a field during flattening.
|
|
141
151
|
*/
|
|
142
152
|
private _scanGenericAnnotations;
|
|
153
|
+
/**
|
|
154
|
+
* Build-time diagnostics for `@db.encrypted` (§6 of the field-encryption
|
|
155
|
+
* spec). Mirrors the compile-time AnnotationSpec validation so models built
|
|
156
|
+
* from pre-compiled types still fail fast.
|
|
157
|
+
*/
|
|
158
|
+
private _validateEncryptedField;
|
|
159
|
+
/** Build-time diagnostics for `@db.index.geo` (§3 of the geo-index spec). */
|
|
160
|
+
private _validateGeoIndexField;
|
|
143
161
|
private _addIndexField;
|
|
144
162
|
/**
|
|
145
163
|
* Classifies each field as column, flattened, json, or parent-object.
|
|
@@ -216,9 +234,13 @@ interface TRelationInfo {
|
|
|
216
234
|
interface TFieldMeta {
|
|
217
235
|
sortable: boolean;
|
|
218
236
|
filterable: boolean;
|
|
237
|
+
/** Present (true) when the field is `@db.encrypted` — stored as ciphertext at rest. */
|
|
238
|
+
encrypted?: boolean;
|
|
239
|
+
/** Present (true) when the field carries a `@db.index.geo` geospatial index. */
|
|
240
|
+
geo?: boolean;
|
|
219
241
|
}
|
|
220
242
|
/** Built-in CRUD operation names; map 1:1 to public method names. */
|
|
221
|
-
type TCrudOp = "query" | "pages" | "one" | "insert" | "update" | "replace" | "remove";
|
|
243
|
+
type TCrudOp = "query" | "pages" | "one" | "geo" | "insert" | "update" | "replace" | "remove";
|
|
222
244
|
/**
|
|
223
245
|
* CRUD permissions advertised in `/meta`. Key absent → operation is denied or
|
|
224
246
|
* not exposed. Key present → operation is allowed; the `string[]` value is the
|
|
@@ -230,6 +252,8 @@ type TCrudPermissions = Partial<Record<TCrudOp, string[]>>;
|
|
|
230
252
|
interface TMetaResponse {
|
|
231
253
|
searchable: boolean;
|
|
232
254
|
vectorSearchable: boolean;
|
|
255
|
+
/** Whether the adapter supports `geoSearch()` AND the table declares a geo index. */
|
|
256
|
+
geoSearchable?: boolean;
|
|
233
257
|
searchIndexes: TSearchIndexInfo[];
|
|
234
258
|
primaryKeys: string[];
|
|
235
259
|
preferredId: string[];
|
|
@@ -330,7 +354,7 @@ interface TDbUpdateResult {
|
|
|
330
354
|
interface TDbDeleteResult {
|
|
331
355
|
deletedCount: number;
|
|
332
356
|
}
|
|
333
|
-
type TDbIndexType = "plain" | "unique" | "fulltext";
|
|
357
|
+
type TDbIndexType = "plain" | "unique" | "fulltext" | "geo";
|
|
334
358
|
interface TDbIndexField {
|
|
335
359
|
name: string;
|
|
336
360
|
sort: "asc" | "desc";
|
|
@@ -436,6 +460,19 @@ interface TDbFieldMeta {
|
|
|
436
460
|
* Undefined for non-FK fields or when the target cannot be resolved.
|
|
437
461
|
*/
|
|
438
462
|
fkTargetField?: TDbFieldMeta;
|
|
463
|
+
/**
|
|
464
|
+
* `@db.encrypted` — the value is AES-256-GCM encrypted by the core layer
|
|
465
|
+
* before reaching the adapter. Adapters must map the column to an unbounded
|
|
466
|
+
* text type and veto filtering/sorting (`canFilterField`/`canSortField`).
|
|
467
|
+
* The descriptor's `designType` is forced to `'string'` (ciphertext envelope);
|
|
468
|
+
* the declared type stays available via `type` for validation.
|
|
469
|
+
*/
|
|
470
|
+
encrypted?: boolean;
|
|
471
|
+
/**
|
|
472
|
+
* The field's declared type is the `db.geoPoint` primitive (`[lng, lat]` tuple).
|
|
473
|
+
* Adapters map this to their native geo storage (e.g. MongoDB GeoJSON Point).
|
|
474
|
+
*/
|
|
475
|
+
isGeoPoint?: boolean;
|
|
439
476
|
}
|
|
440
477
|
interface TValueFormatterPair {
|
|
441
478
|
/** Converts a JS value to storage representation (write + filter paths). */
|
|
@@ -906,6 +943,14 @@ declare abstract class BaseDbAdapter {
|
|
|
906
943
|
dropIndex(name: string): Promise<void>;
|
|
907
944
|
prefix?: string;
|
|
908
945
|
shouldSkipType?(type: TDbIndex["type"]): boolean;
|
|
946
|
+
/**
|
|
947
|
+
* Index types declared on the model but not supported by this adapter —
|
|
948
|
+
* warns and skips (models stay portable; sync never errors on these).
|
|
949
|
+
*/
|
|
950
|
+
warnUnsupportedTypes?: {
|
|
951
|
+
adapter: string;
|
|
952
|
+
types: ReadonlyArray<TDbIndex["type"]>;
|
|
953
|
+
};
|
|
909
954
|
}): Promise<void>;
|
|
910
955
|
/**
|
|
911
956
|
* Returns available search indexes for this adapter.
|
|
@@ -960,6 +1005,32 @@ declare abstract class BaseDbAdapter {
|
|
|
960
1005
|
data: Array<Record<string, unknown>>;
|
|
961
1006
|
count: number;
|
|
962
1007
|
}>;
|
|
1008
|
+
/**
|
|
1009
|
+
* Whether this adapter supports geospatial search (`geoSearch()` and the
|
|
1010
|
+
* `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
|
|
1011
|
+
*/
|
|
1012
|
+
isGeoSearchable(): boolean;
|
|
1013
|
+
/**
|
|
1014
|
+
* Distance-ranked geospatial search. Results sorted by distance ascending;
|
|
1015
|
+
* each row carries a `$distance` field (meters from the query point).
|
|
1016
|
+
* `$maxDistance` / `$minDistance` (meters) ride in `query.controls`.
|
|
1017
|
+
*
|
|
1018
|
+
* @param point - `[lng, lat]` query point (GeoJSON coordinate order).
|
|
1019
|
+
* @param query - Filter, select, skip/limit, $maxDistance/$minDistance.
|
|
1020
|
+
* @param indexName - Optional geo index to target (multi-geo documents).
|
|
1021
|
+
*/
|
|
1022
|
+
geoSearch(_point: [number, number], _query: DbQuery, _indexName?: string): Promise<Array<Record<string, unknown>>>;
|
|
1023
|
+
/**
|
|
1024
|
+
* Distance-ranked geospatial search with count (for paginated results).
|
|
1025
|
+
*
|
|
1026
|
+
* @param point - `[lng, lat]` query point (GeoJSON coordinate order).
|
|
1027
|
+
* @param query - Filter, select, skip/limit, $maxDistance/$minDistance.
|
|
1028
|
+
* @param indexName - Optional geo index to target (multi-geo documents).
|
|
1029
|
+
*/
|
|
1030
|
+
geoSearchWithCount(_point: [number, number], _query: DbQuery, _indexName?: string): Promise<{
|
|
1031
|
+
data: Array<Record<string, unknown>>;
|
|
1032
|
+
count: number;
|
|
1033
|
+
}>;
|
|
963
1034
|
/**
|
|
964
1035
|
* Fetches records and total count in one call.
|
|
965
1036
|
* Default: two parallel calls. Adapters may override for single-query optimization.
|
|
@@ -1207,6 +1278,66 @@ declare class DocumentFieldMapper extends FieldMappingStrategy {
|
|
|
1207
1278
|
translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
1208
1279
|
}
|
|
1209
1280
|
//#endregion
|
|
1281
|
+
//#region src/encryption.d.ts
|
|
1282
|
+
/**
|
|
1283
|
+
* Configuration for field-level encryption at rest (`@db.encrypted`).
|
|
1284
|
+
* Passed to `DbSpace` via the options bag.
|
|
1285
|
+
*/
|
|
1286
|
+
interface TDbEncryptionOptions {
|
|
1287
|
+
/** Key used for all new writes. */
|
|
1288
|
+
defaultKeyId: string;
|
|
1289
|
+
/**
|
|
1290
|
+
* Key registry: keyId → 32-byte key (Buffer, or base64/hex/utf8 string).
|
|
1291
|
+
* Decryption looks keys up by the keyId recorded in each value's envelope,
|
|
1292
|
+
* so old keys stay in the registry for as long as data encrypted with them exists.
|
|
1293
|
+
*/
|
|
1294
|
+
keys?: Record<string, string | Buffer>;
|
|
1295
|
+
/**
|
|
1296
|
+
* Alternative/supplement to `keys`: async resolver (KMS, Vault, env indirection).
|
|
1297
|
+
* Called once per keyId, result cached for the process lifetime.
|
|
1298
|
+
*/
|
|
1299
|
+
resolveKey?: (keyId: string) => Promise<string | Buffer> | string | Buffer;
|
|
1300
|
+
/**
|
|
1301
|
+
* What to do when a stored value is NOT a valid envelope (pre-existing
|
|
1302
|
+
* plaintext rows, e.g. when @db.encrypted is added to a live column).
|
|
1303
|
+
* - 'error' (default): fail the read with DbError("ENC_NOT_ENCRYPTED")
|
|
1304
|
+
* - 'passthrough': return the raw value as-is (migration window mode);
|
|
1305
|
+
* the value gets encrypted on its next write.
|
|
1306
|
+
*/
|
|
1307
|
+
onUnencrypted?: "error" | "passthrough";
|
|
1308
|
+
}
|
|
1309
|
+
/** Decryption context — carried into error messages (never the key itself). */
|
|
1310
|
+
interface TDecryptContext {
|
|
1311
|
+
table?: string;
|
|
1312
|
+
field?: string;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* AES-256-GCM envelope encryption service for `@db.encrypted` fields.
|
|
1316
|
+
*
|
|
1317
|
+
* Owned by `DbSpace` and shared across all tables in the space. Values are
|
|
1318
|
+
* `JSON.stringify`'d before encryption (type-exact round-trips) and stored as
|
|
1319
|
+
* a single ASCII envelope string: `aes1$<keyId>$<iv>$<tag>$<ciphertext>`.
|
|
1320
|
+
*
|
|
1321
|
+
* Key material is validated eagerly at construction (`ENC_KEY_INVALID`);
|
|
1322
|
+
* `resolveKey` lookups are cached per keyId for the process lifetime.
|
|
1323
|
+
*/
|
|
1324
|
+
declare class DbEncryption {
|
|
1325
|
+
readonly defaultKeyId: string;
|
|
1326
|
+
readonly onUnencrypted: "error" | "passthrough";
|
|
1327
|
+
private readonly _keys;
|
|
1328
|
+
private readonly _resolveKey?;
|
|
1329
|
+
private readonly _resolved;
|
|
1330
|
+
constructor(options: TDbEncryptionOptions);
|
|
1331
|
+
/** True when `value` looks like an encryption envelope produced by this service. */
|
|
1332
|
+
isEnvelope(value: unknown): value is string;
|
|
1333
|
+
/** Encrypts a JSON-serializable value into an envelope string using the default key. */
|
|
1334
|
+
encrypt(value: unknown): Promise<string>;
|
|
1335
|
+
/** Decrypts an envelope string back into its plaintext value. */
|
|
1336
|
+
decrypt(envelope: string, ctx?: TDecryptContext): Promise<unknown>;
|
|
1337
|
+
private _decryptFailed;
|
|
1338
|
+
private _getKey;
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1210
1341
|
//#region src/table/db-readable.d.ts
|
|
1211
1342
|
/**
|
|
1212
1343
|
* Extracts nav prop names from a query's `$with` array.
|
|
@@ -1266,8 +1397,16 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1266
1397
|
/** Strategy for mapping between logical field shapes and physical storage. */
|
|
1267
1398
|
protected readonly _fieldMapper: FieldMappingStrategy;
|
|
1268
1399
|
protected _writeTableResolver?: TWriteTableResolver;
|
|
1400
|
+
/** Encryption service for `@db.encrypted` fields — set by `DbSpace` from its options. */
|
|
1401
|
+
protected _encryption?: DbEncryption;
|
|
1269
1402
|
private _metaIdPhysical;
|
|
1270
1403
|
constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver | undefined);
|
|
1404
|
+
/**
|
|
1405
|
+
* Sets the encryption service used for `@db.encrypted` fields.
|
|
1406
|
+
* Called by `DbSpace` after table/view creation when the space was
|
|
1407
|
+
* configured with an `encryption` options block.
|
|
1408
|
+
*/
|
|
1409
|
+
setEncryption(encryption: DbEncryption | undefined): void;
|
|
1271
1410
|
/** Ensures metadata is built. Called before any metadata access. */
|
|
1272
1411
|
protected _ensureBuilt(): void;
|
|
1273
1412
|
/**
|
|
@@ -1277,6 +1416,28 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1277
1416
|
*/
|
|
1278
1417
|
getMetadata(): TableMetadata;
|
|
1279
1418
|
protected _ensureSearchable(): void;
|
|
1419
|
+
/** Engine-agnostic query-time guards (encrypted-field refs, $geoWithin shape). */
|
|
1420
|
+
protected _guardQuery(query: Uniquery | undefined): void;
|
|
1421
|
+
private _encryptedPathsCache?;
|
|
1422
|
+
/** Pre-split `encryptedFields` paths — computed once, reused on every read/write. */
|
|
1423
|
+
protected get _encryptedPaths(): Array<{
|
|
1424
|
+
path: string;
|
|
1425
|
+
segments: string[];
|
|
1426
|
+
leaf: string;
|
|
1427
|
+
}>;
|
|
1428
|
+
/**
|
|
1429
|
+
* Walks all but the last of `segments` down from `root`, returning the
|
|
1430
|
+
* object holding the leaf — or `undefined` when the path is unreachable
|
|
1431
|
+
* (a missing, non-object, or array step). With `cloneParents`, every
|
|
1432
|
+
* traversed object is shallow-cloned and re-linked so caller-shared
|
|
1433
|
+
* nested objects are never mutated.
|
|
1434
|
+
*/
|
|
1435
|
+
protected _walkToLeafParent(root: Record<string, unknown>, segments: string[], cloneParents: boolean): Record<string, unknown> | undefined;
|
|
1436
|
+
/**
|
|
1437
|
+
* Decrypts `@db.encrypted` fields on reconstructed rows (in place).
|
|
1438
|
+
* Non-envelope stored values follow the configured `onUnencrypted` policy.
|
|
1439
|
+
*/
|
|
1440
|
+
protected _decryptRows(rows: Array<Record<string, unknown>>): Promise<void>;
|
|
1280
1441
|
/** Whether this readable is a view (overridden in AtscriptDbView). */
|
|
1281
1442
|
get isView(): boolean;
|
|
1282
1443
|
/** Returns the underlying adapter with its concrete type preserved. */
|
|
@@ -1438,6 +1599,38 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1438
1599
|
}>;
|
|
1439
1600
|
/** Resolves overloaded vector search arguments into canonical form. */
|
|
1440
1601
|
private _resolveVectorSearchArgs;
|
|
1602
|
+
/** Whether the underlying adapter supports geospatial search. */
|
|
1603
|
+
isGeoSearchable(): boolean;
|
|
1604
|
+
/**
|
|
1605
|
+
* Distance-ranked geospatial search (mirrors {@link vectorSearch}).
|
|
1606
|
+
* Results are sorted by distance ascending; each row carries a computed
|
|
1607
|
+
* `$distance` field (meters from the query point). `$maxDistance` /
|
|
1608
|
+
* `$minDistance` (meters) ride in `query.controls`; user `$sort` is rejected.
|
|
1609
|
+
*
|
|
1610
|
+
* Overloads:
|
|
1611
|
+
* - `geoSearch(point, query?)` — uses the table's only geo index
|
|
1612
|
+
* - `geoSearch(indexName, point, query?)` — targets a specific geo index
|
|
1613
|
+
*/
|
|
1614
|
+
geoSearch<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<Array<DbResponse<DataType, NavType, Q> & {
|
|
1615
|
+
$distance: number;
|
|
1616
|
+
}>>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Distance-ranked geospatial search with count for paginated results.
|
|
1619
|
+
*
|
|
1620
|
+
* Overloads:
|
|
1621
|
+
* - `geoSearchWithCount(point, query?)` — uses the table's only geo index
|
|
1622
|
+
* - `geoSearchWithCount(indexName, point, query?)` — targets a specific geo index
|
|
1623
|
+
*/
|
|
1624
|
+
geoSearchWithCount<Q extends Uniquery<OwnProps, NavType>>(pointOrIndex: [number, number] | string, maybePointOrQuery?: [number, number] | Q, maybeQuery?: Q): Promise<{
|
|
1625
|
+
data: Array<DbResponse<DataType, NavType, Q> & {
|
|
1626
|
+
$distance: number;
|
|
1627
|
+
}>;
|
|
1628
|
+
count: number;
|
|
1629
|
+
}>;
|
|
1630
|
+
/** Resolves overloaded geo search arguments into canonical form. */
|
|
1631
|
+
private _resolveGeoSearchArgs;
|
|
1632
|
+
/** Shared geoSearch validation + query translation. */
|
|
1633
|
+
private _prepareGeoSearch;
|
|
1441
1634
|
/**
|
|
1442
1635
|
* Finds a single record by any type-compatible identifier — primary key
|
|
1443
1636
|
* or single-field unique index.
|
|
@@ -1497,4 +1690,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
|
|
|
1497
1690
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
1498
1691
|
}
|
|
1499
1692
|
//#endregion
|
|
1500
|
-
export {
|
|
1693
|
+
export { TIdentification as $, TDbActionLevel as A, TDbIndexType as B, TCascadeResolver as C, TCrudPermissions as D, TCrudOp as E, TDbDeleteResult as F, TDbStorageType as G, TDbInsertResult as H, TDbFieldMeta as I, TExistingTableOption as J, TDbUpdateResult as K, TDbForeignKey as L, TDbCollation as M, TDbDefaultFn as N, TDbActionInfo as O, TDbDefaultValue as P, TIdDescriptor as Q, TDbIndex as R, PrimaryKeyOf$1 as S, TColumnDiff as T, TDbReferentialAction as U, TDbInsertManyResult as V, TDbRelation as W, TFkLookupResolver as X, TFieldMeta as Y, TFkLookupTarget as Z, FieldOpsFor as _, TGenericLogger as _t, TDbEncryptionOptions as a, TTableOptionDiff as at, NavPropsOf$1 as b, BaseDbAdapter as c, TWriteTableResolver as ct, AggregateFn as d, UniqueryControls$1 as dt, TMetaResponse as et, AggregateQuery$1 as f, WithRelation$1 as ft, DbQuery as g, NoopLogger as gt, DbControls as h, isGeoPointType as ht, DbEncryption as i, TSyncColumnResult as it, TDbActionProcessor as j, TDbActionIntent as k, AggregateControls as l, TypedWithRelation as lt, AtscriptDbWritable as m, isGeoIndexableType as mt, DbResponse as n, TRelationInfo as nt, DocumentFieldMapper as o, TTableResolver as ot, AggregateResult as p, TableMetadata as pt, TExistingColumn as q, resolveDesignType as r, TSearchIndexInfo as rt, FieldMappingStrategy as s, TValueFormatterPair as st, AtscriptDbReadable as t, TMetadataOverrides as tt, AggregateExpr$1 as u, Uniquery$1 as ut, FilterExpr$1 as v, UniquSelect as vt, TCascadeTarget as w, OwnPropsOf$1 as x, FlatOf$1 as y, TDbIndexField as z };
|