@apibara/plugin-drizzle 2.1.0-beta.4 → 2.1.0-beta.40

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.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import { useIndexerContext } from '@apibara/indexer';
2
- import { defineIndexerPlugin } from '@apibara/indexer/plugins';
2
+ import { defineIndexerPlugin, useLogger } from '@apibara/indexer/plugins';
3
3
  import { generateIndexerId } from '@apibara/indexer/internal';
4
4
  import { useInternalContext } from '@apibara/indexer/internal/plugins';
5
+ import { S as SCHEMA_NAME, D as DRIZZLE_PROPERTY, a as DRIZZLE_STORAGE_DB_PROPERTY } from './shared/plugin-drizzle.2d226351.mjs';
6
+ import { entityKind, sql, eq, and, isNull, gt, lt } from 'drizzle-orm';
5
7
  import { normalizeCursor } from '@apibara/protocol';
6
- import { eq, and, isNull, gt, lt, sql } from 'drizzle-orm';
7
- import { pgTable, text, integer, primaryKey, serial, char, jsonb } from 'drizzle-orm/pg-core';
8
+ import { pgSchema, text, integer, primaryKey, serial, char, jsonb } from 'drizzle-orm/pg-core';
8
9
 
9
10
  class DrizzleStorageError extends Error {
10
11
  constructor(message, options) {
@@ -33,16 +34,89 @@ function serialize(obj) {
33
34
  function sleep(ms) {
34
35
  return new Promise((resolve) => setTimeout(resolve, ms));
35
36
  }
37
+ const getIdColumnForTable = (tableName, idColumn) => {
38
+ if (idColumn[tableName]) {
39
+ return idColumn[tableName];
40
+ }
41
+ return idColumn["*"];
42
+ };
36
43
 
37
- const CHECKPOINTS_TABLE_NAME = "__indexer_checkpoints";
38
- const FILTERS_TABLE_NAME = "__indexer_filters";
39
- const SCHEMA_VERSION_TABLE_NAME = "__indexer_schema_version";
40
- const checkpoints = pgTable(CHECKPOINTS_TABLE_NAME, {
44
+ function drizzle(options) {
45
+ const {
46
+ connectionString = process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://",
47
+ schema,
48
+ type = "pglite",
49
+ config,
50
+ poolConfig
51
+ } = options ?? {};
52
+ if (isPgliteConnectionString(connectionString) && type === "pglite") {
53
+ const { drizzle: drizzlePGLite } = require("drizzle-orm/pglite");
54
+ return drizzlePGLite({
55
+ schema,
56
+ connection: {
57
+ dataDir: connectionString || "memory://pglite"
58
+ },
59
+ ...config || {}
60
+ });
61
+ }
62
+ const { Pool } = require("pg");
63
+ const { drizzle: drizzleNode } = require("drizzle-orm/node-postgres");
64
+ const pool = new Pool({
65
+ connectionString,
66
+ ...poolConfig || {}
67
+ });
68
+ return drizzleNode(pool, { schema, ...config || {} });
69
+ }
70
+ async function migrate(db, options) {
71
+ const isPglite = isDrizzleKind(db, "PgliteDatabase");
72
+ try {
73
+ if (isPglite) {
74
+ const { migrate: migratePGLite } = require("drizzle-orm/pglite/migrator");
75
+ await migratePGLite(db, options);
76
+ } else {
77
+ const {
78
+ migrate: migrateNode
79
+ } = require("drizzle-orm/node-postgres/migrator");
80
+ await migrateNode(db, options);
81
+ }
82
+ } catch (error) {
83
+ throw new DrizzleStorageError(
84
+ "Failed to apply migrations! Please check if you have generated migrations using drizzle:generate",
85
+ {
86
+ cause: error
87
+ }
88
+ );
89
+ }
90
+ }
91
+ function isPgliteConnectionString(conn) {
92
+ return conn.startsWith("memory://") || conn.startsWith("file://") || conn.startsWith("idb://");
93
+ }
94
+ function isDrizzleKind(value, entityKindValue) {
95
+ if (!value || typeof value !== "object") {
96
+ return false;
97
+ }
98
+ let cls = Object.getPrototypeOf(value).constructor;
99
+ if (cls) {
100
+ while (cls) {
101
+ if (entityKind in cls && cls[entityKind] === entityKindValue) {
102
+ return true;
103
+ }
104
+ cls = Object.getPrototypeOf(cls);
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+
110
+ const CHECKPOINTS_TABLE_NAME = "checkpoints";
111
+ const FILTERS_TABLE_NAME = "filters";
112
+ const SCHEMA_VERSION_TABLE_NAME = "schema_version";
113
+ const schema$1 = pgSchema(SCHEMA_NAME);
114
+ const checkpoints = schema$1.table(CHECKPOINTS_TABLE_NAME, {
41
115
  id: text("id").notNull().primaryKey(),
42
116
  orderKey: integer("order_key").notNull(),
43
117
  uniqueKey: text("unique_key")
44
118
  });
45
- const filters = pgTable(
119
+ const filters = schema$1.table(
46
120
  FILTERS_TABLE_NAME,
47
121
  {
48
122
  id: text("id").notNull(),
@@ -56,7 +130,7 @@ const filters = pgTable(
56
130
  }
57
131
  ]
58
132
  );
59
- const schemaVersion = pgTable(SCHEMA_VERSION_TABLE_NAME, {
133
+ const schemaVersion = schema$1.table(SCHEMA_VERSION_TABLE_NAME, {
60
134
  k: integer("k").notNull().primaryKey(),
61
135
  version: integer("version").notNull()
62
136
  });
@@ -67,12 +141,19 @@ const MIGRATIONS = [
67
141
  // Add more migration arrays for future versions
68
142
  ];
69
143
  async function initializePersistentState(tx) {
70
- await tx.execute(`
71
- CREATE TABLE IF NOT EXISTS ${SCHEMA_VERSION_TABLE_NAME} (
144
+ await tx.execute(
145
+ sql.raw(`
146
+ CREATE SCHEMA IF NOT EXISTS ${SCHEMA_NAME};
147
+ `)
148
+ );
149
+ await tx.execute(
150
+ sql.raw(`
151
+ CREATE TABLE IF NOT EXISTS ${SCHEMA_NAME}.${SCHEMA_VERSION_TABLE_NAME} (
72
152
  k INTEGER PRIMARY KEY,
73
153
  version INTEGER NOT NULL
74
154
  );
75
- `);
155
+ `)
156
+ );
76
157
  const versionRows = await tx.select().from(schemaVersion).where(eq(schemaVersion.k, 0));
77
158
  const storedVersion = versionRows[0]?.version ?? -1;
78
159
  if (storedVersion > CURRENT_SCHEMA_VERSION) {
@@ -82,22 +163,26 @@ async function initializePersistentState(tx) {
82
163
  }
83
164
  try {
84
165
  if (storedVersion === -1) {
85
- await tx.execute(`
86
- CREATE TABLE IF NOT EXISTS ${CHECKPOINTS_TABLE_NAME} (
166
+ await tx.execute(
167
+ sql.raw(`
168
+ CREATE TABLE IF NOT EXISTS ${SCHEMA_NAME}.${CHECKPOINTS_TABLE_NAME} (
87
169
  id TEXT PRIMARY KEY,
88
170
  order_key INTEGER NOT NULL,
89
171
  unique_key TEXT
90
172
  );
91
- `);
92
- await tx.execute(`
93
- CREATE TABLE IF NOT EXISTS ${FILTERS_TABLE_NAME} (
173
+ `)
174
+ );
175
+ await tx.execute(
176
+ sql.raw(`
177
+ CREATE TABLE IF NOT EXISTS ${SCHEMA_NAME}.${FILTERS_TABLE_NAME} (
94
178
  id TEXT NOT NULL,
95
179
  filter TEXT NOT NULL,
96
180
  from_block INTEGER NOT NULL,
97
181
  to_block INTEGER DEFAULT NULL,
98
182
  PRIMARY KEY (id, from_block)
99
183
  );
100
- `);
184
+ `)
185
+ );
101
186
  await tx.insert(schemaVersion).values({
102
187
  k: 0,
103
188
  version: CURRENT_SCHEMA_VERSION
@@ -132,7 +217,9 @@ async function persistState(props) {
132
217
  target: checkpoints.id,
133
218
  set: {
134
219
  orderKey: Number(endCursor.orderKey),
135
- uniqueKey: endCursor.uniqueKey
220
+ // Explicitly set the unique key to `null` to indicate that it has been deleted
221
+ // Otherwise drizzle will not update its value.
222
+ uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null
136
223
  }
137
224
  });
138
225
  if (filter) {
@@ -211,11 +298,24 @@ async function finalizeState(props) {
211
298
  });
212
299
  }
213
300
  }
301
+ async function resetPersistence(props) {
302
+ const { tx, indexerId } = props;
303
+ try {
304
+ await tx.delete(checkpoints).where(eq(checkpoints.id, indexerId));
305
+ await tx.delete(filters).where(eq(filters.id, indexerId));
306
+ } catch (error) {
307
+ throw new DrizzleStorageError("Failed to reset persistence state", {
308
+ cause: error
309
+ });
310
+ }
311
+ }
214
312
 
313
+ const ROLLBACK_TABLE_NAME = "reorg_rollback";
314
+ const schema = pgSchema(SCHEMA_NAME);
215
315
  function getReorgTriggerName(table, indexerId) {
216
316
  return `${table}_reorg_${indexerId}`;
217
317
  }
218
- pgTable("__reorg_rollback", {
318
+ schema.table(ROLLBACK_TABLE_NAME, {
219
319
  n: serial("n").primaryKey(),
220
320
  op: char("op", { length: 1 }).$type().notNull(),
221
321
  table_name: text("table_name").notNull(),
@@ -226,9 +326,12 @@ pgTable("__reorg_rollback", {
226
326
  });
227
327
  async function initializeReorgRollbackTable(tx, indexerId) {
228
328
  try {
329
+ await tx.execute(`
330
+ CREATE SCHEMA IF NOT EXISTS ${SCHEMA_NAME};
331
+ `);
229
332
  await tx.execute(
230
333
  sql.raw(`
231
- CREATE TABLE IF NOT EXISTS __reorg_rollback(
334
+ CREATE TABLE IF NOT EXISTS ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(
232
335
  n SERIAL PRIMARY KEY,
233
336
  op CHAR(1) NOT NULL,
234
337
  table_name TEXT NOT NULL,
@@ -241,7 +344,7 @@ async function initializeReorgRollbackTable(tx, indexerId) {
241
344
  );
242
345
  await tx.execute(
243
346
  sql.raw(`
244
- CREATE INDEX IF NOT EXISTS idx_reorg_rollback_indexer_id_cursor ON __reorg_rollback(indexer_id, cursor);
347
+ CREATE INDEX IF NOT EXISTS idx_reorg_rollback_indexer_id_cursor ON ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(indexer_id, cursor);
245
348
  `)
246
349
  );
247
350
  } catch (error) {
@@ -252,24 +355,25 @@ async function initializeReorgRollbackTable(tx, indexerId) {
252
355
  try {
253
356
  await tx.execute(
254
357
  sql.raw(`
255
- CREATE OR REPLACE FUNCTION reorg_checkpoint()
358
+ CREATE OR REPLACE FUNCTION ${SCHEMA_NAME}.reorg_checkpoint()
256
359
  RETURNS TRIGGER AS $$
257
360
  DECLARE
258
- id_col TEXT := TG_ARGV[0]::TEXT;
259
- order_key INTEGER := TG_ARGV[1]::INTEGER;
260
- indexer_id TEXT := TG_ARGV[2]::TEXT;
361
+ table_name TEXT := TG_ARGV[0]::TEXT;
362
+ id_col TEXT := TG_ARGV[1]::TEXT;
363
+ order_key INTEGER := TG_ARGV[2]::INTEGER;
364
+ indexer_id TEXT := TG_ARGV[3]::TEXT;
261
365
  new_id_value TEXT := row_to_json(NEW.*)->>id_col;
262
366
  old_id_value TEXT := row_to_json(OLD.*)->>id_col;
263
367
  BEGIN
264
368
  IF (TG_OP = 'DELETE') THEN
265
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
266
- SELECT 'D', TG_TABLE_NAME, order_key, old_id_value, row_to_json(OLD.*), indexer_id;
369
+ INSERT INTO ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
370
+ SELECT 'D', table_name, order_key, old_id_value, row_to_json(OLD.*), indexer_id;
267
371
  ELSIF (TG_OP = 'UPDATE') THEN
268
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
269
- SELECT 'U', TG_TABLE_NAME, order_key, new_id_value, row_to_json(OLD.*), indexer_id;
372
+ INSERT INTO ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
373
+ SELECT 'U', table_name, order_key, new_id_value, row_to_json(OLD.*), indexer_id;
270
374
  ELSIF (TG_OP = 'INSERT') THEN
271
- INSERT INTO __reorg_rollback(op, table_name, cursor, row_id, row_value, indexer_id)
272
- SELECT 'I', TG_TABLE_NAME, order_key, new_id_value, null, indexer_id;
375
+ INSERT INTO ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}(op, table_name, cursor, row_id, row_value, indexer_id)
376
+ SELECT 'I', table_name, order_key, new_id_value, null, indexer_id;
273
377
  END IF;
274
378
  RETURN NULL;
275
379
  END;
@@ -285,9 +389,10 @@ async function initializeReorgRollbackTable(tx, indexerId) {
285
389
  );
286
390
  }
287
391
  }
288
- async function registerTriggers(tx, tables, endCursor, idColumn, indexerId) {
392
+ async function registerTriggers(tx, tables, endCursor, idColumnMap, indexerId) {
289
393
  try {
290
394
  for (const table of tables) {
395
+ const tableIdColumn = getIdColumnForTable(table, idColumnMap);
291
396
  await tx.execute(
292
397
  sql.raw(
293
398
  `DROP TRIGGER IF EXISTS ${getReorgTriggerName(table, indexerId)} ON ${table};`
@@ -298,7 +403,7 @@ async function registerTriggers(tx, tables, endCursor, idColumn, indexerId) {
298
403
  CREATE CONSTRAINT TRIGGER ${getReorgTriggerName(table, indexerId)}
299
404
  AFTER INSERT OR UPDATE OR DELETE ON ${table}
300
405
  DEFERRABLE INITIALLY DEFERRED
301
- FOR EACH ROW EXECUTE FUNCTION reorg_checkpoint('${idColumn}', ${`${Number(endCursor.orderKey)}`}, '${indexerId}');
406
+ FOR EACH ROW EXECUTE FUNCTION ${SCHEMA_NAME}.reorg_checkpoint('${table}', '${tableIdColumn}', ${Number(endCursor.orderKey)}, '${indexerId}');
302
407
  `)
303
408
  );
304
409
  }
@@ -323,11 +428,11 @@ async function removeTriggers(db, tables, indexerId) {
323
428
  });
324
429
  }
325
430
  }
326
- async function invalidate(tx, cursor, idColumn, indexerId) {
431
+ async function invalidate(tx, cursor, idColumnMap, indexerId) {
327
432
  const { rows: result } = await tx.execute(
328
433
  sql.raw(`
329
434
  WITH deleted AS (
330
- DELETE FROM __reorg_rollback
435
+ DELETE FROM ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
331
436
  WHERE cursor > ${Number(cursor.orderKey)}
332
437
  AND indexer_id = '${indexerId}'
333
438
  RETURNING *
@@ -341,6 +446,7 @@ async function invalidate(tx, cursor, idColumn, indexerId) {
341
446
  );
342
447
  }
343
448
  for (const op of result) {
449
+ const tableIdColumn = getIdColumnForTable(op.table_name, idColumnMap);
344
450
  switch (op.op) {
345
451
  case "I":
346
452
  try {
@@ -350,7 +456,7 @@ async function invalidate(tx, cursor, idColumn, indexerId) {
350
456
  await tx.execute(
351
457
  sql.raw(`
352
458
  DELETE FROM ${op.table_name}
353
- WHERE ${idColumn} = '${op.row_id}'
459
+ WHERE ${tableIdColumn} = '${op.row_id}'
354
460
  `)
355
461
  );
356
462
  } catch (error) {
@@ -390,7 +496,9 @@ async function invalidate(tx, cursor, idColumn, indexerId) {
390
496
  );
391
497
  }
392
498
  const rowValue = typeof op.row_value === "string" ? JSON.parse(op.row_value) : op.row_value;
393
- const nonIdKeys = Object.keys(rowValue).filter((k) => k !== idColumn);
499
+ const nonIdKeys = Object.keys(rowValue).filter(
500
+ (k) => k !== tableIdColumn
501
+ );
394
502
  const fields = nonIdKeys.map((c) => `${c} = prev.${c}`).join(", ");
395
503
  const query = sql.raw(`
396
504
  UPDATE ${op.table_name}
@@ -398,7 +506,7 @@ async function invalidate(tx, cursor, idColumn, indexerId) {
398
506
  FROM (
399
507
  SELECT * FROM json_populate_record(null::${op.table_name}, '${JSON.stringify(op.row_value)}'::json)
400
508
  ) as prev
401
- WHERE ${op.table_name}.${idColumn} = '${op.row_id}'
509
+ WHERE ${op.table_name}.${tableIdColumn} = '${op.row_id}'
402
510
  `);
403
511
  await tx.execute(query);
404
512
  } catch (error) {
@@ -420,7 +528,7 @@ async function finalize(tx, cursor, indexerId) {
420
528
  try {
421
529
  await tx.execute(
422
530
  sql.raw(`
423
- DELETE FROM __reorg_rollback
531
+ DELETE FROM ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
424
532
  WHERE cursor <= ${Number(cursor.orderKey)}
425
533
  AND indexer_id = '${indexerId}'
426
534
  `)
@@ -431,8 +539,37 @@ async function finalize(tx, cursor, indexerId) {
431
539
  });
432
540
  }
433
541
  }
542
+ async function cleanupStorage(tx, tables, indexerId) {
543
+ try {
544
+ for (const table of tables) {
545
+ await tx.execute(
546
+ sql.raw(
547
+ `DROP TRIGGER IF EXISTS ${getReorgTriggerName(table, indexerId)} ON ${table};`
548
+ )
549
+ );
550
+ }
551
+ await tx.execute(
552
+ sql.raw(`
553
+ DELETE FROM ${SCHEMA_NAME}.${ROLLBACK_TABLE_NAME}
554
+ WHERE indexer_id = '${indexerId}'
555
+ `)
556
+ );
557
+ for (const table of tables) {
558
+ try {
559
+ await tx.execute(sql.raw(`TRUNCATE TABLE ${table} CASCADE;`));
560
+ } catch (error) {
561
+ throw new DrizzleStorageError(`Failed to truncate table ${table}`, {
562
+ cause: error
563
+ });
564
+ }
565
+ }
566
+ } catch (error) {
567
+ throw new DrizzleStorageError("Failed to clean up storage", {
568
+ cause: error
569
+ });
570
+ }
571
+ }
434
572
 
435
- const DRIZZLE_PROPERTY = "_drizzle";
436
573
  const MAX_RETRIES = 5;
437
574
  function useDrizzleStorage(_db) {
438
575
  const context = useIndexerContext();
@@ -443,40 +580,92 @@ function useDrizzleStorage(_db) {
443
580
  }
444
581
  return context[DRIZZLE_PROPERTY];
445
582
  }
583
+ function useTestDrizzleStorage() {
584
+ const context = useIndexerContext();
585
+ if (!context[DRIZZLE_STORAGE_DB_PROPERTY]) {
586
+ throw new DrizzleStorageError(
587
+ "drizzle storage db is not available. Did you register the plugin?"
588
+ );
589
+ }
590
+ return context[DRIZZLE_STORAGE_DB_PROPERTY];
591
+ }
446
592
  function drizzleStorage({
447
593
  db,
448
594
  persistState: enablePersistence = true,
449
595
  indexerName: identifier = "default",
450
- schema,
451
- idColumn = "id"
596
+ schema: _schema,
597
+ idColumn,
598
+ migrate: migrateOptions
452
599
  }) {
453
600
  return defineIndexerPlugin((indexer) => {
454
601
  let tableNames = [];
455
602
  let indexerId = "";
603
+ const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
604
+ let prevFinality;
605
+ const schema = _schema ?? db._.schema ?? {};
606
+ const idColumnMap = {
607
+ "*": typeof idColumn === "string" ? idColumn : "id",
608
+ ...typeof idColumn === "object" ? idColumn : {}
609
+ };
456
610
  try {
457
- tableNames = Object.values(schema ?? db._.schema ?? {}).map(
458
- (table) => table.dbName
459
- );
611
+ tableNames = Object.values(schema).map((table) => table.dbName);
460
612
  } catch (error) {
461
613
  throw new DrizzleStorageError("Failed to get table names from schema", {
462
614
  cause: error
463
615
  });
464
616
  }
465
- indexer.hooks.hook("run:before", async () => {
466
- const { indexerName: indexerFileName, availableIndexers } = useInternalContext();
617
+ for (const table of Object.values(schema)) {
618
+ const columns = table.columns;
619
+ const tableIdColumn = getIdColumnForTable(table.dbName, idColumnMap);
620
+ const columnExists = Object.values(columns).some(
621
+ (column) => column.name === tableIdColumn
622
+ );
623
+ if (!columnExists) {
624
+ throw new DrizzleStorageError(
625
+ `Column \`"${tableIdColumn}"\` does not exist in table \`"${table.dbName}"\`. Make sure the table has the specified column or provide a valid \`idColumn\` mapping to \`drizzleStorage\`.`
626
+ );
627
+ }
628
+ }
629
+ indexer.hooks.hook("plugins:init", async () => {
630
+ const internalContext = useInternalContext();
631
+ const context = useIndexerContext();
632
+ const logger = useLogger();
633
+ context[DRIZZLE_STORAGE_DB_PROPERTY] = db;
634
+ const { indexerName: indexerFileName, availableIndexers } = internalContext;
467
635
  indexerId = generateIndexerId(indexerFileName, identifier);
468
636
  let retries = 0;
637
+ let migrationsApplied = false;
638
+ let cleanupApplied = false;
469
639
  while (retries <= MAX_RETRIES) {
470
640
  try {
641
+ if (migrateOptions && !migrationsApplied) {
642
+ await migrate(db, migrateOptions);
643
+ migrationsApplied = true;
644
+ logger.success("Migrations applied");
645
+ }
471
646
  await withTransaction(db, async (tx) => {
472
647
  await initializeReorgRollbackTable(tx, indexerId);
473
648
  if (enablePersistence) {
474
649
  await initializePersistentState(tx);
475
650
  }
651
+ if (alwaysReindex && !cleanupApplied) {
652
+ logger.warn(
653
+ `Reindexing: Deleting all data from tables - ${tableNames.join(", ")}`
654
+ );
655
+ await cleanupStorage(tx, tableNames, indexerId);
656
+ if (enablePersistence) {
657
+ await resetPersistence({ tx, indexerId });
658
+ }
659
+ cleanupApplied = true;
660
+ logger.success("Tables have been cleaned up for reindexing");
661
+ }
476
662
  });
477
663
  break;
478
664
  } catch (error) {
479
665
  if (retries === MAX_RETRIES) {
666
+ if (error instanceof DrizzleStorageError) {
667
+ throw error;
668
+ }
480
669
  throw new DrizzleStorageError(
481
670
  "Initialization failed after 5 retries",
482
671
  {
@@ -512,7 +701,7 @@ function drizzleStorage({
512
701
  return;
513
702
  }
514
703
  await withTransaction(db, async (tx) => {
515
- await invalidate(tx, cursor, idColumn, indexerId);
704
+ await invalidate(tx, cursor, idColumnMap, indexerId);
516
705
  if (enablePersistence) {
517
706
  await invalidateState({ tx, cursor, indexerId });
518
707
  }
@@ -533,7 +722,7 @@ function drizzleStorage({
533
722
  }
534
723
  });
535
724
  indexer.hooks.hook("message:finalize", async ({ message }) => {
536
- const { cursor } = message.finalize;
725
+ const { cursor } = message;
537
726
  if (!cursor) {
538
727
  throw new DrizzleStorageError("Finalized Cursor is undefined");
539
728
  }
@@ -545,12 +734,12 @@ function drizzleStorage({
545
734
  });
546
735
  });
547
736
  indexer.hooks.hook("message:invalidate", async ({ message }) => {
548
- const { cursor } = message.invalidate;
737
+ const { cursor } = message;
549
738
  if (!cursor) {
550
739
  throw new DrizzleStorageError("Invalidate Cursor is undefined");
551
740
  }
552
741
  await withTransaction(db, async (tx) => {
553
- await invalidate(tx, cursor, idColumn, indexerId);
742
+ await invalidate(tx, cursor, idColumnMap, indexerId);
554
743
  if (enablePersistence) {
555
744
  await invalidateState({ tx, cursor, indexerId });
556
745
  }
@@ -559,30 +748,34 @@ function drizzleStorage({
559
748
  indexer.hooks.hook("handler:middleware", async ({ use }) => {
560
749
  use(async (context, next) => {
561
750
  try {
562
- const { endCursor, finality } = context;
751
+ const { endCursor, finality, cursor } = context;
563
752
  if (!endCursor) {
564
753
  throw new DrizzleStorageError("End Cursor is undefined");
565
754
  }
566
755
  await withTransaction(db, async (tx) => {
567
756
  context[DRIZZLE_PROPERTY] = { db: tx };
757
+ if (prevFinality === "pending") {
758
+ await invalidate(tx, cursor, idColumnMap, indexerId);
759
+ }
568
760
  if (finality !== "finalized") {
569
761
  await registerTriggers(
570
762
  tx,
571
763
  tableNames,
572
764
  endCursor,
573
- idColumn,
765
+ idColumnMap,
574
766
  indexerId
575
767
  );
576
768
  }
577
769
  await next();
578
770
  delete context[DRIZZLE_PROPERTY];
579
- if (enablePersistence) {
771
+ if (enablePersistence && finality !== "pending") {
580
772
  await persistState({
581
773
  tx,
582
774
  endCursor,
583
775
  indexerId
584
776
  });
585
777
  }
778
+ prevFinality = finality;
586
779
  });
587
780
  if (finality !== "finalized") {
588
781
  await removeTriggers(db, tableNames, indexerId);
@@ -598,4 +791,5 @@ function drizzleStorage({
598
791
  });
599
792
  }
600
793
 
601
- export { drizzleStorage, useDrizzleStorage };
794
+ export { drizzle, drizzleStorage, migrate, useDrizzleStorage, useTestDrizzleStorage };
795
+ //# sourceMappingURL=index.mjs.map