@apibara/plugin-drizzle 2.1.0-beta.2 → 2.1.0-beta.20

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.cjs CHANGED
@@ -4,10 +4,21 @@ const indexer = require('@apibara/indexer');
4
4
  const plugins = require('@apibara/indexer/plugins');
5
5
  const internal = require('@apibara/indexer/internal');
6
6
  const plugins$1 = require('@apibara/indexer/internal/plugins');
7
+ const constants = require('./shared/plugin-drizzle.cae20704.cjs');
8
+ const pglite$1 = require('@electric-sql/pglite');
9
+ const nodePostgres = require('drizzle-orm/node-postgres');
10
+ const migrator$1 = require('drizzle-orm/node-postgres/migrator');
11
+ const pglite = require('drizzle-orm/pglite');
12
+ const migrator = require('drizzle-orm/pglite/migrator');
13
+ const pg = require('pg');
7
14
  const protocol = require('@apibara/protocol');
8
15
  const drizzleOrm = require('drizzle-orm');
9
16
  const pgCore = require('drizzle-orm/pg-core');
10
17
 
18
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
19
+
20
+ const pg__default = /*#__PURE__*/_interopDefaultCompat(pg);
21
+
11
22
  class DrizzleStorageError extends Error {
12
23
  constructor(message, options) {
13
24
  super(message, options);
@@ -36,15 +47,63 @@ function sleep(ms) {
36
47
  return new Promise((resolve) => setTimeout(resolve, ms));
37
48
  }
38
49
 
39
- const CHECKPOINTS_TABLE_NAME = "__indexer_checkpoints";
40
- const FILTERS_TABLE_NAME = "__indexer_filters";
41
- const SCHEMA_VERSION_TABLE_NAME = "__indexer_schema_version";
42
- const checkpoints = pgCore.pgTable(CHECKPOINTS_TABLE_NAME, {
50
+ function drizzle(options) {
51
+ const {
52
+ connectionString = process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://",
53
+ schema,
54
+ type = "pglite",
55
+ config,
56
+ poolConfig
57
+ } = options ?? {};
58
+ if (isPostgresConnectionString(connectionString) || type === "node-postgres") {
59
+ const pool = new pg__default.Pool({
60
+ connectionString,
61
+ ...poolConfig || {}
62
+ });
63
+ return nodePostgres.drizzle(pool, { schema, ...config || {} });
64
+ }
65
+ if (type === "pglite") {
66
+ return pglite.drizzle({
67
+ schema,
68
+ connection: {
69
+ dataDir: connectionString || "memory://pglite"
70
+ },
71
+ ...config || {}
72
+ });
73
+ }
74
+ throw new Error("Invalid database type");
75
+ }
76
+ async function migrate(db, options) {
77
+ const isPglite = !!("$client" in db && db.$client instanceof pglite$1.PGlite);
78
+ try {
79
+ if (isPglite) {
80
+ await migrator.migrate(db, options);
81
+ } else {
82
+ await migrator$1.migrate(db, options);
83
+ }
84
+ } catch (error) {
85
+ throw new DrizzleStorageError(
86
+ "Failed to apply migrations! Please check if you have generated migrations using drizzle:generate",
87
+ {
88
+ cause: error
89
+ }
90
+ );
91
+ }
92
+ }
93
+ function isPostgresConnectionString(conn) {
94
+ return conn.startsWith("postgres://") || conn.startsWith("postgresql://");
95
+ }
96
+
97
+ const CHECKPOINTS_TABLE_NAME = "checkpoints";
98
+ const FILTERS_TABLE_NAME = "filters";
99
+ const SCHEMA_VERSION_TABLE_NAME = "schema_version";
100
+ const schema$1 = pgCore.pgSchema(constants.SCHEMA_NAME);
101
+ const checkpoints = schema$1.table(CHECKPOINTS_TABLE_NAME, {
43
102
  id: pgCore.text("id").notNull().primaryKey(),
44
103
  orderKey: pgCore.integer("order_key").notNull(),
45
104
  uniqueKey: pgCore.text("unique_key")
46
105
  });
47
- const filters = pgCore.pgTable(
106
+ const filters = schema$1.table(
48
107
  FILTERS_TABLE_NAME,
49
108
  {
50
109
  id: pgCore.text("id").notNull(),
@@ -58,7 +117,7 @@ const filters = pgCore.pgTable(
58
117
  }
59
118
  ]
60
119
  );
61
- const schemaVersion = pgCore.pgTable(SCHEMA_VERSION_TABLE_NAME, {
120
+ const schemaVersion = schema$1.table(SCHEMA_VERSION_TABLE_NAME, {
62
121
  k: pgCore.integer("k").notNull().primaryKey(),
63
122
  version: pgCore.integer("version").notNull()
64
123
  });
@@ -69,12 +128,19 @@ const MIGRATIONS = [
69
128
  // Add more migration arrays for future versions
70
129
  ];
71
130
  async function initializePersistentState(tx) {
72
- await tx.execute(`
73
- CREATE TABLE IF NOT EXISTS ${SCHEMA_VERSION_TABLE_NAME} (
131
+ await tx.execute(
132
+ drizzleOrm.sql.raw(`
133
+ CREATE SCHEMA IF NOT EXISTS ${constants.SCHEMA_NAME};
134
+ `)
135
+ );
136
+ await tx.execute(
137
+ drizzleOrm.sql.raw(`
138
+ CREATE TABLE IF NOT EXISTS ${constants.SCHEMA_NAME}.${SCHEMA_VERSION_TABLE_NAME} (
74
139
  k INTEGER PRIMARY KEY,
75
140
  version INTEGER NOT NULL
76
141
  );
77
- `);
142
+ `)
143
+ );
78
144
  const versionRows = await tx.select().from(schemaVersion).where(drizzleOrm.eq(schemaVersion.k, 0));
79
145
  const storedVersion = versionRows[0]?.version ?? -1;
80
146
  if (storedVersion > CURRENT_SCHEMA_VERSION) {
@@ -84,22 +150,26 @@ async function initializePersistentState(tx) {
84
150
  }
85
151
  try {
86
152
  if (storedVersion === -1) {
87
- await tx.execute(`
88
- CREATE TABLE IF NOT EXISTS ${CHECKPOINTS_TABLE_NAME} (
153
+ await tx.execute(
154
+ drizzleOrm.sql.raw(`
155
+ CREATE TABLE IF NOT EXISTS ${constants.SCHEMA_NAME}.${CHECKPOINTS_TABLE_NAME} (
89
156
  id TEXT PRIMARY KEY,
90
157
  order_key INTEGER NOT NULL,
91
158
  unique_key TEXT
92
159
  );
93
- `);
94
- await tx.execute(`
95
- CREATE TABLE IF NOT EXISTS ${FILTERS_TABLE_NAME} (
160
+ `)
161
+ );
162
+ await tx.execute(
163
+ drizzleOrm.sql.raw(`
164
+ CREATE TABLE IF NOT EXISTS ${constants.SCHEMA_NAME}.${FILTERS_TABLE_NAME} (
96
165
  id TEXT NOT NULL,
97
166
  filter TEXT NOT NULL,
98
167
  from_block INTEGER NOT NULL,
99
168
  to_block INTEGER DEFAULT NULL,
100
169
  PRIMARY KEY (id, from_block)
101
170
  );
102
- `);
171
+ `)
172
+ );
103
173
  await tx.insert(schemaVersion).values({
104
174
  k: 0,
105
175
  version: CURRENT_SCHEMA_VERSION
@@ -134,7 +204,9 @@ async function persistState(props) {
134
204
  target: checkpoints.id,
135
205
  set: {
136
206
  orderKey: Number(endCursor.orderKey),
137
- uniqueKey: endCursor.uniqueKey
207
+ // Explicitly set the unique key to `null` to indicate that it has been deleted
208
+ // Otherwise drizzle will not update its value.
209
+ uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null
138
210
  }
139
211
  });
140
212
  if (filter) {
@@ -213,11 +285,24 @@ async function finalizeState(props) {
213
285
  });
214
286
  }
215
287
  }
288
+ async function resetPersistence(props) {
289
+ const { tx, indexerId } = props;
290
+ try {
291
+ await tx.delete(checkpoints).where(drizzleOrm.eq(checkpoints.id, indexerId));
292
+ await tx.delete(filters).where(drizzleOrm.eq(filters.id, indexerId));
293
+ } catch (error) {
294
+ throw new DrizzleStorageError("Failed to reset persistence state", {
295
+ cause: error
296
+ });
297
+ }
298
+ }
216
299
 
300
+ const ROLLBACK_TABLE_NAME = "reorg_rollback";
301
+ const schema = pgCore.pgSchema(constants.SCHEMA_NAME);
217
302
  function getReorgTriggerName(table, indexerId) {
218
303
  return `${table}_reorg_${indexerId}`;
219
304
  }
220
- pgCore.pgTable("__reorg_rollback", {
305
+ schema.table(ROLLBACK_TABLE_NAME, {
221
306
  n: pgCore.serial("n").primaryKey(),
222
307
  op: pgCore.char("op", { length: 1 }).$type().notNull(),
223
308
  table_name: pgCore.text("table_name").notNull(),
@@ -228,9 +313,12 @@ pgCore.pgTable("__reorg_rollback", {
228
313
  });
229
314
  async function initializeReorgRollbackTable(tx, indexerId) {
230
315
  try {
316
+ await tx.execute(`
317
+ CREATE SCHEMA IF NOT EXISTS ${constants.SCHEMA_NAME};
318
+ `);
231
319
  await tx.execute(
232
320
  drizzleOrm.sql.raw(`
233
- CREATE TABLE IF NOT EXISTS __reorg_rollback(
321
+ CREATE TABLE IF NOT EXISTS ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(
234
322
  n SERIAL PRIMARY KEY,
235
323
  op CHAR(1) NOT NULL,
236
324
  table_name TEXT NOT NULL,
@@ -243,7 +331,7 @@ async function initializeReorgRollbackTable(tx, indexerId) {
243
331
  );
244
332
  await tx.execute(
245
333
  drizzleOrm.sql.raw(`
246
- CREATE INDEX IF NOT EXISTS idx_reorg_rollback_indexer_id_cursor ON __reorg_rollback(indexer_id, cursor);
334
+ CREATE INDEX IF NOT EXISTS idx_reorg_rollback_indexer_id_cursor ON ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(indexer_id, cursor);
247
335
  `)
248
336
  );
249
337
  } catch (error) {
@@ -254,7 +342,7 @@ async function initializeReorgRollbackTable(tx, indexerId) {
254
342
  try {
255
343
  await tx.execute(
256
344
  drizzleOrm.sql.raw(`
257
- CREATE OR REPLACE FUNCTION reorg_checkpoint()
345
+ CREATE OR REPLACE FUNCTION ${constants.SCHEMA_NAME}.reorg_checkpoint()
258
346
  RETURNS TRIGGER AS $$
259
347
  DECLARE
260
348
  id_col TEXT := TG_ARGV[0]::TEXT;
@@ -264,13 +352,13 @@ async function initializeReorgRollbackTable(tx, indexerId) {
264
352
  old_id_value TEXT := row_to_json(OLD.*)->>id_col;
265
353
  BEGIN
266
354
  IF (TG_OP = 'DELETE') THEN
267
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
355
+ INSERT INTO ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
268
356
  SELECT 'D', TG_TABLE_NAME, order_key, old_id_value, row_to_json(OLD.*), indexer_id;
269
357
  ELSIF (TG_OP = 'UPDATE') THEN
270
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
358
+ INSERT INTO ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
271
359
  SELECT 'U', TG_TABLE_NAME, order_key, new_id_value, row_to_json(OLD.*), indexer_id;
272
360
  ELSIF (TG_OP = 'INSERT') THEN
273
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
361
+ INSERT INTO ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
274
362
  SELECT 'I', TG_TABLE_NAME, order_key, new_id_value, null, indexer_id;
275
363
  END IF;
276
364
  RETURN NULL;
@@ -300,7 +388,7 @@ async function registerTriggers(tx, tables, endCursor, idColumn, indexerId) {
300
388
  CREATE CONSTRAINT TRIGGER ${getReorgTriggerName(table, indexerId)}
301
389
  AFTER INSERT OR UPDATE OR DELETE ON ${table}
302
390
  DEFERRABLE INITIALLY DEFERRED
303
- FOR EACH ROW EXECUTE FUNCTION reorg_checkpoint('${idColumn}', ${`${Number(endCursor.orderKey)}`}, '${indexerId}');
391
+ FOR EACH ROW EXECUTE FUNCTION ${constants.SCHEMA_NAME}.reorg_checkpoint('${idColumn}', ${`${Number(endCursor.orderKey)}`}, '${indexerId}');
304
392
  `)
305
393
  );
306
394
  }
@@ -329,7 +417,7 @@ async function invalidate(tx, cursor, idColumn, indexerId) {
329
417
  const { rows: result } = await tx.execute(
330
418
  drizzleOrm.sql.raw(`
331
419
  WITH deleted AS (
332
- DELETE FROM __reorg_rollback
420
+ DELETE FROM ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
333
421
  WHERE cursor > ${Number(cursor.orderKey)}
334
422
  AND indexer_id = '${indexerId}'
335
423
  RETURNING *
@@ -422,7 +510,7 @@ async function finalize(tx, cursor, indexerId) {
422
510
  try {
423
511
  await tx.execute(
424
512
  drizzleOrm.sql.raw(`
425
- DELETE FROM __reorg_rollback
513
+ DELETE FROM ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
426
514
  WHERE cursor <= ${Number(cursor.orderKey)}
427
515
  AND indexer_id = '${indexerId}'
428
516
  `)
@@ -433,28 +521,60 @@ async function finalize(tx, cursor, indexerId) {
433
521
  });
434
522
  }
435
523
  }
524
+ async function cleanupStorage(tx, tables, indexerId) {
525
+ try {
526
+ for (const table of tables) {
527
+ await tx.execute(
528
+ drizzleOrm.sql.raw(
529
+ `DROP TRIGGER IF EXISTS ${getReorgTriggerName(table, indexerId)} ON ${table};`
530
+ )
531
+ );
532
+ }
533
+ await tx.execute(
534
+ drizzleOrm.sql.raw(`
535
+ DELETE FROM ${constants.SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
536
+ WHERE indexer_id = '${indexerId}'
537
+ `)
538
+ );
539
+ for (const table of tables) {
540
+ try {
541
+ await tx.execute(drizzleOrm.sql.raw(`TRUNCATE TABLE ${table} CASCADE;`));
542
+ } catch (error) {
543
+ throw new DrizzleStorageError(`Failed to truncate table ${table}`, {
544
+ cause: error
545
+ });
546
+ }
547
+ }
548
+ } catch (error) {
549
+ throw new DrizzleStorageError("Failed to clean up storage", {
550
+ cause: error
551
+ });
552
+ }
553
+ }
436
554
 
437
- const DRIZZLE_PROPERTY = "_drizzle";
438
555
  const MAX_RETRIES = 5;
439
556
  function useDrizzleStorage(_db) {
440
557
  const context = indexer.useIndexerContext();
441
- if (!context[DRIZZLE_PROPERTY]) {
558
+ if (!context[constants.DRIZZLE_PROPERTY]) {
442
559
  throw new DrizzleStorageError(
443
560
  "drizzle storage is not available. Did you register the plugin?"
444
561
  );
445
562
  }
446
- return context[DRIZZLE_PROPERTY];
563
+ return context[constants.DRIZZLE_PROPERTY];
447
564
  }
448
565
  function drizzleStorage({
449
566
  db,
450
567
  persistState: enablePersistence = true,
451
568
  indexerName: identifier = "default",
452
569
  schema,
453
- idColumn = "id"
570
+ idColumn = "id",
571
+ migrate: migrateOptions
454
572
  }) {
455
- return plugins.defineIndexerPlugin((indexer) => {
573
+ return plugins.defineIndexerPlugin((indexer$1) => {
456
574
  let tableNames = [];
457
575
  let indexerId = "";
576
+ const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
577
+ let prevFinality;
458
578
  try {
459
579
  tableNames = Object.values(schema ?? db._.schema ?? {}).map(
460
580
  (table) => table.dbName
@@ -464,12 +584,34 @@ function drizzleStorage({
464
584
  cause: error
465
585
  });
466
586
  }
467
- indexer.hooks.hook("run:before", async () => {
468
- const { indexerName: indexerFileName, availableIndexers } = plugins$1.useInternalContext();
587
+ indexer$1.hooks.hook("run:before", async () => {
588
+ const internalContext = plugins$1.useInternalContext();
589
+ const context = indexer.useIndexerContext();
590
+ const logger = plugins.useLogger();
591
+ context[constants.DRIZZLE_STORAGE_DB_PROPERTY] = db;
592
+ const { indexerName: indexerFileName, availableIndexers } = internalContext;
469
593
  indexerId = internal.generateIndexerId(indexerFileName, identifier);
594
+ if (alwaysReindex) {
595
+ logger.warn(
596
+ `Reindexing: Deleting all data from tables - ${tableNames.join(", ")}`
597
+ );
598
+ await withTransaction(db, async (tx) => {
599
+ await cleanupStorage(tx, tableNames, indexerId);
600
+ if (enablePersistence) {
601
+ await resetPersistence({ tx, indexerId });
602
+ }
603
+ logger.success("Tables have been cleaned up for reindexing");
604
+ });
605
+ }
470
606
  let retries = 0;
607
+ let migrationsApplied = false;
471
608
  while (retries <= MAX_RETRIES) {
472
609
  try {
610
+ if (migrateOptions && !migrationsApplied) {
611
+ await migrate(db, migrateOptions);
612
+ migrationsApplied = true;
613
+ logger.success("Migrations applied");
614
+ }
473
615
  await withTransaction(db, async (tx) => {
474
616
  await initializeReorgRollbackTable(tx, indexerId);
475
617
  if (enablePersistence) {
@@ -479,6 +621,9 @@ function drizzleStorage({
479
621
  break;
480
622
  } catch (error) {
481
623
  if (retries === MAX_RETRIES) {
624
+ if (error instanceof DrizzleStorageError) {
625
+ throw error;
626
+ }
482
627
  throw new DrizzleStorageError(
483
628
  "Initialization failed after 5 retries",
484
629
  {
@@ -491,7 +636,7 @@ function drizzleStorage({
491
636
  }
492
637
  }
493
638
  });
494
- indexer.hooks.hook("connect:before", async ({ request }) => {
639
+ indexer$1.hooks.hook("connect:before", async ({ request }) => {
495
640
  if (!enablePersistence) {
496
641
  return;
497
642
  }
@@ -508,7 +653,7 @@ function drizzleStorage({
508
653
  }
509
654
  });
510
655
  });
511
- indexer.hooks.hook("connect:after", async ({ request }) => {
656
+ indexer$1.hooks.hook("connect:after", async ({ request }) => {
512
657
  const cursor = request.startingCursor;
513
658
  if (!cursor) {
514
659
  return;
@@ -520,7 +665,7 @@ function drizzleStorage({
520
665
  }
521
666
  });
522
667
  });
523
- indexer.hooks.hook("connect:factory", async ({ request, endCursor }) => {
668
+ indexer$1.hooks.hook("connect:factory", async ({ request, endCursor }) => {
524
669
  if (!enablePersistence) {
525
670
  return;
526
671
  }
@@ -534,7 +679,7 @@ function drizzleStorage({
534
679
  });
535
680
  }
536
681
  });
537
- indexer.hooks.hook("message:finalize", async ({ message }) => {
682
+ indexer$1.hooks.hook("message:finalize", async ({ message }) => {
538
683
  const { cursor } = message.finalize;
539
684
  if (!cursor) {
540
685
  throw new DrizzleStorageError("Finalized Cursor is undefined");
@@ -546,7 +691,7 @@ function drizzleStorage({
546
691
  }
547
692
  });
548
693
  });
549
- indexer.hooks.hook("message:invalidate", async ({ message }) => {
694
+ indexer$1.hooks.hook("message:invalidate", async ({ message }) => {
550
695
  const { cursor } = message.invalidate;
551
696
  if (!cursor) {
552
697
  throw new DrizzleStorageError("Invalidate Cursor is undefined");
@@ -558,15 +703,18 @@ function drizzleStorage({
558
703
  }
559
704
  });
560
705
  });
561
- indexer.hooks.hook("handler:middleware", async ({ use }) => {
706
+ indexer$1.hooks.hook("handler:middleware", async ({ use }) => {
562
707
  use(async (context, next) => {
563
708
  try {
564
- const { endCursor, finality } = context;
709
+ const { endCursor, finality, cursor } = context;
565
710
  if (!endCursor) {
566
711
  throw new DrizzleStorageError("End Cursor is undefined");
567
712
  }
568
713
  await withTransaction(db, async (tx) => {
569
- context[DRIZZLE_PROPERTY] = { db: tx };
714
+ context[constants.DRIZZLE_PROPERTY] = { db: tx };
715
+ if (prevFinality === "pending") {
716
+ await invalidate(tx, cursor, idColumn, indexerId);
717
+ }
570
718
  if (finality !== "finalized") {
571
719
  await registerTriggers(
572
720
  tx,
@@ -577,14 +725,15 @@ function drizzleStorage({
577
725
  );
578
726
  }
579
727
  await next();
580
- delete context[DRIZZLE_PROPERTY];
581
- if (enablePersistence) {
728
+ delete context[constants.DRIZZLE_PROPERTY];
729
+ if (enablePersistence && finality !== "pending") {
582
730
  await persistState({
583
731
  tx,
584
732
  endCursor,
585
733
  indexerId
586
734
  });
587
735
  }
736
+ prevFinality = finality;
588
737
  });
589
738
  if (finality !== "finalized") {
590
739
  await removeTriggers(db, tableNames, indexerId);
@@ -600,5 +749,7 @@ function drizzleStorage({
600
749
  });
601
750
  }
602
751
 
752
+ exports.drizzle = drizzle;
603
753
  exports.drizzleStorage = drizzleStorage;
754
+ exports.migrate = migrate;
604
755
  exports.useDrizzleStorage = useDrizzleStorage;
package/dist/index.d.cts CHANGED
@@ -1,17 +1,147 @@
1
1
  import * as _apibara_indexer_plugins from '@apibara/indexer/plugins';
2
- import { TablesRelationalConfig, ExtractTablesWithRelations } from 'drizzle-orm';
2
+ import { DrizzleConfig, TablesRelationalConfig, ExtractTablesWithRelations } from 'drizzle-orm';
3
3
  import { PgQueryResultHKT, PgTransaction, PgDatabase } from 'drizzle-orm/pg-core';
4
+ import { PGliteOptions, PGlite } from '@electric-sql/pglite';
5
+ import { MigrationConfig } from 'drizzle-orm/migrator';
6
+ import { NodePgDatabase as NodePgDatabase$1 } from 'drizzle-orm/node-postgres';
7
+ import { PgliteDatabase as PgliteDatabase$1 } from 'drizzle-orm/pglite';
8
+ import pg from 'pg';
9
+
10
+ /**
11
+ * Union type of all possible drizzle database options
12
+ */
13
+ type DrizzleOptions = PgliteDrizzleOptions | NodePgDrizzleOptions;
14
+ /**
15
+ * Configuration options for Node-Postgres database connection
16
+ */
17
+ type NodePgDrizzleOptions = {
18
+ /**
19
+ * Type of database to use -
20
+ * - "pglite" - PGLite database
21
+ * - "node-postgres" - Node-Postgres database
22
+ * @default "pglite"
23
+ */
24
+ type: "node-postgres";
25
+ /**
26
+ * Connection string to use for the database
27
+ * @default ""
28
+ */
29
+ connectionString?: string;
30
+ /**
31
+ * Pool configuration options for Node-Postgres
32
+ */
33
+ poolConfig?: pg.PoolConfig;
34
+ /**
35
+ * Additional drizzle configuration options
36
+ */
37
+ config?: Omit<DrizzleConfig, "schema">;
38
+ };
39
+ /**
40
+ * Configuration options for PGLite database connection
41
+ */
42
+ type PgliteDrizzleOptions = {
43
+ /**
44
+ * Type of database to use -
45
+ * - "pglite" - PGLite database
46
+ * - "node-postgres" - Node-Postgres database
47
+ */
48
+ type?: "pglite";
49
+ /**
50
+ * Connection string to use for the database
51
+ * @default process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://pglite"
52
+ */
53
+ connectionString?: string;
54
+ /**
55
+ * Pool configuration is not supported for PGLite
56
+ */
57
+ poolConfig?: never;
58
+ /**
59
+ * Additional drizzle configuration options with PGLite specific connection options
60
+ */
61
+ config?: Omit<DrizzleConfig, "schema"> & {
62
+ connection?: (PGliteOptions & {
63
+ dataDir?: string;
64
+ }) | string;
65
+ };
66
+ };
67
+ /**
68
+ * Extended PGLite database type with client information
69
+ */
70
+ type PgliteDatabase<TSchema extends Record<string, unknown>> = PgliteDatabase$1<TSchema> & {
71
+ $client: PGlite;
72
+ };
73
+ /**
74
+ * Extended Node-Postgres database type with client information
75
+ */
76
+ type NodePgDatabase<TSchema extends Record<string, unknown>> = NodePgDatabase$1<TSchema> & {
77
+ $client: pg.Pool;
78
+ };
79
+ type Database<TOptions extends DrizzleOptions, TSchema extends Record<string, unknown>> = TOptions extends PgliteDrizzleOptions ? PgliteDatabase<TSchema> : NodePgDatabase<TSchema>;
80
+ /**
81
+ * Creates a new Drizzle database instance based on the provided options
82
+ *
83
+ * @important connectionString defaults to process.env["POSTGRES_CONNECTION_STRING"], if not set, it defaults to "memory://" (in-memory pglite)
84
+ *
85
+ * @param options - Configuration options for the database connection
86
+ * @returns A configured Drizzle database instance
87
+ * @throws {Error} If an invalid database type is specified
88
+ */
89
+ declare function drizzle<TSchema extends Record<string, unknown>, TOptions extends DrizzleOptions>(options?: TOptions & {
90
+ /**
91
+ * Schema to use for the database
92
+ * @default {}
93
+ */
94
+ schema?: TSchema;
95
+ }): Database<TOptions, TSchema>;
96
+ /**
97
+ * Options for database migration
98
+ */
99
+ type MigrateOptions = MigrationConfig;
100
+ /**
101
+ * Performs database migration based on the provided configuration
102
+ * @param db - The database instance to migrate
103
+ * @param options - Migration configuration options
104
+ *
105
+ * @important This function runs migrations on the database instance provided to the `drizzleStorage` plugin.
106
+ * It automatically detects the type of database and runs the appropriate migrate function
107
+ * (PGLite or Node-Postgres).
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * await migrate(db, { migrationsFolder: "./drizzle" });
112
+ * ```
113
+ */
114
+ declare function migrate<TSchema extends Record<string, unknown>>(db: PgliteDatabase<TSchema> | NodePgDatabase<TSchema>, options: MigrateOptions): Promise<void>;
4
115
 
5
116
  type DrizzleStorage<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> = {
6
117
  db: PgTransaction<TQueryResult, TFullSchema, TSchema>;
7
118
  };
8
119
  declare function useDrizzleStorage<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>>(_db?: PgDatabase<TQueryResult, TFullSchema, TSchema>): DrizzleStorage<TQueryResult, TFullSchema, TSchema>;
9
120
  interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> {
121
+ /**
122
+ * The Drizzle database instance.
123
+ */
10
124
  db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
125
+ /**
126
+ * Whether to persist the indexer's state. Defaults to true.
127
+ */
11
128
  persistState?: boolean;
129
+ /**
130
+ * The name of the indexer. Default value is 'default'.
131
+ */
12
132
  indexerName?: string;
133
+ /**
134
+ * The schema of the database.
135
+ */
13
136
  schema?: Record<string, unknown>;
137
+ /**
138
+ * The column to use as the id. Defaults to 'id'.
139
+ */
14
140
  idColumn?: string;
141
+ /**
142
+ * The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
143
+ */
144
+ migrate?: MigrateOptions;
15
145
  }
16
146
  /**
17
147
  * Creates a plugin that uses Drizzle as the storage layer.
@@ -22,7 +152,8 @@ interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSche
22
152
  * @param options.indexerName - The name of the indexer. Defaults value is 'default'.
23
153
  * @param options.schema - The schema of the database.
24
154
  * @param options.idColumn - The column to use as the id. Defaults to 'id'.
155
+ * @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
25
156
  */
26
- declare function drizzleStorage<TFilter, TBlock, TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>>({ db, persistState: enablePersistence, indexerName: identifier, schema, idColumn, }: DrizzleStorageOptions<TQueryResult, TFullSchema, TSchema>): _apibara_indexer_plugins.IndexerPlugin<TFilter, TBlock>;
157
+ declare function drizzleStorage<TFilter, TBlock, TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>>({ db, persistState: enablePersistence, indexerName: identifier, schema, idColumn, migrate: migrateOptions, }: DrizzleStorageOptions<TQueryResult, TFullSchema, TSchema>): _apibara_indexer_plugins.IndexerPlugin<TFilter, TBlock>;
27
158
 
28
- export { type DrizzleStorage, type DrizzleStorageOptions, drizzleStorage, useDrizzleStorage };
159
+ export { type Database, type DrizzleOptions, type DrizzleStorage, type DrizzleStorageOptions, type MigrateOptions, type NodePgDatabase, type NodePgDrizzleOptions, type PgliteDatabase, type PgliteDrizzleOptions, drizzle, drizzleStorage, migrate, useDrizzleStorage };