@atscript/db 0.1.102 → 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.
@@ -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 { TMetadataOverrides as $, TDbCollation as A, TDbInsertResult as B, TColumnDiff as C, TDbActionIntent as D, TDbActionInfo as E, TDbForeignKey as F, TExistingColumn as G, TDbRelation as H, TDbIndex as I, TFkLookupResolver as J, TExistingTableOption as K, TDbIndexField as L, TDbDefaultValue as M, TDbDeleteResult as N, TDbActionLevel as O, TDbFieldMeta as P, TMetaResponse as Q, TDbIndexType as R, TCascadeTarget as S, TCrudPermissions as T, TDbStorageType as U, TDbReferentialAction as V, TDbUpdateResult as W, TIdDescriptor as X, TFkLookupTarget as Y, TIdentification as Z, FlatOf$1 as _, FieldMappingStrategy as a, TValueFormatterPair as at, PrimaryKeyOf$1 as b, AggregateExpr$1 as c, Uniquery$1 as ct, AggregateResult as d, TableMetadata as dt, TRelationInfo as et, AtscriptDbWritable as f, NoopLogger as ft, FilterExpr$1 as g, FieldOpsFor as h, DocumentFieldMapper as i, TTableResolver as it, TDbDefaultFn as j, TDbActionProcessor as k, AggregateFn as l, UniqueryControls$1 as lt, DbQuery as m, UniquSelect as mt, DbResponse as n, TSyncColumnResult as nt, BaseDbAdapter as o, TWriteTableResolver as ot, DbControls as p, TGenericLogger as pt, TFieldMeta as q, resolveDesignType as r, TTableOptionDiff as rt, AggregateControls as s, TypedWithRelation as st, AtscriptDbReadable as t, TSearchIndexInfo as tt, AggregateQuery$1 as u, WithRelation$1 as ut, NavPropsOf$1 as v, TCrudOp as w, TCascadeResolver as x, OwnPropsOf$1 as y, TDbInsertManyResult as z };
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 { TMetadataOverrides as $, TDbCollation as A, TDbInsertResult as B, TColumnDiff as C, TDbActionIntent as D, TDbActionInfo as E, TDbForeignKey as F, TExistingColumn as G, TDbRelation as H, TDbIndex as I, TFkLookupResolver as J, TExistingTableOption as K, TDbIndexField as L, TDbDefaultValue as M, TDbDeleteResult as N, TDbActionLevel as O, TDbFieldMeta as P, TMetaResponse as Q, TDbIndexType as R, TCascadeTarget as S, TCrudPermissions as T, TDbStorageType as U, TDbReferentialAction as V, TDbUpdateResult as W, TIdDescriptor as X, TFkLookupTarget as Y, TIdentification as Z, FlatOf$1 as _, FieldMappingStrategy as a, TValueFormatterPair as at, PrimaryKeyOf$1 as b, AggregateExpr$1 as c, Uniquery$1 as ct, AggregateResult as d, TableMetadata as dt, TRelationInfo as et, AtscriptDbWritable as f, NoopLogger as ft, FilterExpr$1 as g, FieldOpsFor as h, DocumentFieldMapper as i, TTableResolver as it, TDbDefaultFn as j, TDbActionProcessor as k, AggregateFn as l, UniqueryControls$1 as lt, DbQuery as m, UniquSelect as mt, DbResponse as n, TSyncColumnResult as nt, BaseDbAdapter as o, TWriteTableResolver as ot, DbControls as p, TGenericLogger as pt, TFieldMeta as q, resolveDesignType as r, TTableOptionDiff as rt, AggregateControls as s, TypedWithRelation as st, AtscriptDbReadable as t, TSearchIndexInfo as tt, AggregateQuery$1 as u, WithRelation$1 as ut, NavPropsOf$1 as v, TCrudOp as w, TCascadeResolver as x, OwnPropsOf$1 as y, TDbInsertManyResult as z };
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 };