@voltro/database 0.69.1 → 0.71.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.
@@ -800,11 +800,11 @@ var me = () => new s({
800
800
  }, qe = (e) => e === "sqlite" || e === "turso", Je = (e) => e === "mysql" || e === "mariadb", R = (e, t) => {
801
801
  switch (t) {
802
802
  case "mysql":
803
- case "mariadb": return `\`${e}\``;
804
- case "mssql": return `[${e}]`;
803
+ case "mariadb": return `\`${e.replace(/`/g, "``")}\``;
804
+ case "mssql": return `[${e.replace(/\]/g, "]]")}]`;
805
805
  case "postgres":
806
806
  case "sqlite":
807
- case "turso": return `"${e}"`;
807
+ case "turso": return `"${e.replace(/"/g, "\"\"")}"`;
808
808
  }
809
809
  }, Ye = 1e4, z = Symbol.for("@voltro/database/tableRegistry"), B = globalThis, V = B[z] ?? (B[z] = /* @__PURE__ */ new Map()), H = (e) => {
810
810
  let t = V.get(e.tableName);
package/dist/index.d.ts CHANGED
@@ -1825,6 +1825,9 @@ export declare const columnsPerRow: (rows: ReadonlyArray<Record<string, unknown>
1825
1825
 
1826
1826
  export declare type ColumnType = 'id' | 'text' | 'integer' | 'real' | 'decimal' | 'bigint' | 'boolean' | 'timestamp' | 'date' | 'json' | 'bytes' | 'reference' | 'vector' | 'enum' | 'array' | 'interval' | 'raw';
1827
1827
 
1828
+ /** Exact comparison of fixed-point decimal strings, without a floating-point conversion. */
1829
+ export declare const compareDecimal: (left: unknown, right: unknown) => number | null;
1830
+
1828
1831
  /**
1829
1832
  * Try to compile a `QueryDescriptor` with `eager` populated into a
1830
1833
  * single-roundtrip JSON-agg query for the given dialect.
@@ -2392,12 +2395,28 @@ export declare interface DataStore {
2392
2395
  insertIgnore(table: string, row: Row, options: {
2393
2396
  conflictColumns: ReadonlyArray<string>;
2394
2397
  }): Promise<Row>;
2398
+ /**
2399
+ * `insertIgnore`, plus the one thing it cannot report: whether the row was
2400
+ * INSERTED or already there.
2401
+ *
2402
+ * The store's actual insert decision supplies the outcome, including for
2403
+ * caller-supplied and database-generated IDs. A pre-read or comparison of
2404
+ * IDs cannot establish which concurrent caller inserted. Every raw store,
2405
+ * namespace view and transaction view implements this method; wrappers
2406
+ * preserve its outcome while encoding/decoding the row. Inside a transaction
2407
+ * the result is provisional until commit. The typed helper is
2408
+ * `insertIgnoreRowOutcome`.
2409
+ */
2410
+ insertIgnoreWithOutcome(table: string, row: Row, options: {
2411
+ conflictColumns: ReadonlyArray<string>;
2412
+ }): Promise<InsertIgnoreResult>;
2395
2413
  /**
2396
2414
  * Run `work` inside a transaction. ALL `insert/update/delete` calls
2397
2415
  * made via `work`'s store reference (passed as argument) are:
2398
2416
  *
2399
2417
  * - atomic — the dialect's native BEGIN/COMMIT/ROLLBACK wraps the
2400
- * writes; the memory store serialises trivially.
2418
+ * writes; memory validates snapshot read/write-table revisions before
2419
+ * committing and retries conflicting attempts with a fresh snapshot.
2401
2420
  * - delta-deferred — ChangeEvents queue inside the transaction and
2402
2421
  * are emitted ONLY after a successful commit. A throw triggers
2403
2422
  * rollback AND drops every queued event so no subscriber ever
@@ -2405,6 +2424,10 @@ export declare interface DataStore {
2405
2424
  *
2406
2425
  * Reads (`query()`) inside the transaction see the in-flight write
2407
2426
  * set (read-your-own-writes within the transaction).
2427
+ * Transient contention can replay the whole callback (at most four attempts,
2428
+ * exponential delay starting at 10 ms). Do not perform external side effects
2429
+ * in the callback. Memory validation is table-granular and process-local;
2430
+ * `forUpdate` does not acquire a blocking row lock there.
2408
2431
  *
2409
2432
  * `transactional()` MUST NOT be nested — nested invocations throw to
2410
2433
  * surface design mistakes (transactions are atomic units, not
@@ -3368,7 +3391,7 @@ declare type EmptyMerge = unknown;
3368
3391
  export declare const encodeFieldValue: (cipher: FieldCipher, value: unknown, binding?: FieldBinding) => string;
3369
3392
 
3370
3393
  /**
3371
- * Serialize a row's json()/array() cells to JSON strings so they can be
3394
+ * Serialize selected json()/array()/vector() cells to JSON strings so they can be
3372
3395
  * bound as scalar SQL parameters. Symmetric to `decodeRowsFromSchema`.
3373
3396
  * No-op for tables with no json/array columns (cost: one map build +
3374
3397
  * a per-cell type lookup). Unknown tables pass through unchanged.
@@ -3438,7 +3461,9 @@ export declare const eq: <RowOf = Record<string, unknown>, K extends keyof RowOf
3438
3461
  * explicit `ESCAPE '\'` clause at the call site. */
3439
3462
  export declare const escapeLike: (value: string) => string;
3440
3463
 
3441
- export declare const evaluatePredicate: (predicate: Predicate, row: Row | null, subqueries?: SubqueryResults | null) => boolean;
3464
+ export declare const evaluatePredicate: (predicate: Predicate, row: Row | null, subqueries?: SubqueryResults | null, columns?: Readonly<Record<string, {
3465
+ readonly type: string;
3466
+ }>>) => boolean;
3442
3467
 
3443
3468
  /** `EXCEPT` — rows in the FIRST input not present in any subsequent input. */
3444
3469
  export declare const except: (...queries: ReadonlyArray<QueryWithDescriptorAny>) => Query<Row, string>;
@@ -3726,7 +3751,7 @@ export declare const formatSubjectGraph: (graph: SubjectGraph) => string;
3726
3751
  */
3727
3752
  export declare const FRAMEWORK_LIVE_TABLES: ReadonlySet<string>;
3728
3753
 
3729
- export declare const frameworkTables: readonly [TableLike, TableLike, TableLike];
3754
+ export declare const frameworkTables: readonly [SchemaTable, SchemaTable, SchemaTable];
3730
3755
 
3731
3756
  declare type FullTextIndexMeta = {
3732
3757
  readonly name: string;
@@ -4003,14 +4028,14 @@ export declare interface IndexKindOptions {
4003
4028
  * - `'inner'` → `vector_ip_ops` (negative inner product)
4004
4029
  * The opclass MUST match the metric the query orders by, or the
4005
4030
  * index can't accelerate the search. Set `opclass` directly only to
4006
- * override the mapping (e.g. `halfvec_cosine_ops`).
4031
+ * override the mapping. Half-precision columns select `halfvec_*` automatically.
4007
4032
  */
4008
4033
  readonly distance?: VectorDistance;
4009
4034
  /**
4010
4035
  * pgvector opclass token spliced into `USING hnsw (col <opclass>)`.
4011
- * When unset, derived from `distance` (default `vector_cosine_ops`).
4012
- * Set explicitly to override (e.g. `'halfvec_cosine_ops'` for a
4013
- * `vector(n, { precision: 'half' })` column).
4036
+ * When unset, derived from `distance` and the column's precision
4037
+ * (default `vector_cosine_ops`, or `halfvec_cosine_ops` for half precision).
4038
+ * An explicit override is an unquoted, optionally schema-qualified identifier.
4014
4039
  */
4015
4040
  readonly opclass?: string;
4016
4041
  };
@@ -4071,6 +4096,36 @@ export declare type InferViewRow<V> = V extends View<string, infer F> ? InferRow
4071
4096
  */
4072
4097
  export declare const INITIAL_VERSION = 1;
4073
4098
 
4099
+ /** What an `insertIgnoreWithOutcome` did, beside the row it hands back. */
4100
+ export declare interface InsertIgnoreResult {
4101
+ readonly row: Row;
4102
+ /** `'ignored'` means a row already existed for the conflict key; it is the
4103
+ * row returned, untouched. */
4104
+ readonly outcome: 'inserted' | 'ignored';
4105
+ }
4106
+
4107
+ /**
4108
+ * `insertIgnore`, plus whether the row was INSERTED or was already there.
4109
+ *
4110
+ * `insertIgnore` returns the existing row on the ignore path, and that row is
4111
+ * indistinguishable from one it just wrote. The native store's insert
4112
+ * decision supplies the outcome; wrappers preserve it without a pre-read.
4113
+ *
4114
+ * const { row, outcome } = await insertIgnoreRowOutcome(ctx.store, actors, actor, {
4115
+ * conflictColumns: ['id'],
4116
+ * })
4117
+ * if (outcome === 'inserted') planted++
4118
+ *
4119
+ * Caller-supplied and database-assigned IDs obey the same contract. Inside
4120
+ * a transaction the decision becomes durable only when that transaction commits.
4121
+ */
4122
+ export declare const insertIgnoreRowOutcome: <T extends AnyTable>(store: TypedInsertIgnoreOutcomeStore, table: T, row: InferInsertRow<T>, options: {
4123
+ readonly conflictColumns: ReadonlyArray<keyof InferRow<T> & string>;
4124
+ }) => Promise<{
4125
+ readonly row: InferRow<T>;
4126
+ readonly outcome: "inserted" | "ignored";
4127
+ }>;
4128
+
4074
4129
  /**
4075
4130
  * Insert MANY rows, typed against the table — `insertRow`'s batch, and nothing
4076
4131
  * else: it forwards to `store.insertMany` unchanged, so the chunking, the
@@ -6011,9 +6066,8 @@ declare interface QueryWithDescriptorAny {
6011
6066
  * in ANSI_QUOTES mode)
6012
6067
  * - mssql: `[name]` (T-SQL bracketed identifier)
6013
6068
  *
6014
- * Identifier names in the schema DSL are restricted to
6015
- * `[a-zA-Z][a-zA-Z0-9_]*`, so the inner quote character never needs
6016
- * escaping — but the dialect MUST match the engine to avoid syntax errors.
6069
+ * Catalog-derived identifiers can contain quote characters even when the
6070
+ * schema DSL restricts its own names. Escape them at this shared boundary.
6017
6071
  */
6018
6072
  export declare const quoteIdent: (name: string, dialect: DialectId) => string;
6019
6073
 
@@ -8144,6 +8198,9 @@ export declare interface TransactionalViewHandle {
8144
8198
  */
8145
8199
  export declare type TransactionConnectionContext = Context.Tag.Service<typeof TransactionConnection>;
8146
8200
 
8201
+ /** Shared bounded contention policy, including stores without a SQL connection. */
8202
+ export declare const transactionRetrySchedule: (isRetryable: (error: unknown) => boolean) => Schedule.Schedule<number, unknown, never>;
8203
+
8147
8204
  /** Turso's MVCC conflicts arrive as messages, not codes. */
8148
8205
  export declare const TURSO_TRANSIENT_MESSAGE: RegExp;
8149
8206
 
@@ -8167,6 +8224,16 @@ export declare interface TxnRecorderPort {
8167
8224
  readonly maxOf: TxnMaxOf;
8168
8225
  }
8169
8226
 
8227
+ /** The minimal store surface `insertIgnoreRowOutcome` needs. */
8228
+ export declare interface TypedInsertIgnoreOutcomeStore {
8229
+ readonly insertIgnoreWithOutcome: (table: string, row: Row, options: {
8230
+ readonly conflictColumns: ReadonlyArray<string>;
8231
+ }) => Promise<{
8232
+ readonly row: Row;
8233
+ readonly outcome: 'inserted' | 'ignored';
8234
+ }>;
8235
+ }
8236
+
8170
8237
  /** The minimal store surface `insertManyRows` needs. */
8171
8238
  export declare interface TypedInsertManyStore {
8172
8239
  readonly insertMany: (table: string, rows: ReadonlyArray<Row>) => Promise<ReadonlyArray<Row>>;
@@ -8598,7 +8665,7 @@ export declare const VOLTRO_LOCAL_FIRST_MIXIN_ID: "voltro/localFirst";
8598
8665
  * app (empty unless `apiKeys` is enabled in app.config). See
8599
8666
  * `@voltro/runtime`'s `ApiKeyService`.
8600
8667
  */
8601
- export declare const _voltroApiKeysTable: TableLike;
8668
+ export declare const _voltroApiKeysTable: SchemaTable;
8602
8669
 
8603
8670
  export declare const _voltroCdcOffsetsTable: TableLike;
8604
8671
 
@@ -8632,7 +8699,7 @@ export declare const _voltroDataTransfersTable: TableLike;
8632
8699
  * a record dropped while still inside that window lets the duplicate request it
8633
8700
  * exists to stop execute a second time.
8634
8701
  */
8635
- export declare const _voltroIdempotencyTable: TableLike;
8702
+ export declare const _voltroIdempotencyTable: SchemaTable;
8636
8703
 
8637
8704
  /**
8638
8705
  * `_voltro_kv` — the durable key-value store behind `ctx.kv` / the `Kv`
@@ -8643,7 +8710,7 @@ export declare const _voltroIdempotencyTable: TableLike;
8643
8710
  * evicted for capacity — they live until deleted or their TTL lapses. Created
8644
8711
  * for every sql app (empty unless `ctx.kv` is used).
8645
8712
  */
8646
- export declare const _voltroKvTable: TableLike;
8713
+ export declare const _voltroKvTable: SchemaTable;
8647
8714
 
8648
8715
  /**
8649
8716
  * `_voltro_migration_plans` — one row per declarative-diff plan
@@ -8661,7 +8728,7 @@ export declare const _voltroKvTable: TableLike;
8661
8728
  * the escape-hatch for split/merge/data-move migrations the diff can't
8662
8729
  * infer.
8663
8730
  */
8664
- export declare const _voltroMigrationPlansTable: TableLike;
8731
+ export declare const _voltroMigrationPlansTable: SchemaTable;
8665
8732
 
8666
8733
  /**
8667
8734
  * `_voltro_migrations` — one row per migration file. Created by the
@@ -8669,7 +8736,7 @@ export declare const _voltroMigrationPlansTable: TableLike;
8669
8736
  * as each migration's lifecycle progresses (pending → running → applied
8670
8737
  * | failed | reverted).
8671
8738
  */
8672
- export declare const _voltroMigrationsTable: TableLike;
8739
+ export declare const _voltroMigrationsTable: SchemaTable;
8673
8740
 
8674
8741
  export declare const _voltroMssqlCdcOffsetsTable: TableLike;
8675
8742
 
@@ -8718,7 +8785,7 @@ export declare const _voltroReplaceInProgressTable: TableLike;
8718
8785
  * writing a full subscription list here every heartbeat would be write
8719
8786
  * amplification for state nobody is reading.
8720
8787
  */
8721
- export declare const _voltroReplicaObservationsTable: TableLike;
8788
+ export declare const _voltroReplicaObservationsTable: SchemaTable;
8722
8789
 
8723
8790
  /**
8724
8791
  * `_voltro_seeds` — one row per registered seed. Written to by the
@@ -8726,7 +8793,7 @@ export declare const _voltroReplicaObservationsTable: TableLike;
8726
8793
  * seeds re-run when the fingerprint changes; the `lastRun*` fields
8727
8794
  * capture the most recent execution (history lives in the audit log).
8728
8795
  */
8729
- export declare const _voltroSeedsTable: TableLike;
8796
+ export declare const _voltroSeedsTable: SchemaTable;
8730
8797
 
8731
8798
  export declare const walkLeaves: (predicate: Predicate, conjunctive?: boolean) => Generator<PredicateLeafWithContext>;
8732
8799