@atscript/db 0.1.127 → 0.1.129

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.
Files changed (41) hide show
  1. package/dist/{db-error-DXwEzmYJ.cjs → db-error-C4JuLcvb.cjs} +27 -0
  2. package/dist/{db-error-BHPXOKzc.mjs → db-error-COrO58t5.mjs} +22 -1
  3. package/dist/{db-readable-BkAGccv9.d.mts → db-readable-B7eYWS5q.d.cts} +299 -17
  4. package/dist/{db-readable-C0nDKX8A.d.cts → db-readable-Bn1bV_eC.d.mts} +299 -17
  5. package/dist/{db-space-B_ASuDaR.d.mts → db-space-C2UCnGHd.d.cts} +108 -30
  6. package/dist/{db-space-CSntT6yS.d.cts → db-space-DdIPYD0Q.d.mts} +108 -30
  7. package/dist/{db-view-BP0Qbeux.cjs → db-view-CRBgkEp0.cjs} +786 -100
  8. package/dist/{db-view-C8rZM5_N.mjs → db-view-Dl0aDTiT.mjs} +733 -101
  9. package/dist/index.cjs +48 -3
  10. package/dist/index.d.cts +162 -37
  11. package/dist/index.d.mts +162 -37
  12. package/dist/index.mjs +37 -5
  13. package/dist/{nested-writer-DI-HeTky.mjs → nested-writer-CkDo-ZfH.mjs} +1 -1
  14. package/dist/{nested-writer-DoDhl3X3.cjs → nested-writer-DxPhmWFz.cjs} +1 -1
  15. package/dist/{ops-DJRnNTVo.d.cts → ops-AqhV7s9o.d.cts} +24 -1
  16. package/dist/{ops-DJRnNTVo.d.mts → ops-AqhV7s9o.d.mts} +24 -1
  17. package/dist/ops.cjs +44 -1
  18. package/dist/ops.d.cts +2 -2
  19. package/dist/ops.d.mts +2 -2
  20. package/dist/ops.mjs +44 -2
  21. package/dist/plugin.cjs +12 -5
  22. package/dist/plugin.mjs +12 -5
  23. package/dist/rel.cjs +2 -2
  24. package/dist/rel.d.cts +1 -1
  25. package/dist/rel.d.mts +1 -1
  26. package/dist/rel.mjs +2 -2
  27. package/dist/{relation-loader-BnUgJsUG.cjs → relation-loader-C8GOpNYJ.cjs} +1 -1
  28. package/dist/{relation-loader-BmeOMj0b.mjs → relation-loader-CUGcxJ18.mjs} +1 -1
  29. package/dist/sync.cjs +1270 -398
  30. package/dist/sync.d.cts +255 -18
  31. package/dist/sync.d.mts +255 -18
  32. package/dist/sync.mjs +1269 -399
  33. package/dist/{validator-0vRXN51D.mjs → validator-CeD_fqyW.mjs} +21 -4
  34. package/dist/{validator-CSGug4vg.cjs → validator-lkCJKuoo.cjs} +32 -3
  35. package/dist/{validator-BcBtg8yW.d.cts → validator-wBARmD68.d.cts} +57 -1
  36. package/dist/{validator-BcBtg8yW.d.mts → validator-wBARmD68.d.mts} +57 -1
  37. package/dist/validator.cjs +7 -1
  38. package/dist/validator.d.cts +3 -3
  39. package/dist/validator.d.mts +3 -3
  40. package/dist/validator.mjs +4 -3
  41. package/package.json +8 -8
@@ -50,6 +50,27 @@ var CasExhaustedError = class extends DbError {
50
50
  this.lastSeenVersion = lastSeenVersion;
51
51
  }
52
52
  };
53
+ /**
54
+ * Thrown by `AtscriptDbTable.touchMany` (`require: 'all'`, the default) when
55
+ * fewer rows than keys matched their expected version — at least one row is
56
+ * stale or missing. Nothing was written (the pre-count refused before the
57
+ * first statement, or the SQL transaction rolled every bump back). Surfaced
58
+ * as HTTP 409 by moost-db.
59
+ */
60
+ var CasMismatchError = class extends DbError {
61
+ matched;
62
+ expected;
63
+ name = "CasMismatchError";
64
+ constructor(matched, expected) {
65
+ const message = `touchMany: ${matched} of ${expected} rows matched — stale or missing rows`;
66
+ super("CAS_MISMATCH", [{
67
+ path: "$cas",
68
+ message
69
+ }], message);
70
+ this.matched = matched;
71
+ this.expected = expected;
72
+ }
73
+ };
53
74
  //#endregion
54
75
  Object.defineProperty(exports, "CasExhaustedError", {
55
76
  enumerable: true,
@@ -57,6 +78,12 @@ Object.defineProperty(exports, "CasExhaustedError", {
57
78
  return CasExhaustedError;
58
79
  }
59
80
  });
81
+ Object.defineProperty(exports, "CasMismatchError", {
82
+ enumerable: true,
83
+ get: function() {
84
+ return CasMismatchError;
85
+ }
86
+ });
60
87
  Object.defineProperty(exports, "DbError", {
61
88
  enumerable: true,
62
89
  get: function() {
@@ -50,5 +50,26 @@ var CasExhaustedError = class extends DbError {
50
50
  this.lastSeenVersion = lastSeenVersion;
51
51
  }
52
52
  };
53
+ /**
54
+ * Thrown by `AtscriptDbTable.touchMany` (`require: 'all'`, the default) when
55
+ * fewer rows than keys matched their expected version — at least one row is
56
+ * stale or missing. Nothing was written (the pre-count refused before the
57
+ * first statement, or the SQL transaction rolled every bump back). Surfaced
58
+ * as HTTP 409 by moost-db.
59
+ */
60
+ var CasMismatchError = class extends DbError {
61
+ matched;
62
+ expected;
63
+ name = "CasMismatchError";
64
+ constructor(matched, expected) {
65
+ const message = `touchMany: ${matched} of ${expected} rows matched — stale or missing rows`;
66
+ super("CAS_MISMATCH", [{
67
+ path: "$cas",
68
+ message
69
+ }], message);
70
+ this.matched = matched;
71
+ this.expected = expected;
72
+ }
73
+ };
53
74
  //#endregion
54
- export { DbError as n, DepthLimitExceededError as r, CasExhaustedError as t };
75
+ export { DepthLimitExceededError as i, CasMismatchError as n, DbError as r, CasExhaustedError as t };
@@ -1,6 +1,6 @@
1
- import { f as TFieldOps } from "./ops-DJRnNTVo.mjs";
2
- 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";
1
+ import { f as TFieldOps } from "./ops-AqhV7s9o.cjs";
3
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";
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
6
6
  /**
@@ -55,6 +55,12 @@ interface TGenericLogger {
55
55
  declare const NoopLogger: TGenericLogger;
56
56
  //#endregion
57
57
  //#region src/table/table-metadata.d.ts
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;
58
64
  /** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
59
65
  declare function isGeoPointType(fieldType: TAtscriptAnnotatedType): boolean;
60
66
  /**
@@ -118,6 +124,22 @@ declare class TableMetadata {
118
124
  leafByPhysical: Map<string, TDbFieldMeta>;
119
125
  /** Leaf field descriptors indexed by logical path (write/patch/filter paths). */
120
126
  leafByLogical: Map<string, TDbFieldMeta>;
127
+ /**
128
+ * Non-ignored field descriptors keyed by logical path, excluding navigation
129
+ * relations and their descendants. Unlike `leafByLogical` (relational
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.
133
+ */
134
+ descriptorByPath: Map<string, TDbFieldMeta>;
135
+ /**
136
+ * Logical paths stored as a single JSON column (`storage === 'json'`,
137
+ * non-ignored descriptors). Retained after build — unlike the build-time
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).
141
+ */
142
+ jsonParents: ReadonlySet<string>;
121
143
  private _built;
122
144
  private _identifications?;
123
145
  private _collateMap;
@@ -166,6 +188,14 @@ declare class TableMetadata {
166
188
  private _classifyFields;
167
189
  /** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
168
190
  private _flattenedPrefix;
191
+ /** Nearest `@db.encrypted` ancestor of `path` (exclusive), or `undefined`. */
192
+ /**
193
+ * Indexes non-ignored descriptors by logical path and retains the JSON-parent
194
+ * set. Navigation relations and their descendants are skipped even when the
195
+ * adapter keeps them as descriptors (nested-object adapters do) — they are
196
+ * loaded with `$with`, never addressed as columns of this table.
197
+ */
198
+ private _buildGuardIndexes;
169
199
  /**
170
200
  * Indexes `fieldDescriptors` into two lookup maps for unified
171
201
  * read/write field classification in the RelationalFieldMapper.
@@ -244,6 +274,13 @@ interface TFieldMeta {
244
274
  * present in read responses. UIs render it as a set-only input.
245
275
  */
246
276
  writeOnly?: boolean;
277
+ /**
278
+ * Present (true) when the field is index-backed (explicit `@db.index*`,
279
+ * primary key or unique field). Advisory only — a hint for UIs that want to
280
+ * steer users toward cheap sort keys; it never affects whether a `$sort`
281
+ * is accepted (`sortable` does). Since 0.1.128.
282
+ */
283
+ indexed?: boolean;
247
284
  }
248
285
  /** Built-in CRUD operation names; map 1:1 to public method names. */
249
286
  type TCrudOp = "query" | "pages" | "one" | "geo" | "insert" | "update" | "replace" | "remove";
@@ -543,6 +580,57 @@ interface TColumnDiff {
543
580
  oldName: string;
544
581
  conflictsWith: string;
545
582
  }>;
583
+ /**
584
+ * The primary-key FIELD SET differs between the live table and the model
585
+ * (set semantics — a composite-key reorder is not a change, consistent with
586
+ * the schema hash). Column names are physical; a renamed PK column is
587
+ * compared under its new name. Only reported when the table exists.
588
+ * @since 0.1.128
589
+ */
590
+ primaryKeyChanged?: TPrimaryKeyChange;
591
+ }
592
+ /** Old and new primary-key column sets of a table whose key definition moved. */
593
+ interface TPrimaryKeyChange {
594
+ /** Physical PK columns currently in the database (after rename mapping). */
595
+ from: string[];
596
+ /** Physical PK columns the model declares. */
597
+ to: string[];
598
+ }
599
+ /**
600
+ * A live foreign-key constraint as introspected from the database
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 {
627
+ /**
628
+ * Table names whose inline FOREIGN KEY constraints must be omitted from
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.
632
+ */
633
+ deferForeignKeysTo?: ReadonlySet<string>;
546
634
  }
547
635
  /** Result of applying column diff to the database. */
548
636
  interface TSyncColumnResult {
@@ -684,6 +772,102 @@ interface TDbRelation {
684
772
  /** Junction type reference for 'via' (M:N) relations. */
685
773
  viaType?: () => TAtscriptAnnotatedType;
686
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;
797
+ /**
798
+ * insert/replace: validated rows with SDK-side defaults applied (plaintext,
799
+ * nav data still attached); update: validated patches with the identifying
800
+ * PK/unique fields present and `$cas` removed. Mutate in place to enrich —
801
+ * the table re-validates the rows after the guard.
802
+ */
803
+ readonly rows: Row[];
804
+ /** Parallel to `rows`: expected version lifted from `$cas`, or `undefined`. */
805
+ readonly expectedVersions: ReadonlyArray<number | undefined>;
806
+ /**
807
+ * Lazy, memoised pre-image of `rows[i]` by its identifying filter, read
808
+ * inside the transaction. `null` when the row is missing OR when it carries
809
+ * no identifying key yet (e.g. auto-increment inserts) — never throws.
810
+ */
811
+ current(i: number): Promise<Row | null>;
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 {
833
+ /**
834
+ * `'all'` (default): every key must match its stored version — a stale or
835
+ * missing row throws `DbError("CAS_MISMATCH")` and no version moves.
836
+ * `'any'`: bump whatever matches and report the honest counts.
837
+ */
838
+ require?: "all" | "any";
839
+ }
840
+ /** Options of `insertOne/Many`, `replaceOne` / `bulkReplace`, `updateOne` / `bulkUpdate`. */
841
+ interface TWriteOptions<Row = Record<string, unknown>> {
842
+ /** Nested-relation write recursion limit (default 3). */
843
+ maxDepth?: number;
844
+ /**
845
+ * Validated-stage guard (since 0.1.128): invoked exactly once inside the
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.
851
+ */
852
+ guard?: TDbWriteGuard<Row>;
853
+ }
854
+ /** Options of `deleteOne`. */
855
+ interface TDeleteOptions<Row = Record<string, unknown>> {
856
+ /**
857
+ * Validated-stage guard (since 0.1.128): invoked inside the table's
858
+ * transaction after the id resolved to a filter and before cascade /
859
+ * delete. A throw rolls the transaction back and propagates unchanged.
860
+ */
861
+ guard?: TDbRemoveGuard<Row>;
862
+ }
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] };
687
871
  //#endregion
688
872
  //#region src/base-adapter.d.ts
689
873
  /**
@@ -710,6 +894,15 @@ interface TDbRelation {
710
894
  * - `this._table.isView` — whether this is a view (vs a table)
711
895
  */
712
896
  declare abstract class BaseDbAdapter {
897
+ /**
898
+ * The readable this adapter serves. UNSET on an administrative adapter:
899
+ * `DbSpace` creates one from the factory without a readable for the
900
+ * name-taking schema-sync primitives (`dropTableByName`, `dropViewByName`,
901
+ * `dropTablesByName`, `getReferencingForeignKeys`, `getObjectKind`,
902
+ * `getExistingColumnsForTable`, `hasRows(tableName)`), so those must derive
903
+ * everything — the schema included — from the driver/connection, never from
904
+ * `this._table`.
905
+ */
713
906
  protected _table: AtscriptDbReadable<any, any, any, any, any, any, any>;
714
907
  /**
715
908
  * Resolves the correct insertedId: prefers the user-supplied PK value
@@ -738,7 +931,9 @@ declare abstract class BaseDbAdapter {
738
931
  protected _log(...args: unknown[]): void;
739
932
  /**
740
933
  * Runs `fn` inside a database transaction. Nested calls (from related tables
741
- * within the same async chain) reuse the existing transaction automatically.
934
+ * within the same async chain) reuse the existing transaction automatically
935
+ * — "existing" meaning a transaction of the same {@link _transactionOwner};
936
+ * inside another adapter family's transaction this opens its own.
742
937
  *
743
938
  * The generic layer handles nesting detection via `AsyncLocalStorage`.
744
939
  * Adapters override `_beginTransaction`, `_commitTransaction`, and
@@ -746,7 +941,17 @@ declare abstract class BaseDbAdapter {
746
941
  */
747
942
  withTransaction<T>(fn: () => Promise<T>): Promise<T>;
748
943
  /**
749
- * Returns the opaque transaction state from the current async context.
944
+ * The object a transaction state is branded with (since 0.1.128). Every
945
+ * adapter instance that returns the same owner shares one transaction —
946
+ * override to return the driver / pool / client the adapter was constructed
947
+ * with, so all tables of a space join it. The default (the adapter class)
948
+ * suits adapters without a connection object (in-memory, mocks).
949
+ */
950
+ protected _transactionOwner(): unknown;
951
+ /**
952
+ * Returns the opaque transaction state of THIS adapter's owner from the
953
+ * current async context — `undefined` when no transaction is open or only
954
+ * another adapter family's transaction is (its state is never handed out).
750
955
  * Adapters use this to retrieve DB-specific state (e.g., MongoDB `ClientSession`).
751
956
  */
752
957
  protected _getTransactionState(): unknown;
@@ -755,7 +960,7 @@ declare abstract class BaseDbAdapter {
755
960
  * Adapters that override `withTransaction` (e.g., to use MongoDB's
756
961
  * `session.withTransaction()` Convenient API) use this to set up the
757
962
  * shared context so that nested adapters see the same session.
758
- * If a context already exists (nesting), it's reused.
963
+ * If a context of the same owner already exists (nesting), it's reused.
759
964
  */
760
965
  protected _runInTransactionContext<T>(state: unknown, fn: () => Promise<T>): Promise<T>;
761
966
  /**
@@ -797,11 +1002,14 @@ declare abstract class BaseDbAdapter {
797
1002
  */
798
1003
  supportsNestedObjects(): boolean;
799
1004
  /**
800
- * Whether the DB engine handles static `@db.default "value"` natively
801
- * via column-level DEFAULT clauses in CREATE TABLE.
802
- * When `true`, `_applyDefaults()` skips client-side value defaults,
803
- * letting the DB apply its own DEFAULT. SQL adapters return `true`;
804
- * document stores (MongoDB) return `false` and apply defaults client-side.
1005
+ * Whether the DB engine carries static `@db.default "value"` defaults in
1006
+ * its DDL (`DEFAULT` clauses in `CREATE TABLE`).
1007
+ *
1008
+ * @deprecated since 0.1.128 — no longer consulted: the table layer fills
1009
+ * static value defaults SDK-side on every adapter before validation, and the
1010
+ * SQL adapters emit their DDL `DEFAULT` clauses regardless of this flag.
1011
+ * Kept as a capability hint for tooling; nothing in the generic layer
1012
+ * branches on it.
805
1013
  */
806
1014
  supportsNativeValueDefaults(): boolean;
807
1015
  /**
@@ -838,9 +1046,12 @@ declare abstract class BaseDbAdapter {
838
1046
  canFilterField(fd: TDbFieldMeta): boolean;
839
1047
  /**
840
1048
  * Whether this adapter can sort by a given field.
841
- * Default: scalar columns yes, JSON-stored columns no. Mongo's array sort
842
- * (min/max element) is a footgun for generic UI sort headers, so the default
843
- * stays conservative even for adapters that technically support it.
1049
+ * Default: scalar columns yes; JSON-stored columns, `@db.json` objects and
1050
+ * arrays no. Mongo's array sort (min/max element) is a footgun for generic
1051
+ * UI sort headers, so the default stays conservative even for adapters that
1052
+ * technically support it — the veto keys on `designType` as well as
1053
+ * `storage` because nested-object adapters keep arrays / `@db.json` values
1054
+ * inline as `storage: 'column'` (since 0.1.128).
844
1055
  */
845
1056
  canSortField(fd: TDbFieldMeta): boolean;
846
1057
  /**
@@ -961,6 +1172,14 @@ declare abstract class BaseDbAdapter {
961
1172
  dropIndex(name: string): Promise<void>;
962
1173
  prefix?: string;
963
1174
  shouldSkipType?(type: TDbIndex["type"]): boolean;
1175
+ /**
1176
+ * Renders one desired key part for the drift comparison, so adapters whose
1177
+ * `listExisting` reports more than a bare column name (e.g. MySQL's
1178
+ * `col(255)` key-length prefix) can render the model side identically.
1179
+ * Default: the column name.
1180
+ * @since 0.1.128
1181
+ */
1182
+ renderDesiredColumn?(index: TDbIndex, field: TDbIndex["fields"][number]): string;
964
1183
  /**
965
1184
  * Index types declared on the model but not supported by this adapter —
966
1185
  * warns and skips (models stay portable; sync never errors on these).
@@ -1090,8 +1309,14 @@ declare abstract class BaseDbAdapter {
1090
1309
  /**
1091
1310
  * Ensures the table exists in the database, creating it if needed.
1092
1311
  * Uses `this._table.tableName`, `this._table.schema`, etc.
1312
+ *
1313
+ * @param opts - Optional (since 0.1.128). Relational adapters that emit
1314
+ * inline FOREIGN KEY constraints must omit those whose target is in
1315
+ * `opts.deferForeignKeysTo` — schema sync adds them afterwards through
1316
+ * {@link syncForeignKeys} so a foreign-key cycle can be created in any
1317
+ * order. Adapters without inline constraints ignore the parameter.
1093
1318
  */
1094
- abstract ensureTable(): Promise<void>;
1319
+ abstract ensureTable(opts?: TEnsureTableOptions): Promise<void>;
1095
1320
  /**
1096
1321
  * Synchronizes foreign key constraints between Atscript definitions and the database.
1097
1322
  * Uses `this._table.foreignKeys` for the full FK definitions.
@@ -1128,8 +1353,13 @@ declare abstract class BaseDbAdapter {
1128
1353
  *
1129
1354
  * Returns undefined if the adapter cannot introspect table options.
1130
1355
  * In that case, schema sync falls back to stored snapshot.
1356
+ *
1357
+ * @param tableName - Introspect this table instead of the adapter's own
1358
+ * (schema sync passes the OLD name of a table that is about to be
1359
+ * renamed, as for `getExistingColumnsForTable`). Defaults to the bound
1360
+ * table.
1131
1361
  */
1132
- getExistingTableOptions?(): Promise<TExistingTableOption[]>;
1362
+ getExistingTableOptions?(tableName?: string): Promise<TExistingTableOption[]>;
1133
1363
  /**
1134
1364
  * Applies non-destructive table option changes (e.g., MySQL ALTER TABLE ENGINE=X).
1135
1365
  * Called for each non-destructive change in the diff.
@@ -1205,6 +1435,58 @@ declare abstract class BaseDbAdapter {
1205
1435
  * Optional — only relational adapters implement this.
1206
1436
  */
1207
1437
  dropViewByName?(viewName: string): Promise<void>;
1438
+ /**
1439
+ * Drops several tables that reference each other (a foreign-key cycle) as
1440
+ * one operation. Schema sync only calls this for cycles whose members are
1441
+ * ALL being removed. Default: {@link dropTableByName} in the given order —
1442
+ * enough for engines that tolerate it (SQLite with FK checks off, MySQL with
1443
+ * FOREIGN_KEY_CHECKS=0); PostgreSQL overrides it with one multi-table
1444
+ * `DROP TABLE a, b` statement.
1445
+ * @since 0.1.128
1446
+ */
1447
+ dropTablesByName(tableNames: string[]): Promise<void>;
1448
+ /**
1449
+ * Whether the table has at least one row. Schema sync uses it in the
1450
+ * pre-flight phase to refuse a primary-key change on a populated table.
1451
+ * Override with an EXISTS/LIMIT 1 probe — this default is `count() > 0`,
1452
+ * a full scan on some engines, and it can only answer for the adapter's
1453
+ * OWN table: for another `tableName` (or on an administrative adapter
1454
+ * without a readable) it returns `undefined` ("cannot tell"), which schema
1455
+ * sync treats as a refusal.
1456
+ *
1457
+ * @param tableName - Check this table instead of the adapter's own (schema
1458
+ * sync passes the OLD name of a table that is about to be renamed).
1459
+ * @returns `true`/`false`, or `undefined` when the adapter cannot tell.
1460
+ * @since 0.1.128
1461
+ */
1462
+ hasRows(tableName?: string): Promise<boolean | undefined>;
1463
+ /**
1464
+ * Live foreign keys that REFERENCE `tableName` (inbound edges), from any
1465
+ * table in the database — including tables whose models are no longer in
1466
+ * the sync inventory. Schema sync uses it to order drops (children before
1467
+ * parents), to refuse dropping a table that an unmanaged table still
1468
+ * references, and to refuse a primary-key change that a live FK depends on.
1469
+ * Optional — engines without physical foreign keys omit it.
1470
+ * @since 0.1.128
1471
+ */
1472
+ getReferencingForeignKeys?(tableName: string): Promise<TReferencingForeignKey[]>;
1473
+ /**
1474
+ * Kind of the physical object stored under `name`, or `undefined` when
1475
+ * nothing exists. Schema sync refuses a run when a physical table sits
1476
+ * where a managed view is declared (or a view where a table is declared)
1477
+ * instead of silently creating/skipping over it.
1478
+ * Optional — adapters without the method skip the check.
1479
+ * @since 0.1.128
1480
+ */
1481
+ getObjectKind?(name: string): Promise<TDbObjectKind | undefined>;
1482
+ /**
1483
+ * Rewrites the table's primary key from `change.from` to `change.to`.
1484
+ * Called only on an EMPTY table (schema sync refuses populated ones) after
1485
+ * new columns were added and before stale columns are dropped, so both
1486
+ * column sets exist. Adapters without it fall back to {@link recreateTable}.
1487
+ * @since 0.1.128
1488
+ */
1489
+ rebuildPrimaryKey?(change: TPrimaryKeyChange): Promise<void>;
1208
1490
  /**
1209
1491
  * Renames a table/collection from `oldName` to the adapter's current table name.
1210
1492
  * Used by schema sync when `@db.table.renamed` is present.
@@ -1422,7 +1704,7 @@ declare function resolveDesignType(fieldType: TAtscriptAnnotatedType): string;
1422
1704
  * {@link AtscriptDbTable} (adds write operations) and {@link AtscriptDbView}
1423
1705
  * (adds view plan/DDL).
1424
1706
  */
1425
- declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>, _FlatType = FlatOf<T>, A extends BaseDbAdapter = BaseDbAdapter, IdType = PrimaryKeyOf<T>, OwnProps = OwnPropsOf<T>, NavType extends Record<string, unknown> = NavPropsOf<T>> {
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>> {
1426
1708
  protected readonly _type: T;
1427
1709
  protected readonly adapter: A;
1428
1710
  protected readonly logger: TGenericLogger;
@@ -1733,4 +2015,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
1733
2015
  }, thisTableName: string, alias?: string): TDbForeignKey | undefined;
1734
2016
  }
1735
2017
  //#endregion
1736
- 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 };
2018
+ export { TDbWriteAction as $, TCrudPermissions as A, findAncestorInSet as At, TDbForeignKey as B, NullableOptional as C, TWriteOptions as Ct, TCascadeTarget as D, UniqueryControls$1 as Dt, TCascadeResolver as E, Uniquery$1 as Et, TDbCollation as F, UniquSelect as Ft, TDbInsertResult as G, TDbIndexField as H, TDbDefaultFn as I, TDbRelation as J, TDbObjectKind as K, TDbDefaultValue as L, TDbActionIntent as M, isGeoPointType as Mt, TDbActionLevel as N, NoopLogger as Nt, TColumnDiff as O, WithRelation$1 as Ot, TDbActionProcessor as P, TGenericLogger as Pt, TDbUpdateResult as Q, TDbDeleteResult as R, NavPropsOf$1 as S, TValueFormatterPair as St, PrimaryKeyOf$1 as T, TypedWithRelation as Tt, TDbIndexType as U, TDbIndex as V, TDbInsertManyResult as W, TDbRemoveGuardContext as X, TDbRemoveGuard as Y, TDbStorageType as Z, DbQuery as _, TSearchIndexInfo as _t, TDbEncryptionOptions as a, TExistingForeignKey as at, FilterExpr$1 as b, TTableResolver as bt, BaseDbAdapter as c, TFkLookupResolver as ct, AggregateFn as d, TIdentification as dt, TDbWriteGuard as et, AggregateQuery$1 as f, TMetaResponse as ft, DbPatch as g, TRelationInfo as gt, DbControls as h, TReferencingForeignKey as ht, DbEncryption as i, TExistingColumn as it, TDbActionInfo as j, isGeoIndexableType as jt, TCrudOp as k, TableMetadata as kt, AggregateControls as l, TFkLookupTarget as lt, AtscriptDbWritable as m, TPrimaryKeyChange as mt, DbResponse as n, TDeleteOptions as nt, DocumentFieldMapper as o, TExistingTableOption as ot, AggregateResult as p, TMetadataOverrides as pt, TDbReferentialAction as q, resolveDesignType as r, TEnsureTableOptions as rt, FieldMappingStrategy as s, TFieldMeta as st, AtscriptDbReadable as t, TDbWriteGuardContext as tt, AggregateExpr$1 as u, TIdDescriptor as ut, DbRow as v, TSyncColumnResult as vt, OwnPropsOf$1 as w, TWriteTableResolver as wt, FlatOf$1 as x, TTouchManyOptions as xt, FieldOpsFor as y, TTableOptionDiff as yt, TDbFieldMeta as z };