@voltro/database 0.63.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -30,6 +30,32 @@ export declare const actorsTable: Table<'actors', Record<string, ColumnDefinitio
30
30
  */
31
31
  export declare const admitBranch: (branches: ReadonlyArray<BranchRecord>, policy: BranchPolicy, nowMs: number) => BranchLimitDecision;
32
32
 
33
+ export declare interface AgedSoftDropSnapshot {
34
+ /** `table` or `table.column`. */
35
+ readonly name: string;
36
+ readonly droppedAt: Date;
37
+ readonly ageDays: number;
38
+ }
39
+
40
+ /**
41
+ * The soft-drop snapshots older than the review window — the ones nobody
42
+ * restored and nobody reclaimed. A snapshot is a full copy of the dropped data,
43
+ * PII included, and `gc-snapshots` is a manual step an operator has to
44
+ * remember; this is the reminder, computed from the stamp in the name so it
45
+ * costs no query beyond the introspection that already ran.
46
+ */
47
+ export declare const agedSoftDropSnapshots: (live: {
48
+ readonly tables: ReadonlyArray<{
49
+ readonly name: string;
50
+ readonly columns?: ReadonlyArray<{
51
+ readonly name: string;
52
+ }>;
53
+ }>;
54
+ }, options?: {
55
+ readonly now?: Date;
56
+ readonly retentionDays?: number;
57
+ }) => ReadonlyArray<AgedSoftDropSnapshot>;
58
+
33
59
  export declare interface AggregateColumn<TResult = number | Date | null> {
34
60
  readonly op: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'count-distinct' | 'window-row-number' | 'window-rank' | 'window-dense-rank' | 'window-lag' | 'window-lead' | 'window-sum-over' | 'window-avg-over' | 'column';
35
61
  readonly column?: string;
@@ -303,6 +329,9 @@ export declare const bindResidentStore: <S>(subject: ResidencySubject, config: R
303
329
 
304
330
  export declare const boolean: () => ColumnBuilder<boolean, "boolean", boolean>;
305
331
 
332
+ /** The BOUND envelope: `enc:v2:<r|c>:<iv>:<tag>:<ct>` — see {@link FieldBinding}. */
333
+ export declare const BOUND_ENCRYPTED_PREFIX = "enc:v2:";
334
+
306
335
  export declare type BranchEvent = 'provision' | 'provisioned' | 'fail' | 'destroy' | 'destroyed';
307
336
 
308
337
  /**
@@ -1380,6 +1409,14 @@ export declare interface ColumnDefinition<TsType, Type extends ColumnType = Colu
1380
1409
  * has nothing to do with FK semantics.
1381
1410
  */
1382
1411
  readonly refOnUpdate?: 'cascade' | 'restrict' | 'setNull' | 'noAction';
1412
+ /**
1413
+ * What the RUNTIME does to this column's rows when the referenced row is
1414
+ * SOFT-deleted (`deletedAt` set) — set by `reference(…, { onSoftDelete })`.
1415
+ * A foreign key never sees a soft delete (it is an UPDATE), so `onDelete`
1416
+ * cannot express it; the store's post-commit change channel enforces this
1417
+ * one instead, the way `pluginRef` orphan rules are enforced.
1418
+ */
1419
+ readonly refOnSoftDelete?: 'cascade' | 'setNull';
1383
1420
  /**
1384
1421
  * Auto-create a B-tree index on this FK column. Default `true` — FK
1385
1422
  * columns are nearly always read-by, and the lookup cost of a missing
@@ -1483,12 +1520,16 @@ export declare interface ColumnDefinition<TsType, Type extends ColumnType = Colu
1483
1520
  /**
1484
1521
  * Field-level encryption flag, set by `.encrypted()`. When `true`
1485
1522
  * the runtime's store middleware encrypts the value on write and
1486
- * decrypts on read via the registered field cipher (AES-256-GCM,
1487
- * keyed from the Secrets-Resolver — see `@voltro/plugin-governance`).
1488
- * The stored column is an opaque `enc:v1:…` string, so the DB type
1489
- * stays `text`. A row written while encryption is active can't be
1523
+ * decrypts on read via the registered field cipher (AES-256-GCM, keyed
1524
+ * from `VOLTRO_FIELD_ENCRYPTION_KEY` through the Secrets-Resolver — the
1525
+ * framework registers it on both boot paths; `voltro dev` mints the key).
1526
+ * The stored column is an opaque `enc:v2:…` string bound by GCM's
1527
+ * additional authenticated data to its table, column and row
1528
+ * (`FieldBinding`), so a ciphertext copied into another row or column
1529
+ * does not decrypt there — and the DB type stays `text`. A row written
1530
+ * while encryption is active can't be
1490
1531
  * read back without the key — losing the key loses the data, by
1491
- * design. Boot fails loud if an `.encrypted()` column is present but
1532
+ * design. Boot refuses if an `.encrypted()` column is declared but
1492
1533
  * no cipher is registered.
1493
1534
  */
1494
1535
  readonly encrypted?: boolean;
@@ -2757,7 +2798,7 @@ export declare const declareEnumRename: (enumName: string, rename: EnumValueRena
2757
2798
  * it is written here so the next reader does not go looking for the branch that
2758
2799
  * handles it.
2759
2800
  */
2760
- export declare const decodeFieldValue: (cipher: FieldCipher, ciphertext: string, expected?: string) => unknown;
2801
+ export declare const decodeFieldValue: (cipher: FieldCipher, ciphertext: string, expected?: string, binding?: FieldBinding) => unknown;
2761
2802
 
2762
2803
  export declare type DecoderDialectId = 'postgres' | 'mysql' | 'mariadb' | 'sqlite' | 'mssql' | 'turso';
2763
2804
 
@@ -2789,6 +2830,9 @@ export declare const decryptFieldsOnRead: (rows: ReadonlyArray<Row>, table: Tabl
2789
2830
  readonly column: string;
2790
2831
  readonly reason: string;
2791
2832
  }) => void;
2833
+ /** The row's id when the rows themselves do not carry it — the row a KEYED
2834
+ * write returns is addressed by the `pk` the caller passed. */
2835
+ readonly rowId?: unknown;
2792
2836
  }) => ReadonlyArray<Row>;
2793
2837
 
2794
2838
  /**
@@ -2821,6 +2865,10 @@ export declare const DEFAULT_CHANGE_CLAIM_TTL_HOURS = 1;
2821
2865
  */
2822
2866
  export declare const DEFAULT_EAGER_FALLBACK_WARN_INTERVAL_MS = 300000;
2823
2867
 
2868
+ /** Default review window for a soft-drop snapshot, in days — the "~7d" the
2869
+ * soft-drop advice promises. Past it, a boot and a `db plan` say so. */
2870
+ export declare const DEFAULT_SOFT_DROP_RETENTION_DAYS = 7;
2871
+
2824
2872
  export declare const DEFAULT_SUBJECT_GRAPH_DEPTH = 4;
2825
2873
 
2826
2874
  export declare const defineMigration: (input: MigrationDefinitionInput) => MigrationDefinition;
@@ -2921,6 +2969,9 @@ export declare interface DeriveSubjectGraphInput {
2921
2969
  */
2922
2970
  export declare const deriveTypeIdPrefix: (tableName: string) => string;
2923
2971
 
2972
+ /** The one line both the boot and `db plan` print for an aged snapshot set. */
2973
+ export declare const describeAgedSoftDropSnapshots: (aged: ReadonlyArray<AgedSoftDropSnapshot>, retentionDays: number) => string;
2974
+
2924
2975
  /**
2925
2976
  * A one-line driver summary for an error, or `undefined` when the chain carries
2926
2977
  * nothing driver-shaped.
@@ -3268,7 +3319,7 @@ declare type EmptyMerge = unknown;
3268
3319
  * remedies, and collapsing them is what cost the consumer a day. `decodeFieldValue`
3269
3320
  * therefore decrypts and decodes as two separate steps and says which one failed.
3270
3321
  */
3271
- export declare const encodeFieldValue: (cipher: FieldCipher, value: unknown) => string;
3322
+ export declare const encodeFieldValue: (cipher: FieldCipher, value: unknown, binding?: FieldBinding) => string;
3272
3323
 
3273
3324
  /**
3274
3325
  * Serialize a row's json()/array() cells to JSON strings so they can be
@@ -3278,7 +3329,7 @@ export declare const encodeFieldValue: (cipher: FieldCipher, value: unknown) =>
3278
3329
  */
3279
3330
  export declare const encodeRowForSchema: <T extends Record<string, unknown>>(row: T, tableName: string, types?: ReadonlyArray<ColumnType>) => T;
3280
3331
 
3281
- /** A ciphertext produced by the framework cipher carries this prefix. */
3332
+ /** The UNBOUND envelope: `enc:v1:<iv>:<tag>:<ct>`. */
3282
3333
  export declare const ENCRYPTED_PREFIX = "enc:v1:";
3283
3334
 
3284
3335
  /** Column names on a table flagged `.encrypted()`. */
@@ -3290,10 +3341,14 @@ export declare const encryptedColumnsOf: (table: TableLike) => ReadonlyArray<str
3290
3341
  * `null` / `undefined` pass through. An already-encrypted value is left
3291
3342
  * as-is (idempotent — a re-passed ciphertext isn't double-wrapped).
3292
3343
  *
3344
+ * Each value is BOUND to its address (`FieldBinding`): to the row when its id
3345
+ * is known — the row's own `id`, or `rowId` for a keyed write whose patch does
3346
+ * not carry it — and to the column otherwise.
3347
+ *
3293
3348
  * Throws if an encrypted column carries a value but no cipher is
3294
3349
  * registered — losing the key silently would be worse than a loud boot.
3295
3350
  */
3296
- export declare const encryptFieldsForWrite: (row: Row, table: TableLike | undefined, cipher: FieldCipher | undefined) => Row;
3351
+ export declare const encryptFieldsForWrite: (row: Row, table: TableLike | undefined, cipher: FieldCipher | undefined, rowId?: unknown) => Row;
3297
3352
 
3298
3353
  /** Mark it finished — AFTER its `routeEvent` has registered, not after the
3299
3354
  * statement. That is the whole point: the window being closed is exactly the
@@ -3374,9 +3429,10 @@ export declare interface ExistsPredicate {
3374
3429
  * expiry on every existing row".
3375
3430
  *
3376
3431
  * Reads filter it out on EVERY dialect from the moment it passes; the physical
3377
- * delete is a postgres-only sweep. Read the header before relying on the second
3378
- * half an expired row is INVISIBLE everywhere and still PRESENT on four of
3379
- * five dialects, which is the right trade and a surprising one.
3432
+ * delete is the retention sweep, on every dialect, after a grace of
3433
+ * `VOLTRO_EXPIRED_ROWS_TTL_HOURS` (default 24). Read the header before relying
3434
+ * on the second half an expired row is INVISIBLE everywhere and still
3435
+ * PRESENT for up to that grace, which is the right trade and a surprising one.
3380
3436
  *
3381
3437
  * Opt out for a deliberate read of expired rows the same way soft-delete does:
3382
3438
  * `ctx.store.select('inviteLinks').includeExpired()`.
@@ -3427,10 +3483,54 @@ export declare interface FallbackLogger {
3427
3483
  readonly debug: (message: string, fields?: Record<string, unknown>) => void;
3428
3484
  }
3429
3485
 
3486
+ /**
3487
+ * Where a ciphertext BELONGS — the table, the column and, when the row's
3488
+ * identity is known at write time, the row. Fed to AES-GCM as additional
3489
+ * authenticated data, so the tag covers the address as well as the bytes.
3490
+ *
3491
+ * ── The attack this closes ─────────────────────────────────────────────────
3492
+ *
3493
+ * Without it, a ciphertext is a bearer value: anyone who can WRITE the database
3494
+ * — a leaked migration credential, a compromised replica, an operator with too
3495
+ * much access — copies user A's encrypted token into user B's row, and the app,
3496
+ * acting for B, decrypts and USES A's credential. No key is needed; the app is
3497
+ * the confused deputy that holds it. The same copy across columns turns a
3498
+ * refresh token into an access token. Bound to `table:column:rowId`, the copied
3499
+ * value fails GCM authentication in its new home: the attacker needs the key to
3500
+ * re-bind it, which is the thing they did not have.
3501
+ *
3502
+ * `row` is the strong binding and `column` the fallback: a set-based write has
3503
+ * no row identity, and a table whose id the DATABASE assigns has none at insert.
3504
+ * The envelope records which one was used (`enc:v2:r:` / `enc:v2:c:`), so the
3505
+ * reader never guesses.
3506
+ */
3507
+ export declare interface FieldBinding {
3508
+ /** `table:column`. */
3509
+ readonly column: string;
3510
+ /** `table:column:rowId` — present iff the row's id is known. */
3511
+ readonly row?: string;
3512
+ }
3513
+
3514
+ /** The binding for one column of one row. `rowId` is the id as the row carries
3515
+ * it; a number is spelled the way `String()` spells it so a keyed update by
3516
+ * its string form and a read of the numeric column agree. */
3517
+ export declare const fieldBinding: (table: string, column: string, rowId?: unknown) => FieldBinding;
3518
+
3430
3519
  /** The cipher the store middleware injects. Operates on opaque strings. */
3431
3520
  export declare interface FieldCipher {
3432
- readonly encrypt: (plaintext: string) => string;
3433
- readonly decrypt: (ciphertext: string) => string;
3521
+ /**
3522
+ * With a binding: an `enc:v2:r:` (row-bound, when `binding.row` is set) or
3523
+ * `enc:v2:c:` (column-bound) envelope whose GCM tag covers the binding.
3524
+ * Without one: the unbound `enc:v1:` envelope — the raw-SQL escape hatch's
3525
+ * form, and what every row written before 0.64 carries.
3526
+ */
3527
+ readonly encrypt: (plaintext: string, binding?: FieldBinding) => string;
3528
+ /**
3529
+ * Reads every envelope. A bound one is verified against the matching member
3530
+ * of `binding` and fails when it is absent or differs — that failure IS the
3531
+ * feature, and the reader must not fall back to an unbound read.
3532
+ */
3533
+ readonly decrypt: (ciphertext: string, binding?: FieldBinding) => string;
3434
3534
  }
3435
3535
 
3436
3536
  /**
@@ -3977,6 +4077,10 @@ export declare const intersect: (...queries: ReadonlyArray<QueryWithDescriptorAn
3977
4077
  */
3978
4078
  export declare const interval: () => ColumnBuilder<string, "interval", boolean>;
3979
4079
 
4080
+ /** Is this value a BOUND ciphertext (`enc:v2:`)? An unbound `enc:v1:` value
4081
+ * reads fine and is what `voltro db encrypt-column` re-binds. */
4082
+ export declare const isBoundCiphertext: (value: unknown) => value is string;
4083
+
3980
4084
  /** Is this namespace one of ours? (so a teardown sweep never touches a real
3981
4085
  * tenant namespace). */
3982
4086
  export declare const isBranchNamespace: (namespace: string) => boolean;
@@ -4069,7 +4173,7 @@ export declare const isQueryTimeout: (dbCause: Record<string, unknown>) => boole
4069
4173
  * to normalise a column without touching rows that are already current. Answers
4070
4174
  * `undefined` when the two forms coincide (a numeric column, or a value that
4071
4175
  * parses to itself) — nothing to do either way. */
4072
- export declare const isRawEncoding: (cipher: FieldCipher, ciphertext: string, expected?: string) => boolean | undefined;
4176
+ export declare const isRawEncoding: (cipher: FieldCipher, ciphertext: string, expected?: string, binding?: FieldBinding) => boolean | undefined;
4073
4177
 
4074
4178
  /** Can THIS deployment serve the region? */
4075
4179
  export declare const isRegionServable: (region: string, config: ResidencyConfig) => boolean;
@@ -4078,6 +4182,11 @@ export declare const isRelationsSpec: (value: unknown) => value is RelationsSpec
4078
4182
 
4079
4183
  export declare const isSeedDefinition: (value: unknown) => value is SeedDefinition;
4080
4184
 
4185
+ /** True when `name` is a soft-drop snapshot (`<original>__dropped_<ts>`) left
4186
+ * behind by `VOLTRO_SOFT_DROP=1`. Applies to tables AND columns — the applier
4187
+ * uses one suffix for both. */
4188
+ export declare const isSoftDropSnapshotName: (name: string) => boolean;
4189
+
4081
4190
  /**
4082
4191
  * `turso` (the Rust rewrite of SQLite, `@voltro/sql-turso`) is
4083
4192
  * SQL-semantically identical to `sqlite` — it differs only in the connection
@@ -4144,6 +4253,13 @@ export declare const isStagingTableName: (name: string) => boolean;
4144
4253
  */
4145
4254
  export declare const isTableReactive: (tableName: string) => boolean;
4146
4255
 
4256
+ /**
4257
+ * Dialect-agnostic: does this error, anywhere down its `cause` chain, carry a
4258
+ * contention signal any supported engine emits? The mutation runner replays
4259
+ * the whole transaction on a `true`.
4260
+ */
4261
+ export declare const isTransientContention: (err: unknown) => boolean;
4262
+
4147
4263
  /** Type guard: is this descriptor a read-only view (vs a base table)? */
4148
4264
  export declare const isView: (t: TableLike) => t is View<string, Record<string, ColumnDefinition<unknown>>>;
4149
4265
 
@@ -4608,6 +4724,12 @@ export declare interface MixinOptions<Input extends FieldsInput> {
4608
4724
  readonly policies?: MixinDefinition<FieldDefinitions<Input>>['policies'];
4609
4725
  }
4610
4726
 
4727
+ /** SQL Server error numbers, as decimal strings. */
4728
+ export declare const MSSQL_TRANSIENT_NUMBERS: ReadonlySet<string>;
4729
+
4730
+ /** MySQL / MariaDB — `errno` as a decimal string, or the symbolic `code`. */
4731
+ export declare const MYSQL_TRANSIENT_CODES: ReadonlySet<string>;
4732
+
4611
4733
  /**
4612
4734
  * Type-narrowing migration marker. Tells the planner that this column
4613
4735
  * previously had type `from` and is now being narrowed/converted to its
@@ -4997,6 +5119,9 @@ export declare interface PluginRefSpec {
4997
5119
  /** The declared spec for a column builder, when it is a `pluginRef`. */
4998
5120
  export declare const pluginRefSpecOf: (column: unknown) => PluginRefSpec | undefined;
4999
5121
 
5122
+ /** Postgres SQLSTATEs. */
5123
+ export declare const POSTGRES_TRANSIENT_STATES: ReadonlySet<string>;
5124
+
5000
5125
  export declare type Predicate = PredicateLeaf | AndPredicate | OrPredicate | NotPredicate | SubqueryInPredicate | ExistsPredicate;
5001
5126
 
5002
5127
  export declare interface PredicateLeaf {
@@ -5112,6 +5237,16 @@ Joins extends JoinsMap = {}> {
5112
5237
  * SEE tombstoned rows. No-op on tables without `softDelete()`.
5113
5238
  */
5114
5239
  withDeleted: () => Query<RowOf, IxNames, Joins>;
5240
+ /**
5241
+ * Lock the matched rows until the enclosing transaction commits — the
5242
+ * read half of a read-modify-write that must not lose an update to a
5243
+ * concurrent writer. A json array appended by two requests at once keeps
5244
+ * one append without it; with it the second reader waits for the first
5245
+ * transaction and reads the row it wrote. Per dialect: `FOR UPDATE`
5246
+ * (postgres / mysql / mariadb), `WITH (UPDLOCK, ROWLOCK)` (mssql), nothing
5247
+ * on sqlite (its writers are serialised already).
5248
+ */
5249
+ forUpdate: () => Query<RowOf, IxNames, Joins>;
5115
5250
  /**
5116
5251
  * Cross-tenant: opt OUT of the runtime's automatic
5117
5252
  * `tenantId = <subject.tenantId>` scope on `tenant()` tables. Use for
@@ -5477,6 +5612,15 @@ export declare interface QueryDescriptor<R = Row> {
5477
5612
  * rows (an admin view, a grace-period check). Same shape and same posture as
5478
5613
  * `includeDeleted`. */
5479
5614
  readonly includeExpired?: boolean;
5615
+ /**
5616
+ * Lock the matched rows for the rest of the enclosing transaction — set by
5617
+ * `.forUpdate()`. `SELECT … FOR UPDATE` on postgres / mysql / mariadb,
5618
+ * `WITH (UPDLOCK, ROWLOCK)` on mssql; sqlite serialises writers already and
5619
+ * emits nothing. Outside a transaction the lock is released as the statement
5620
+ * ends, which is harmless and useless — the point is a read-modify-write
5621
+ * whose read and write share one transaction.
5622
+ */
5623
+ readonly lock?: 'update';
5480
5624
  /**
5481
5625
  * Opt OUT of the runtime's automatic tenant scope on `tenant()` tables.
5482
5626
  * Set by `.unscoped()`. Honored by the runtime, same as `includeDeleted`.
@@ -5907,25 +6051,26 @@ export declare interface RecordedWrite {
5907
6051
 
5908
6052
  export declare const recordsTable: (table: string) => boolean;
5909
6053
 
5910
- /**
5911
- * Foreign-key reference to another table's id column.
5912
- *
5913
- * Default cascade semantics: `onDelete: 'restrict'`,
5914
- * `onUpdate: 'noAction'`, `index: true`. Override per call when the
5915
- * relationship genuinely is owned-lifecycle (junction tables typically
5916
- * want `onDelete: 'cascade'`).
5917
- *
5918
- * ```ts
5919
- * userId: reference(() => users, { onDelete: 'cascade' })
5920
- * tenantId: reference(() => tenants) // restrict default
5921
- * cacheBucketId: reference(() => cacheBuckets, { index: false }) // rare opt-out
5922
- * ```
5923
- */
5924
6054
  export declare const reference: (target: () => TableLike, options?: ReferenceOptions) => ColumnBuilder<string, "reference">;
5925
6055
 
5926
6056
  export declare interface ReferenceOptions {
5927
6057
  readonly onDelete?: 'cascade' | 'restrict' | 'setNull' | 'noAction';
5928
6058
  readonly onUpdate?: 'cascade' | 'restrict' | 'setNull' | 'noAction';
6059
+ /**
6060
+ * The soft-delete counterpart of `onDelete`. A soft delete is an UPDATE that
6061
+ * sets `deletedAt`, so `ON DELETE CASCADE` never fires for it and the
6062
+ * children of a soft-deleted parent stay visible — a team's members outlive
6063
+ * the team. This is enforced by the runtime on the store's change channel:
6064
+ *
6065
+ * 'cascade' → soft-delete the referencing rows (their table must carry
6066
+ * `softDelete()` too — a hard delete of a child cannot be
6067
+ * undone when the parent is restored, so it is refused at boot)
6068
+ * 'setNull' → null the reference (the column must be `.nullable()`)
6069
+ *
6070
+ * Restoring the parent does NOT restore the children; a restore is a
6071
+ * deliberate act on one row, and the children carry their own `deletedAt`.
6072
+ */
6073
+ readonly onSoftDelete?: 'cascade' | 'setNull';
5929
6074
  /**
5930
6075
  * Auto-build a B-tree index on this column. Default `true` because
5931
6076
  * FK columns are almost always read-by. Set `false` only for tiny
@@ -6529,6 +6674,19 @@ export declare interface SchemaTable extends TableLike {
6529
6674
  readonly appliedFullText: ReadonlyArray<TableFullTextIndex>;
6530
6675
  }
6531
6676
 
6677
+ /** The longest scope key stored. A scope is an address, not a document; a
6678
+ * value longer than this is truncated, deterministically, so equal scopes
6679
+ * keep equal keys. */
6680
+ export declare const SCOPE_KEY_MAX_LENGTH = 255;
6681
+
6682
+ /**
6683
+ * The scope's key. A string scope is its own key (`'team:frontend'`); anything
6684
+ * else is canonical JSON — object keys sorted, `undefined` fields dropped — so
6685
+ * `{ team: 'a', org: 'b' }` and `{ org: 'b', team: 'a' }` address the same rows.
6686
+ * `null` / `undefined` have no key.
6687
+ */
6688
+ export declare const scopeKeyOf: (scope: unknown) => string | null;
6689
+
6532
6690
  export declare interface SeedDefinition {
6533
6691
  readonly __seed: true;
6534
6692
  readonly id: string;
@@ -6655,6 +6813,26 @@ export declare interface SeedStore {
6655
6813
  delete(table: string, primaryKey: string): Promise<boolean>;
6656
6814
  }
6657
6815
 
6816
+ /**
6817
+ * The target thunk `selfReference()` plants — `table()` swaps it for the table
6818
+ * being built. Calling it before then is a declaration error, said out loud
6819
+ * rather than returned as `undefined`.
6820
+ */
6821
+ export declare const SELF_REFERENCE_TARGET: () => TableLike;
6822
+
6823
+ /**
6824
+ * A reference to the table this column is declared IN — `parentId` on
6825
+ * `teams`, `replyToId` on `comments`.
6826
+ *
6827
+ * `reference(() => teams)` inside `teams`' own declaration is a TypeScript
6828
+ * cycle (TS7022 — the table's type depends on the column, whose type mentions
6829
+ * the table), so the honest workaround was `text()` and a foreign key the
6830
+ * schema no longer knew about. This column carries no target type, only the
6831
+ * thunk `table()` resolves to itself, so the FK, the eager loader and the
6832
+ * subject graph see it as the reference it is.
6833
+ */
6834
+ export declare const selfReference: (options?: ReferenceOptions) => ColumnBuilder<string, "reference">;
6835
+
6658
6836
  /**
6659
6837
  * Data-sensitivity classes for `.sensitive(class)`. The class picks the
6660
6838
  * format-preserving fake the export masker applies (an `'email'` becomes a
@@ -6720,6 +6898,10 @@ export declare const settleVersionedUpdate: (input: {
6720
6898
  readonly current: number | null;
6721
6899
  }) => VersionConflict | undefined;
6722
6900
 
6901
+ /** When a `<name>__dropped_<YYYYMMDDHHMMSS>` snapshot was taken (UTC), or
6902
+ * `undefined` for a name that is not one. */
6903
+ export declare const softDropSnapshotStamp: (name: string) => Date | undefined;
6904
+
6723
6905
  /**
6724
6906
  * PostGIS spatial-distance clause carried on a {@link QueryDescriptor}.
6725
6907
  * Drives a `ST_Distance` projected column (distance-as-a-value) and/or a
@@ -6824,6 +7006,9 @@ export declare interface SqlDialect {
6824
7006
  readonly retryFilter: (error: unknown) => RetryDecision;
6825
7007
  }
6826
7008
 
7009
+ /** SQLite / turso result codes — symbolic and numeric spellings. */
7010
+ export declare const SQLITE_TRANSIENT_CODES: ReadonlySet<string>;
7011
+
6827
7012
  /**
6828
7013
  * How long a staged run's silence has to last before its scratch tables are
6829
7014
  * treated as abandoned.
@@ -7387,10 +7572,11 @@ export declare interface Table<Name extends string, Fields extends Record<string
7387
7572
  * row with the same key is allowed — no hand-written
7388
7573
  * `generatedAs("CASE WHEN …")` column, no resurrection footgun.
7389
7574
  *
7390
- * **mysql / mariadb:** these engines have no partial-index support, so
7391
- * `.uniqueActive()` FAILS LOUDLY at migrate time (a full unique index would
7392
- * silently forbid re-creating a soft-deleted row). Use a generated STORED
7393
- * column + `.unique()` there until the framework lowers it for you.
7575
+ * **mysql / mariadb:** these engines have no partial-index support, so the
7576
+ * migrator LOWERS `.uniqueActive()` to a generated STORED column holding the
7577
+ * key while the predicate holds (NULL otherwise, and NULLs never collide) plus
7578
+ * a UNIQUE index on it the same guarantee, on the dialects that cannot
7579
+ * spell it directly. See `lowerUniqueActive` in `migrate.ts`.
7394
7580
  */
7395
7581
  uniqueActive: {
7396
7582
  <const F extends readonly [keyof Fields & string, ...Array<keyof Fields & string>]>(fields: F, options?: {
@@ -7655,8 +7841,9 @@ export declare interface TableIndex {
7655
7841
  * declarative planner is predicate-blind, these are emitted by the
7656
7842
  * fresh-schema DDL path (like every partial predicate) and kept OUT of the
7657
7843
  * declarative index snapshot. mysql/mariadb have no partial-index support —
7658
- * `.uniqueActive()` fails loudly there (use a generated column) rather than
7659
- * silently dropping the predicate into a full unique index.
7844
+ * `.uniqueActive()` is lowered there to a generated STORED column + UNIQUE
7845
+ * (`migrate.ts`) rather than silently dropping the predicate into a full
7846
+ * unique index.
7660
7847
  */
7661
7848
  readonly unique?: boolean;
7662
7849
  }
@@ -7835,6 +8022,9 @@ export declare interface TransactionalViewHandle {
7835
8022
  */
7836
8023
  export declare type TransactionConnectionContext = Context.Tag.Service<typeof TransactionConnection>;
7837
8024
 
8025
+ /** Turso's MVCC conflicts arrive as messages, not codes. */
8026
+ export declare const TURSO_TRANSIENT_MESSAGE: RegExp;
8027
+
7838
8028
  /** Append one row inside the caller's transaction. Insert-only by design. */
7839
8029
  export declare type TxnAppend = (table: string, row: Row) => Promise<void>;
7840
8030