@vertz/db 0.2.12 → 0.2.14

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.js CHANGED
@@ -1,14 +1,10 @@
1
1
  import {
2
- PostgresDialect,
3
- SqliteDialect,
4
2
  buildDelete,
5
3
  buildInsert,
6
4
  buildSelect,
7
5
  buildUpdate,
8
- buildWhere,
9
- defaultPostgresDialect,
10
- defaultSqliteDialect
11
- } from "./shared/chunk-0e1vy9qd.js";
6
+ buildWhere
7
+ } from "./shared/chunk-pxjcpnpx.js";
12
8
  import {
13
9
  CheckConstraintError,
14
10
  ConnectionError,
@@ -18,23 +14,31 @@ import {
18
14
  NotFoundError,
19
15
  NotNullError,
20
16
  UniqueConstraintError,
17
+ computeChecksum,
18
+ computeDiff,
19
+ createMigrationRunner,
21
20
  executeQuery,
21
+ generateMigrationSql,
22
22
  getAutoUpdateColumns,
23
23
  getPrimaryKeyColumns,
24
24
  getReadOnlyColumns,
25
25
  getTimestampColumns,
26
26
  mapRow,
27
27
  mapRows,
28
+ parseMigrationName,
28
29
  parsePgError,
29
- resolveSelectColumns
30
- } from "./shared/chunk-agyds4jw.js";
30
+ resolveSelectColumns,
31
+ validateIndexes
32
+ } from "./shared/chunk-1x9z0p5e.js";
31
33
  import"./shared/chunk-kb4tnn2k.js";
32
34
  import {
33
- camelToSnake
34
- } from "./shared/chunk-v2qm94qp.js";
35
- import {
36
- sha256Hex
37
- } from "./shared/chunk-ssga2xea.js";
35
+ PostgresDialect,
36
+ SqliteDialect,
37
+ camelToSnake,
38
+ defaultPostgresDialect,
39
+ defaultSqliteDialect
40
+ } from "./shared/chunk-8v70x3hq.js";
41
+ import"./shared/chunk-ssga2xea.js";
38
42
  import {
39
43
  diagnoseError,
40
44
  explainError,
@@ -43,10 +47,10 @@ import {
43
47
  import {
44
48
  createD1Adapter,
45
49
  createD1Driver
46
- } from "./shared/chunk-pnk6yzjv.js";
50
+ } from "./shared/chunk-ndxe1h28.js";
47
51
  import {
48
52
  generateId
49
- } from "./shared/chunk-sfmyxz6r.js";
53
+ } from "./shared/chunk-pkv8w501.js";
50
54
  // src/adapters/database-bridge-adapter.ts
51
55
  function createDatabaseBridgeAdapter(db, tableName) {
52
56
  const delegate = db[tableName];
@@ -98,537 +102,6 @@ function createDatabaseBridgeAdapter(db, tableName) {
98
102
  }
99
103
  };
100
104
  }
101
- // src/migration/auto-migrate.ts
102
- import { isErr } from "@vertz/errors";
103
-
104
- // src/migration/differ.ts
105
- function columnSimilarity(a, b) {
106
- let score = 0;
107
- let total = 0;
108
- total += 3;
109
- if (a.type === b.type)
110
- score += 3;
111
- total += 1;
112
- if (a.nullable === b.nullable)
113
- score += 1;
114
- total += 1;
115
- if (a.primary === b.primary)
116
- score += 1;
117
- total += 1;
118
- if (a.unique === b.unique)
119
- score += 1;
120
- return score / total;
121
- }
122
- function indexKey(columns) {
123
- return columns.join(",");
124
- }
125
- function computeDiff(before, after) {
126
- const changes = [];
127
- for (const tableName of Object.keys(after.tables)) {
128
- if (!(tableName in before.tables)) {
129
- changes.push({ type: "table_added", table: tableName });
130
- }
131
- }
132
- for (const tableName of Object.keys(before.tables)) {
133
- if (!(tableName in after.tables)) {
134
- changes.push({ type: "table_removed", table: tableName });
135
- }
136
- }
137
- for (const tableName of Object.keys(after.tables)) {
138
- if (!(tableName in before.tables))
139
- continue;
140
- const beforeTable = before.tables[tableName];
141
- const afterTable = after.tables[tableName];
142
- if (!beforeTable || !afterTable)
143
- continue;
144
- const removedCols = Object.keys(beforeTable.columns).filter((c) => !(c in afterTable.columns));
145
- const addedCols = Object.keys(afterTable.columns).filter((c) => !(c in beforeTable.columns));
146
- const renames = [];
147
- const matchedRemoved = new Set;
148
- const matchedAdded = new Set;
149
- for (const removed of removedCols) {
150
- const removedSnap = beforeTable.columns[removed];
151
- if (!removedSnap)
152
- continue;
153
- let bestMatch = null;
154
- let bestScore = 0;
155
- for (const added of addedCols) {
156
- if (matchedAdded.has(added))
157
- continue;
158
- const addedSnap = afterTable.columns[added];
159
- if (!addedSnap)
160
- continue;
161
- const score = columnSimilarity(removedSnap, addedSnap);
162
- if (score > bestScore) {
163
- bestScore = score;
164
- bestMatch = added;
165
- }
166
- }
167
- if (bestMatch && bestScore >= 0.7) {
168
- renames.push({ oldCol: removed, newCol: bestMatch, confidence: bestScore });
169
- matchedRemoved.add(removed);
170
- matchedAdded.add(bestMatch);
171
- }
172
- }
173
- for (const rename of renames) {
174
- changes.push({
175
- type: "column_renamed",
176
- table: tableName,
177
- oldColumn: rename.oldCol,
178
- newColumn: rename.newCol,
179
- confidence: rename.confidence
180
- });
181
- }
182
- for (const colName of addedCols) {
183
- if (!matchedAdded.has(colName)) {
184
- changes.push({ type: "column_added", table: tableName, column: colName });
185
- }
186
- }
187
- for (const colName of removedCols) {
188
- if (!matchedRemoved.has(colName)) {
189
- changes.push({ type: "column_removed", table: tableName, column: colName });
190
- }
191
- }
192
- for (const colName of Object.keys(afterTable.columns)) {
193
- if (!(colName in beforeTable.columns))
194
- continue;
195
- const beforeCol = beforeTable.columns[colName];
196
- const afterCol = afterTable.columns[colName];
197
- if (!beforeCol || !afterCol)
198
- continue;
199
- if (beforeCol.type !== afterCol.type || beforeCol.nullable !== afterCol.nullable || beforeCol.default !== afterCol.default) {
200
- const change = {
201
- type: "column_altered",
202
- table: tableName,
203
- column: colName
204
- };
205
- if (beforeCol.type !== afterCol.type) {
206
- change.oldType = beforeCol.type;
207
- change.newType = afterCol.type;
208
- }
209
- if (beforeCol.nullable !== afterCol.nullable) {
210
- change.oldNullable = beforeCol.nullable;
211
- change.newNullable = afterCol.nullable;
212
- }
213
- if (beforeCol.default !== afterCol.default) {
214
- change.oldDefault = beforeCol.default;
215
- change.newDefault = afterCol.default;
216
- }
217
- changes.push(change);
218
- }
219
- }
220
- const beforeIndexKeys = new Set(beforeTable.indexes.map((i) => indexKey(i.columns)));
221
- const afterIndexKeys = new Set(afterTable.indexes.map((i) => indexKey(i.columns)));
222
- for (const idx of afterTable.indexes) {
223
- const key = indexKey(idx.columns);
224
- if (!beforeIndexKeys.has(key)) {
225
- changes.push({ type: "index_added", table: tableName, columns: [...idx.columns] });
226
- }
227
- }
228
- for (const idx of beforeTable.indexes) {
229
- const key = indexKey(idx.columns);
230
- if (!afterIndexKeys.has(key)) {
231
- changes.push({ type: "index_removed", table: tableName, columns: [...idx.columns] });
232
- }
233
- }
234
- }
235
- for (const enumName of Object.keys(after.enums)) {
236
- if (!(enumName in before.enums)) {
237
- changes.push({ type: "enum_added", enumName });
238
- }
239
- }
240
- for (const enumName of Object.keys(before.enums)) {
241
- if (!(enumName in after.enums)) {
242
- changes.push({ type: "enum_removed", enumName });
243
- }
244
- }
245
- for (const enumName of Object.keys(after.enums)) {
246
- if (!(enumName in before.enums))
247
- continue;
248
- const beforeVals = before.enums[enumName];
249
- const afterVals = after.enums[enumName];
250
- if (!beforeVals || !afterVals)
251
- continue;
252
- const beforeSet = new Set(beforeVals);
253
- const afterSet = new Set(afterVals);
254
- const addedValues = afterVals.filter((v) => !beforeSet.has(v));
255
- const removedValues = beforeVals.filter((v) => !afterSet.has(v));
256
- if (addedValues.length > 0 || removedValues.length > 0) {
257
- changes.push({ type: "enum_altered", enumName, addedValues, removedValues });
258
- }
259
- }
260
- return { changes };
261
- }
262
-
263
- // src/migration/runner.ts
264
- import {
265
- createMigrationQueryError,
266
- err,
267
- ok
268
- } from "@vertz/errors";
269
- var HISTORY_TABLE = "_vertz_migrations";
270
- function buildCreateHistorySql(dialect) {
271
- if (dialect.name === "sqlite") {
272
- return `
273
- CREATE TABLE IF NOT EXISTS "${HISTORY_TABLE}" (
274
- "id" INTEGER PRIMARY KEY AUTOINCREMENT,
275
- "name" TEXT NOT NULL UNIQUE,
276
- "checksum" TEXT NOT NULL,
277
- "applied_at" TEXT NOT NULL DEFAULT (datetime('now'))
278
- );
279
- `;
280
- }
281
- return `
282
- CREATE TABLE IF NOT EXISTS "${HISTORY_TABLE}" (
283
- "id" serial PRIMARY KEY,
284
- "name" text NOT NULL UNIQUE,
285
- "checksum" text NOT NULL,
286
- "applied_at" timestamp with time zone NOT NULL DEFAULT now()
287
- );
288
- `;
289
- }
290
- async function computeChecksum(sql) {
291
- return sha256Hex(sql);
292
- }
293
- function parseMigrationName(filename) {
294
- const match = filename.match(/^(\d+)_(.+)\.sql$/);
295
- if (!match?.[1] || !match[2])
296
- return null;
297
- return {
298
- timestamp: Number.parseInt(match[1], 10),
299
- name: filename
300
- };
301
- }
302
- function createMigrationRunner(options) {
303
- const dialect = options?.dialect ?? defaultPostgresDialect;
304
- const createHistorySql = buildCreateHistorySql(dialect);
305
- return {
306
- async createHistoryTable(queryFn) {
307
- try {
308
- await queryFn(createHistorySql, []);
309
- return ok(undefined);
310
- } catch (cause) {
311
- return err(createMigrationQueryError("Failed to create migration history table", {
312
- sql: createHistorySql,
313
- cause
314
- }));
315
- }
316
- },
317
- async apply(queryFn, sql, name, options2) {
318
- const checksum = await computeChecksum(sql);
319
- const recordSql = `INSERT INTO "${HISTORY_TABLE}" ("name", "checksum") VALUES (${dialect.param(1)}, ${dialect.param(2)})`;
320
- const statements = [sql, recordSql];
321
- if (options2?.dryRun) {
322
- return ok({
323
- name,
324
- sql,
325
- checksum,
326
- dryRun: true,
327
- statements
328
- });
329
- }
330
- try {
331
- await queryFn(sql, []);
332
- await queryFn(recordSql, [name, checksum]);
333
- return ok({
334
- name,
335
- sql,
336
- checksum,
337
- dryRun: false,
338
- statements
339
- });
340
- } catch (cause) {
341
- return err(createMigrationQueryError(`Failed to apply migration: ${name}`, {
342
- sql,
343
- cause
344
- }));
345
- }
346
- },
347
- async getApplied(queryFn) {
348
- try {
349
- const result = await queryFn(`SELECT "name", "checksum", "applied_at" FROM "${HISTORY_TABLE}" ORDER BY "id" ASC`, []);
350
- return ok(result.rows.map((row) => ({
351
- name: row.name,
352
- checksum: row.checksum,
353
- appliedAt: new Date(row.applied_at)
354
- })));
355
- } catch (cause) {
356
- return err(createMigrationQueryError("Failed to retrieve applied migrations", {
357
- cause
358
- }));
359
- }
360
- },
361
- getPending(files, applied) {
362
- const appliedNames = new Set(applied.map((a) => a.name));
363
- return files.filter((f) => !appliedNames.has(f.name)).sort((a, b) => a.timestamp - b.timestamp);
364
- },
365
- async detectDrift(files, applied) {
366
- const drifted = [];
367
- const appliedMap = new Map(applied.map((a) => [a.name, a.checksum]));
368
- for (const file of files) {
369
- const appliedChecksum = appliedMap.get(file.name);
370
- if (appliedChecksum && appliedChecksum !== await computeChecksum(file.sql)) {
371
- drifted.push(file.name);
372
- }
373
- }
374
- return drifted;
375
- },
376
- detectOutOfOrder(files, applied) {
377
- if (applied.length === 0)
378
- return [];
379
- const appliedNames = new Set(applied.map((a) => a.name));
380
- const lastApplied = applied[applied.length - 1];
381
- if (!lastApplied)
382
- return [];
383
- const lastAppliedFile = files.find((f) => f.name === lastApplied.name);
384
- if (!lastAppliedFile)
385
- return [];
386
- return files.filter((f) => !appliedNames.has(f.name) && f.timestamp < lastAppliedFile.timestamp).map((f) => f.name);
387
- }
388
- };
389
- }
390
-
391
- // src/migration/sql-generator.ts
392
- function escapeSqlString(value) {
393
- return value.replace(/'/g, "''");
394
- }
395
- function isEnumType(col, enums) {
396
- const typeLower = col.type.toLowerCase();
397
- if (enums) {
398
- for (const enumName of Object.keys(enums)) {
399
- if (typeLower === enumName.toLowerCase() || typeLower === enumName) {
400
- return true;
401
- }
402
- }
403
- }
404
- return false;
405
- }
406
- function getEnumValues(col, enums) {
407
- if (!enums)
408
- return;
409
- for (const [enumName, values] of Object.entries(enums)) {
410
- if (col.type.toLowerCase() === enumName.toLowerCase() || col.type === enumName) {
411
- return values;
412
- }
413
- }
414
- return;
415
- }
416
- function columnDef(name, col, dialect, enums) {
417
- const snakeName = camelToSnake(name);
418
- const isEnum = isEnumType(col, enums);
419
- let sqlType;
420
- let checkConstraint;
421
- if (isEnum && dialect.name === "sqlite") {
422
- sqlType = dialect.mapColumnType("text");
423
- const enumValues = getEnumValues(col, enums);
424
- if (enumValues && enumValues.length > 0) {
425
- const escapedValues = enumValues.map((v) => `'${escapeSqlString(v)}'`).join(", ");
426
- checkConstraint = `CHECK("${snakeName}" IN (${escapedValues}))`;
427
- }
428
- } else {
429
- if (dialect.name === "postgres" && col.type === col.type.toLowerCase()) {
430
- sqlType = col.type;
431
- } else {
432
- const normalizedType = normalizeColumnType(col.type);
433
- sqlType = dialect.mapColumnType(normalizedType);
434
- }
435
- }
436
- const parts = [`"${snakeName}" ${sqlType}`];
437
- if (checkConstraint) {
438
- parts.push(checkConstraint);
439
- }
440
- if (!col.nullable) {
441
- parts.push("NOT NULL");
442
- }
443
- if (col.unique) {
444
- parts.push("UNIQUE");
445
- }
446
- if (col.default !== undefined) {
447
- parts.push(`DEFAULT ${col.default}`);
448
- }
449
- return parts.join(" ");
450
- }
451
- function normalizeColumnType(type) {
452
- const typeUpper = type.toUpperCase();
453
- const typeMap = {
454
- UUID: "uuid",
455
- TEXT: "text",
456
- INTEGER: "integer",
457
- SERIAL: "serial",
458
- BOOLEAN: "boolean",
459
- TIMESTAMPTZ: "timestamp",
460
- TIMESTAMP: "timestamp",
461
- "DOUBLE PRECISION": "float",
462
- JSONB: "json",
463
- JSON: "json",
464
- NUMERIC: "decimal",
465
- REAL: "float",
466
- VARCHAR: "varchar"
467
- };
468
- return typeMap[typeUpper] || type.toLowerCase();
469
- }
470
- function generateMigrationSql(changes, ctx, dialect = defaultPostgresDialect) {
471
- const statements = [];
472
- const tables = ctx?.tables;
473
- const enums = ctx?.enums;
474
- for (const change of changes) {
475
- switch (change.type) {
476
- case "enum_added": {
477
- if (!change.enumName)
478
- break;
479
- if (dialect.name === "postgres") {
480
- const values = enums?.[change.enumName];
481
- if (!values || values.length === 0)
482
- break;
483
- const enumSnakeName = camelToSnake(change.enumName);
484
- const valuesStr = values.map((v) => `'${escapeSqlString(v)}'`).join(", ");
485
- statements.push(`CREATE TYPE "${enumSnakeName}" AS ENUM (${valuesStr});`);
486
- }
487
- break;
488
- }
489
- case "table_added": {
490
- if (!change.table)
491
- break;
492
- const table = tables?.[change.table];
493
- if (!table)
494
- break;
495
- const tableName = camelToSnake(change.table);
496
- const cols = [];
497
- const primaryKeys = [];
498
- if (dialect.name === "postgres" && enums) {
499
- for (const [, col] of Object.entries(table.columns)) {
500
- if (isEnumType(col, enums)) {
501
- const enumValues = getEnumValues(col, enums);
502
- if (enumValues && enumValues.length > 0) {
503
- const enumSnakeName = camelToSnake(col.type);
504
- const alreadyEmitted = statements.some((s) => s.includes(`CREATE TYPE "${enumSnakeName}"`));
505
- if (!alreadyEmitted) {
506
- const valuesStr = enumValues.map((v) => `'${escapeSqlString(v)}'`).join(", ");
507
- statements.push(`CREATE TYPE "${enumSnakeName}" AS ENUM (${valuesStr});`);
508
- }
509
- }
510
- }
511
- }
512
- }
513
- for (const [colName, col] of Object.entries(table.columns)) {
514
- cols.push(` ${columnDef(colName, col, dialect, enums)}`);
515
- if (col.primary) {
516
- primaryKeys.push(`"${camelToSnake(colName)}"`);
517
- }
518
- }
519
- if (primaryKeys.length > 0) {
520
- cols.push(` PRIMARY KEY (${primaryKeys.join(", ")})`);
521
- }
522
- for (const fk of table.foreignKeys) {
523
- const fkCol = camelToSnake(fk.column);
524
- const fkTarget = camelToSnake(fk.targetTable);
525
- const fkTargetCol = camelToSnake(fk.targetColumn);
526
- cols.push(` FOREIGN KEY ("${fkCol}") REFERENCES "${fkTarget}" ("${fkTargetCol}")`);
527
- }
528
- statements.push(`CREATE TABLE "${tableName}" (
529
- ${cols.join(`,
530
- `)}
531
- );`);
532
- for (const idx of table.indexes) {
533
- const idxCols = idx.columns.map((c) => `"${camelToSnake(c)}"`).join(", ");
534
- const idxName = `idx_${tableName}_${idx.columns.map((c) => camelToSnake(c)).join("_")}`;
535
- statements.push(`CREATE INDEX "${idxName}" ON "${tableName}" (${idxCols});`);
536
- }
537
- break;
538
- }
539
- case "table_removed": {
540
- if (!change.table)
541
- break;
542
- statements.push(`DROP TABLE "${camelToSnake(change.table)}";`);
543
- break;
544
- }
545
- case "column_added": {
546
- if (!change.table || !change.column)
547
- break;
548
- const col = tables?.[change.table]?.columns[change.column];
549
- if (!col)
550
- break;
551
- statements.push(`ALTER TABLE "${camelToSnake(change.table)}" ADD COLUMN ${columnDef(change.column, col, dialect, enums)};`);
552
- break;
553
- }
554
- case "column_removed": {
555
- if (!change.table || !change.column)
556
- break;
557
- statements.push(`ALTER TABLE "${camelToSnake(change.table)}" DROP COLUMN "${camelToSnake(change.column)}";`);
558
- break;
559
- }
560
- case "column_altered": {
561
- if (!change.table || !change.column)
562
- break;
563
- const snakeTable = camelToSnake(change.table);
564
- const snakeCol = camelToSnake(change.column);
565
- if (change.newType !== undefined) {
566
- statements.push(`ALTER TABLE "${snakeTable}" ALTER COLUMN "${snakeCol}" TYPE ${change.newType};`);
567
- }
568
- if (change.newNullable !== undefined) {
569
- if (change.newNullable) {
570
- statements.push(`ALTER TABLE "${snakeTable}" ALTER COLUMN "${snakeCol}" DROP NOT NULL;`);
571
- } else {
572
- statements.push(`ALTER TABLE "${snakeTable}" ALTER COLUMN "${snakeCol}" SET NOT NULL;`);
573
- }
574
- }
575
- if (change.newDefault !== undefined) {
576
- if (change.newDefault) {
577
- statements.push(`ALTER TABLE "${snakeTable}" ALTER COLUMN "${snakeCol}" SET DEFAULT ${change.newDefault};`);
578
- } else {
579
- statements.push(`ALTER TABLE "${snakeTable}" ALTER COLUMN "${snakeCol}" DROP DEFAULT;`);
580
- }
581
- }
582
- break;
583
- }
584
- case "column_renamed": {
585
- if (!change.table || !change.oldColumn || !change.newColumn)
586
- break;
587
- statements.push(`ALTER TABLE "${camelToSnake(change.table)}" RENAME COLUMN "${camelToSnake(change.oldColumn)}" TO "${camelToSnake(change.newColumn)}";`);
588
- break;
589
- }
590
- case "index_added": {
591
- if (!change.table || !change.columns)
592
- break;
593
- const snakeTable = camelToSnake(change.table);
594
- const idxCols = change.columns.map((c) => `"${camelToSnake(c)}"`).join(", ");
595
- const idxName = `idx_${snakeTable}_${change.columns.map((c) => camelToSnake(c)).join("_")}`;
596
- statements.push(`CREATE INDEX "${idxName}" ON "${snakeTable}" (${idxCols});`);
597
- break;
598
- }
599
- case "index_removed": {
600
- if (!change.table || !change.columns)
601
- break;
602
- const snakeTable = camelToSnake(change.table);
603
- const idxName = `idx_${snakeTable}_${change.columns.map((c) => camelToSnake(c)).join("_")}`;
604
- statements.push(`DROP INDEX "${idxName}";`);
605
- break;
606
- }
607
- case "enum_removed": {
608
- if (!change.enumName)
609
- break;
610
- if (dialect.name === "postgres") {
611
- statements.push(`DROP TYPE "${camelToSnake(change.enumName)}";`);
612
- }
613
- break;
614
- }
615
- case "enum_altered": {
616
- if (!change.enumName || !change.addedValues)
617
- break;
618
- if (dialect.name === "postgres") {
619
- const enumSnakeName = camelToSnake(change.enumName);
620
- for (const val of change.addedValues) {
621
- statements.push(`ALTER TYPE "${enumSnakeName}" ADD VALUE '${escapeSqlString(val)}';`);
622
- }
623
- }
624
- break;
625
- }
626
- }
627
- }
628
- return statements.join(`
629
-
630
- `);
631
- }
632
105
  // src/migration/files.ts
633
106
  function formatMigrationFilename(num, description) {
634
107
  return `${String(num).padStart(4, "0")}_${description}.sql`;
@@ -690,6 +163,13 @@ async function introspectSqlite(queryFn) {
690
163
  columns[colName] = colSnap;
691
164
  }
692
165
  const indexes = [];
166
+ const { rows: indexSqlRows } = await queryFn(`SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ?`, [tableName]);
167
+ const indexSqlMap = new Map;
168
+ for (const row2 of indexSqlRows) {
169
+ if (row2.name && row2.sql) {
170
+ indexSqlMap.set(row2.name, row2.sql);
171
+ }
172
+ }
693
173
  const { rows: indexRows } = await queryFn(`PRAGMA index_list("${validateIdentifier(tableName)}")`, []);
694
174
  for (const idx of indexRows) {
695
175
  const idxName = idx.name;
@@ -704,11 +184,19 @@ async function introspectSqlite(queryFn) {
704
184
  }
705
185
  }
706
186
  if (origin === "c") {
707
- indexes.push({
187
+ const snap = {
708
188
  columns: idxColumns,
709
189
  name: idxName,
710
190
  unique: isUnique
711
- });
191
+ };
192
+ const createSql = indexSqlMap.get(idxName);
193
+ if (createSql) {
194
+ const whereMatch = createSql.match(/\bWHERE\s+(.+)$/i);
195
+ if (whereMatch?.[1]) {
196
+ snap.where = whereMatch[1];
197
+ }
198
+ }
199
+ indexes.push(snap);
712
200
  }
713
201
  }
714
202
  const foreignKeys = [];
@@ -819,24 +307,36 @@ async function introspectPostgres(queryFn) {
819
307
  const indexes = [];
820
308
  const { rows: idxRows } = await queryFn(`SELECT i.relname AS index_name,
821
309
  array_agg(a.attname ORDER BY k.n) AS columns,
822
- ix.indisunique AS is_unique
310
+ ix.indisunique AS is_unique,
311
+ am.amname AS access_method,
312
+ pg_get_expr(ix.indpred, ix.indrelid) AS predicate
823
313
  FROM pg_index ix
824
314
  JOIN pg_class i ON i.oid = ix.indexrelid
825
315
  JOIN pg_class t ON t.oid = ix.indrelid
826
316
  JOIN pg_namespace ns ON ns.oid = t.relnamespace
317
+ JOIN pg_am am ON am.oid = i.relam
827
318
  JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, n) ON true
828
319
  JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
829
320
  WHERE t.relname = $1
830
321
  AND ns.nspname = 'public'
831
322
  AND NOT ix.indisprimary
832
323
  AND NOT ix.indisunique
833
- GROUP BY i.relname, ix.indisunique`, [tableName]);
324
+ GROUP BY i.relname, ix.indisunique, am.amname, ix.indpred, ix.indrelid`, [tableName]);
834
325
  for (const idx of idxRows) {
835
- indexes.push({
326
+ const snap = {
836
327
  columns: idx.columns,
837
328
  name: idx.index_name,
838
329
  unique: idx.is_unique
839
- });
330
+ };
331
+ const accessMethod = idx.access_method;
332
+ if (accessMethod && accessMethod !== "btree") {
333
+ snap.type = accessMethod;
334
+ }
335
+ const predicate = idx.predicate;
336
+ if (predicate) {
337
+ snap.where = predicate;
338
+ }
339
+ indexes.push(snap);
840
340
  }
841
341
  snapshot.tables[tableName] = {
842
342
  columns,
@@ -917,15 +417,57 @@ function detectCollisions(journal, existingFiles) {
917
417
  return collisions;
918
418
  }
919
419
  // src/migration/snapshot.ts
920
- function createSnapshot(tables) {
420
+ function isModelDef(v) {
421
+ return v !== null && typeof v === "object" && "table" in v && "relations" in v && typeof v.table === "object";
422
+ }
423
+ function resolveTable(entry) {
424
+ return isModelDef(entry) ? entry.table : entry;
425
+ }
426
+ function resolveRelations(entry) {
427
+ return isModelDef(entry) ? entry.relations : {};
428
+ }
429
+ function findPkColumn(table) {
430
+ for (const [colName, col] of Object.entries(table._columns)) {
431
+ if (col._meta.primary)
432
+ return colName;
433
+ }
434
+ throw new Error(`Table "${table._name}" has no primary key column`);
435
+ }
436
+ function deriveForeignKeys(table, relations) {
437
+ const foreignKeys = [];
438
+ for (const [relName, rel] of Object.entries(relations)) {
439
+ if (rel._type !== "one")
440
+ continue;
441
+ if (!rel._foreignKey)
442
+ continue;
443
+ if (!(rel._foreignKey in table._columns)) {
444
+ throw new Error(`Relation "${relName}" on table "${table._name}" references column "${rel._foreignKey}" which does not exist`);
445
+ }
446
+ const targetTable = rel._target();
447
+ let targetColumn;
448
+ try {
449
+ targetColumn = findPkColumn(targetTable);
450
+ } catch {
451
+ throw new Error(`Target table "${targetTable._name}" referenced by relation "${relName}" on table "${table._name}" has no primary key column`);
452
+ }
453
+ foreignKeys.push({
454
+ column: rel._foreignKey,
455
+ targetTable: targetTable._name,
456
+ targetColumn
457
+ });
458
+ }
459
+ return foreignKeys;
460
+ }
461
+ function createSnapshot(entries) {
921
462
  const snapshot = {
922
463
  version: 1,
923
464
  tables: {},
924
465
  enums: {}
925
466
  };
926
- for (const table of tables) {
467
+ for (const entry of entries) {
468
+ const table = resolveTable(entry);
469
+ const relations = resolveRelations(entry);
927
470
  const columns = {};
928
- const foreignKeys = [];
929
471
  const indexes = [];
930
472
  for (const [colName, col] of Object.entries(table._columns)) {
931
473
  const meta = col._meta;
@@ -944,20 +486,23 @@ function createSnapshot(tables) {
944
486
  colSnap.annotations = annotationNames;
945
487
  }
946
488
  columns[colName] = colSnap;
947
- if (meta.references) {
948
- foreignKeys.push({
949
- column: colName,
950
- targetTable: meta.references.table,
951
- targetColumn: meta.references.column
952
- });
953
- }
954
489
  if (meta.enumName && meta.enumValues) {
955
490
  snapshot.enums[meta.enumName] = [...meta.enumValues];
956
491
  }
957
492
  }
958
493
  for (const idx of table._indexes) {
959
- indexes.push({ columns: [...idx.columns] });
960
- }
494
+ const snap = { columns: [...idx.columns] };
495
+ if (idx.name)
496
+ snap.name = idx.name;
497
+ if (idx.unique)
498
+ snap.unique = idx.unique;
499
+ if (idx.type)
500
+ snap.type = idx.type;
501
+ if (idx.where)
502
+ snap.where = idx.where;
503
+ indexes.push(snap);
504
+ }
505
+ const foreignKeys = deriveForeignKeys(table, relations);
961
506
  snapshot.tables[table._name] = {
962
507
  columns,
963
508
  indexes,
@@ -1169,8 +714,8 @@ async function push(options) {
1169
714
  return { sql, tablesAffected };
1170
715
  }
1171
716
  // src/cli/reset.ts
1172
- import { createMigrationQueryError as createMigrationQueryError2, err as err2 } from "@vertz/errors";
1173
- var HISTORY_TABLE2 = "_vertz_migrations";
717
+ import { createMigrationQueryError, err } from "@vertz/errors";
718
+ var HISTORY_TABLE = "_vertz_migrations";
1174
719
  function getUserTablesQuery(dialect) {
1175
720
  if (dialect.name === "sqlite") {
1176
721
  return `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`;
@@ -1191,23 +736,23 @@ async function reset(options) {
1191
736
  const tablesResult = await options.queryFn(getUserTablesQuery(dialect), []);
1192
737
  tableNames = tablesResult.rows.map((row) => row.name);
1193
738
  } catch (cause) {
1194
- return err2(createMigrationQueryError2("Failed to list user tables", { cause }));
739
+ return err(createMigrationQueryError("Failed to list user tables", { cause }));
1195
740
  }
1196
741
  const tablesDropped = [];
1197
742
  for (const tableName of tableNames) {
1198
743
  try {
1199
744
  await options.queryFn(buildDropTableSql(tableName, dialect), []);
1200
- if (tableName !== HISTORY_TABLE2) {
745
+ if (tableName !== HISTORY_TABLE) {
1201
746
  tablesDropped.push(tableName);
1202
747
  }
1203
748
  } catch (cause) {
1204
- return err2(createMigrationQueryError2(`Failed to drop table: ${tableName}`, { cause }));
749
+ return err(createMigrationQueryError(`Failed to drop table: ${tableName}`, { cause }));
1205
750
  }
1206
751
  }
1207
752
  try {
1208
- await options.queryFn(buildDropTableSql(HISTORY_TABLE2, dialect), []);
753
+ await options.queryFn(buildDropTableSql(HISTORY_TABLE, dialect), []);
1209
754
  } catch (cause) {
1210
- return err2(createMigrationQueryError2("Failed to drop history table", { cause }));
755
+ return err(createMigrationQueryError("Failed to drop history table", { cause }));
1211
756
  }
1212
757
  const createResult = await runner.createHistoryTable(options.queryFn);
1213
758
  if (!createResult.ok) {
@@ -1405,7 +950,7 @@ async function migrateStatus(options) {
1405
950
  };
1406
951
  }
1407
952
  // src/client/database.ts
1408
- import { err as err3, ok as ok2 } from "@vertz/schema";
953
+ import { err as err2, ok } from "@vertz/schema";
1409
954
  // src/errors/error-codes.ts
1410
955
  var DbErrorCode = {
1411
956
  UNIQUE_VIOLATION: "23505",
@@ -2404,18 +1949,21 @@ function computeTenantGraph(registry) {
2404
1949
  shared.push(key);
2405
1950
  continue;
2406
1951
  }
2407
- const columns = entry.table._columns;
2408
- for (const colKey of Object.keys(columns)) {
2409
- const col = columns[colKey];
2410
- if (col._meta.isTenant && col._meta.references) {
2411
- if (!directlyScoped.includes(key)) {
2412
- directlyScoped.push(key);
2413
- }
2414
- const rootTableName = col._meta.references.table;
2415
- const rootKey = tableNameToKey.get(rootTableName);
2416
- if (rootKey && root === null) {
2417
- root = rootKey;
1952
+ if (entry._tenant) {
1953
+ const tenantRel = entry.relations[entry._tenant];
1954
+ if (!tenantRel) {
1955
+ throw new Error(`Model "${key}": tenant relation "${entry._tenant}" not found in relations`);
1956
+ }
1957
+ if (!directlyScoped.includes(key)) {
1958
+ directlyScoped.push(key);
1959
+ }
1960
+ const rootTableName = tenantRel._target()._name;
1961
+ const rootKey = tableNameToKey.get(rootTableName);
1962
+ if (rootKey) {
1963
+ if (root !== null && root !== rootKey) {
1964
+ throw new Error(`Conflicting tenant roots: "${root}" and "${rootKey}". All tenant declarations must point to the same root table.`);
2418
1965
  }
1966
+ root = rootKey;
2419
1967
  }
2420
1968
  }
2421
1969
  }
@@ -2441,18 +1989,14 @@ function computeTenantGraph(registry) {
2441
1989
  if (key === root || directlyScoped.includes(key) || shared.includes(key) || indirectlyScopedNames.has(entry.table._name)) {
2442
1990
  continue;
2443
1991
  }
2444
- const columns = entry.table._columns;
2445
- for (const colKey of Object.keys(columns)) {
2446
- const col = columns[colKey];
2447
- if (col._meta.references && !col._meta.isTenant) {
2448
- const refTable = col._meta.references.table;
2449
- if (scopedTableNames.has(refTable) || indirectlyScopedNames.has(refTable)) {
2450
- indirectlyScoped.push(key);
2451
- indirectlyScopedNames.add(entry.table._name);
2452
- scopedTableNames.add(entry.table._name);
2453
- changed = true;
2454
- break;
2455
- }
1992
+ for (const rel of Object.values(entry.relations)) {
1993
+ const targetTableName = rel._target()._name;
1994
+ if (scopedTableNames.has(targetTableName) || indirectlyScopedNames.has(targetTableName)) {
1995
+ indirectlyScoped.push(key);
1996
+ indirectlyScopedNames.add(entry.table._name);
1997
+ scopedTableNames.add(entry.table._name);
1998
+ changed = true;
1999
+ break;
2456
2000
  }
2457
2001
  }
2458
2002
  }
@@ -2591,8 +2135,8 @@ function createDb(options) {
2591
2135
  replicaIndex = (replicaIndex + 1) % replicaDrivers.length;
2592
2136
  try {
2593
2137
  return await targetReplica.queryFn(sqlStr, params);
2594
- } catch (err4) {
2595
- console.warn("[vertz/db] replica query failed, falling back to primary:", err4.message);
2138
+ } catch (err3) {
2139
+ console.warn("[vertz/db] replica query failed, falling back to primary:", err3.message);
2596
2140
  }
2597
2141
  }
2598
2142
  if (!driver) {
@@ -2612,11 +2156,11 @@ function createDb(options) {
2612
2156
  const result = await get(queryFn, entry.table, opts, dialectObj);
2613
2157
  if (result !== null && opts?.include) {
2614
2158
  const rows = await loadRelations(queryFn, [result], entry.relations, opts.include, 0, modelsRegistry, entry.table);
2615
- return ok2(rows[0] ?? null);
2159
+ return ok(rows[0] ?? null);
2616
2160
  }
2617
- return ok2(result);
2161
+ return ok(result);
2618
2162
  } catch (e) {
2619
- return err3(toReadError(e));
2163
+ return err2(toReadError(e));
2620
2164
  }
2621
2165
  })();
2622
2166
  }
@@ -2626,7 +2170,7 @@ function createDb(options) {
2626
2170
  const entry = resolveModel(models, name);
2627
2171
  const result = await get(queryFn, entry.table, opts, dialectObj);
2628
2172
  if (result === null) {
2629
- return err3({
2173
+ return err2({
2630
2174
  code: "NotFound",
2631
2175
  message: `Record not found in table ${name}`,
2632
2176
  table: name
@@ -2634,11 +2178,11 @@ function createDb(options) {
2634
2178
  }
2635
2179
  if (opts?.include) {
2636
2180
  const rows = await loadRelations(queryFn, [result], entry.relations, opts.include, 0, modelsRegistry, entry.table);
2637
- return ok2(rows[0]);
2181
+ return ok(rows[0]);
2638
2182
  }
2639
- return ok2(result);
2183
+ return ok(result);
2640
2184
  } catch (e) {
2641
- return err3(toReadError(e));
2185
+ return err2(toReadError(e));
2642
2186
  }
2643
2187
  })();
2644
2188
  }
@@ -2649,11 +2193,11 @@ function createDb(options) {
2649
2193
  const results = await list(queryFn, entry.table, opts, dialectObj);
2650
2194
  if (opts?.include && results.length > 0) {
2651
2195
  const withRelations = await loadRelations(queryFn, results, entry.relations, opts.include, 0, modelsRegistry, entry.table);
2652
- return ok2(withRelations);
2196
+ return ok(withRelations);
2653
2197
  }
2654
- return ok2(results);
2198
+ return ok(results);
2655
2199
  } catch (e) {
2656
- return err3(toReadError(e));
2200
+ return err2(toReadError(e));
2657
2201
  }
2658
2202
  })();
2659
2203
  }
@@ -2664,11 +2208,11 @@ function createDb(options) {
2664
2208
  const { data, total } = await listAndCount(queryFn, entry.table, opts, dialectObj);
2665
2209
  if (opts?.include && data.length > 0) {
2666
2210
  const withRelations = await loadRelations(queryFn, data, entry.relations, opts.include, 0, modelsRegistry, entry.table);
2667
- return ok2({ data: withRelations, total });
2211
+ return ok({ data: withRelations, total });
2668
2212
  }
2669
- return ok2({ data, total });
2213
+ return ok({ data, total });
2670
2214
  } catch (e) {
2671
- return err3(toReadError(e));
2215
+ return err2(toReadError(e));
2672
2216
  }
2673
2217
  })();
2674
2218
  }
@@ -2677,9 +2221,9 @@ function createDb(options) {
2677
2221
  try {
2678
2222
  const entry = resolveModel(models, name);
2679
2223
  const result = await create(queryFn, entry.table, opts, dialectObj);
2680
- return ok2(result);
2224
+ return ok(result);
2681
2225
  } catch (e) {
2682
- return err3(toWriteError(e));
2226
+ return err2(toWriteError(e));
2683
2227
  }
2684
2228
  })();
2685
2229
  }
@@ -2688,9 +2232,9 @@ function createDb(options) {
2688
2232
  try {
2689
2233
  const entry = resolveModel(models, name);
2690
2234
  const result = await createMany(queryFn, entry.table, opts, dialectObj);
2691
- return ok2(result);
2235
+ return ok(result);
2692
2236
  } catch (e) {
2693
- return err3(toWriteError(e));
2237
+ return err2(toWriteError(e));
2694
2238
  }
2695
2239
  })();
2696
2240
  }
@@ -2699,9 +2243,9 @@ function createDb(options) {
2699
2243
  try {
2700
2244
  const entry = resolveModel(models, name);
2701
2245
  const result = await createManyAndReturn(queryFn, entry.table, opts, dialectObj);
2702
- return ok2(result);
2246
+ return ok(result);
2703
2247
  } catch (e) {
2704
- return err3(toWriteError(e));
2248
+ return err2(toWriteError(e));
2705
2249
  }
2706
2250
  })();
2707
2251
  }
@@ -2710,9 +2254,9 @@ function createDb(options) {
2710
2254
  try {
2711
2255
  const entry = resolveModel(models, name);
2712
2256
  const result = await update(queryFn, entry.table, opts, dialectObj);
2713
- return ok2(result);
2257
+ return ok(result);
2714
2258
  } catch (e) {
2715
- return err3(toWriteError(e));
2259
+ return err2(toWriteError(e));
2716
2260
  }
2717
2261
  })();
2718
2262
  }
@@ -2721,9 +2265,9 @@ function createDb(options) {
2721
2265
  try {
2722
2266
  const entry = resolveModel(models, name);
2723
2267
  const result = await updateMany(queryFn, entry.table, opts, dialectObj);
2724
- return ok2(result);
2268
+ return ok(result);
2725
2269
  } catch (e) {
2726
- return err3(toWriteError(e));
2270
+ return err2(toWriteError(e));
2727
2271
  }
2728
2272
  })();
2729
2273
  }
@@ -2732,9 +2276,9 @@ function createDb(options) {
2732
2276
  try {
2733
2277
  const entry = resolveModel(models, name);
2734
2278
  const result = await upsert(queryFn, entry.table, opts, dialectObj);
2735
- return ok2(result);
2279
+ return ok(result);
2736
2280
  } catch (e) {
2737
- return err3(toWriteError(e));
2281
+ return err2(toWriteError(e));
2738
2282
  }
2739
2283
  })();
2740
2284
  }
@@ -2743,9 +2287,9 @@ function createDb(options) {
2743
2287
  try {
2744
2288
  const entry = resolveModel(models, name);
2745
2289
  const result = await deleteOne(queryFn, entry.table, opts, dialectObj);
2746
- return ok2(result);
2290
+ return ok(result);
2747
2291
  } catch (e) {
2748
- return err3(toWriteError(e));
2292
+ return err2(toWriteError(e));
2749
2293
  }
2750
2294
  })();
2751
2295
  }
@@ -2754,9 +2298,9 @@ function createDb(options) {
2754
2298
  try {
2755
2299
  const entry = resolveModel(models, name);
2756
2300
  const result = await deleteMany(queryFn, entry.table, opts, dialectObj);
2757
- return ok2(result);
2301
+ return ok(result);
2758
2302
  } catch (e) {
2759
- return err3(toWriteError(e));
2303
+ return err2(toWriteError(e));
2760
2304
  }
2761
2305
  })();
2762
2306
  }
@@ -2765,9 +2309,9 @@ function createDb(options) {
2765
2309
  try {
2766
2310
  const entry = resolveModel(models, name);
2767
2311
  const result = await count(queryFn, entry.table, opts);
2768
- return ok2(result);
2312
+ return ok(result);
2769
2313
  } catch (e) {
2770
- return err3(toReadError(e));
2314
+ return err2(toReadError(e));
2771
2315
  }
2772
2316
  })();
2773
2317
  }
@@ -2776,9 +2320,9 @@ function createDb(options) {
2776
2320
  try {
2777
2321
  const entry = resolveModel(models, name);
2778
2322
  const result = await aggregate(queryFn, entry.table, opts);
2779
- return ok2(result);
2323
+ return ok(result);
2780
2324
  } catch (e) {
2781
- return err3(toReadError(e));
2325
+ return err2(toReadError(e));
2782
2326
  }
2783
2327
  })();
2784
2328
  }
@@ -2787,9 +2331,9 @@ function createDb(options) {
2787
2331
  try {
2788
2332
  const entry = resolveModel(models, name);
2789
2333
  const result = await groupBy(queryFn, entry.table, opts);
2790
- return ok2(result);
2334
+ return ok(result);
2791
2335
  } catch (e) {
2792
- return err3(toReadError(e));
2336
+ return err2(toReadError(e));
2793
2337
  }
2794
2338
  })();
2795
2339
  }
@@ -2816,9 +2360,9 @@ function createDb(options) {
2816
2360
  client.query = async (fragment) => {
2817
2361
  try {
2818
2362
  const result = await executeQuery(queryFn, fragment.sql, fragment.params);
2819
- return ok2(result);
2363
+ return ok(result);
2820
2364
  } catch (e) {
2821
- return err3(toReadError(e, fragment.sql));
2365
+ return err2(toReadError(e, fragment.sql));
2822
2366
  }
2823
2367
  };
2824
2368
  client.close = async () => {
@@ -2883,11 +2427,6 @@ function createColumnWithMeta(meta) {
2883
2427
  },
2884
2428
  check(sql) {
2885
2429
  return cloneWith(this, { check: sql });
2886
- },
2887
- references(table, column) {
2888
- return cloneWith(this, {
2889
- references: { table, column: column ?? "id" }
2890
- });
2891
2430
  }
2892
2431
  };
2893
2432
  return col;
@@ -2902,8 +2441,6 @@ function defaultMeta(sqlType) {
2902
2441
  _annotations: {},
2903
2442
  isReadOnly: false,
2904
2443
  isAutoUpdate: false,
2905
- isTenant: false,
2906
- references: null,
2907
2444
  check: null
2908
2445
  };
2909
2446
  }
@@ -2923,23 +2460,6 @@ function createSerialColumn() {
2923
2460
  _annotations: {},
2924
2461
  isReadOnly: false,
2925
2462
  isAutoUpdate: false,
2926
- isTenant: false,
2927
- references: null,
2928
- check: null
2929
- });
2930
- }
2931
- function createTenantColumn(targetTableName) {
2932
- return createColumnWithMeta({
2933
- sqlType: "uuid",
2934
- primary: false,
2935
- unique: false,
2936
- nullable: false,
2937
- hasDefault: false,
2938
- _annotations: {},
2939
- isReadOnly: false,
2940
- isAutoUpdate: false,
2941
- isTenant: true,
2942
- references: { table: targetTableName, column: "id" },
2943
2463
  check: null
2944
2464
  });
2945
2465
  }
@@ -3022,11 +2542,12 @@ function getColumnNamesWithAnnotation(table, annotation) {
3022
2542
  }
3023
2543
 
3024
2544
  // src/schema/model.ts
3025
- function createModel(table, relations) {
2545
+ function createModel(table, relations, options) {
3026
2546
  return {
3027
2547
  table,
3028
2548
  relations: relations ?? {},
3029
- schemas: deriveSchemas(table)
2549
+ schemas: deriveSchemas(table),
2550
+ _tenant: options?.tenant ?? null
3030
2551
  };
3031
2552
  }
3032
2553
 
@@ -3061,7 +2582,11 @@ function createManyRelation(target, foreignKey) {
3061
2582
  }
3062
2583
 
3063
2584
  // src/schema/table.ts
2585
+ var DANGEROUS_SQL_PATTERN = /;|--|\b(DROP|DELETE|INSERT|UPDATE|ALTER|CREATE|EXEC)\b/i;
3064
2586
  function createIndex(columns, options) {
2587
+ if (options?.where && DANGEROUS_SQL_PATTERN.test(options.where)) {
2588
+ throw new Error(`Unsafe WHERE clause expression in index: "${options.where}"`);
2589
+ }
3065
2590
  return {
3066
2591
  columns: Array.isArray(columns) ? columns : [columns],
3067
2592
  ...options
@@ -3136,9 +2661,8 @@ var d = {
3136
2661
  enumValues: values
3137
2662
  });
3138
2663
  },
3139
- tenant: (targetTable) => createTenantColumn(targetTable._name),
3140
2664
  table: (name, columns, options) => createTable(name, columns, options),
3141
- index: (columns) => createIndex(columns),
2665
+ index: (columns, options) => createIndex(columns, options),
3142
2666
  ref: {
3143
2667
  one: (target, foreignKey) => createOneRelation(target, foreignKey),
3144
2668
  many: (target, foreignKey) => createManyRelation(target, foreignKey)
@@ -3147,7 +2671,7 @@ var d = {
3147
2671
  table,
3148
2672
  relations
3149
2673
  }),
3150
- model: (table, relations = {}) => createModel(table, relations)
2674
+ model: (table, relations = {}, options) => createModel(table, relations, options)
3151
2675
  };
3152
2676
  // src/schema/define-annotations.ts
3153
2677
  function defineAnnotations(...annotations) {
@@ -3192,6 +2716,7 @@ function createRegistry(tables, relationsCallback) {
3192
2716
  return result;
3193
2717
  }
3194
2718
  export {
2719
+ validateIndexes,
3195
2720
  toWriteError,
3196
2721
  toReadError,
3197
2722
  resolveErrorCode,