@apibara/plugin-drizzle 2.1.0-beta.6 → 2.1.0-beta.7
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 +85 -15
- package/dist/index.d.cts +131 -3
- package/dist/index.d.mts +131 -3
- package/dist/index.d.ts +131 -3
- package/dist/index.mjs +69 -5
- package/dist/shared/plugin-drizzle.e884ca32.cjs +7 -0
- package/dist/shared/plugin-drizzle.f8d1b186.mjs +4 -0
- package/dist/testing.cjs +13 -0
- package/dist/testing.d.cts +6 -0
- package/dist/testing.d.mts +6 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.mjs +11 -0
- package/package.json +9 -3
- package/src/constants.ts +2 -0
- package/src/helper.ts +192 -0
- package/src/index.ts +46 -3
- package/src/testing.ts +13 -0
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.e884ca32.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,6 +47,50 @@ function sleep(ms) {
|
|
|
36
47
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
37
48
|
}
|
|
38
49
|
|
|
50
|
+
function drizzle(options) {
|
|
51
|
+
const {
|
|
52
|
+
connectionString = "memory://",
|
|
53
|
+
schema,
|
|
54
|
+
type = "pglite",
|
|
55
|
+
config,
|
|
56
|
+
poolConfig
|
|
57
|
+
} = options ?? {};
|
|
58
|
+
if (connectionString.startsWith("postgres://") || 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
|
+
|
|
39
94
|
const CHECKPOINTS_TABLE_NAME = "__indexer_checkpoints";
|
|
40
95
|
const FILTERS_TABLE_NAME = "__indexer_filters";
|
|
41
96
|
const SCHEMA_VERSION_TABLE_NAME = "__indexer_schema_version";
|
|
@@ -434,25 +489,25 @@ async function finalize(tx, cursor, indexerId) {
|
|
|
434
489
|
}
|
|
435
490
|
}
|
|
436
491
|
|
|
437
|
-
const DRIZZLE_PROPERTY = "_drizzle";
|
|
438
492
|
const MAX_RETRIES = 5;
|
|
439
493
|
function useDrizzleStorage(_db) {
|
|
440
494
|
const context = indexer.useIndexerContext();
|
|
441
|
-
if (!context[DRIZZLE_PROPERTY]) {
|
|
495
|
+
if (!context[constants.DRIZZLE_PROPERTY]) {
|
|
442
496
|
throw new DrizzleStorageError(
|
|
443
497
|
"drizzle storage is not available. Did you register the plugin?"
|
|
444
498
|
);
|
|
445
499
|
}
|
|
446
|
-
return context[DRIZZLE_PROPERTY];
|
|
500
|
+
return context[constants.DRIZZLE_PROPERTY];
|
|
447
501
|
}
|
|
448
502
|
function drizzleStorage({
|
|
449
503
|
db,
|
|
450
504
|
persistState: enablePersistence = true,
|
|
451
505
|
indexerName: identifier = "default",
|
|
452
506
|
schema,
|
|
453
|
-
idColumn = "id"
|
|
507
|
+
idColumn = "id",
|
|
508
|
+
migrate: migrateOptions
|
|
454
509
|
}) {
|
|
455
|
-
return plugins.defineIndexerPlugin((indexer) => {
|
|
510
|
+
return plugins.defineIndexerPlugin((indexer$1) => {
|
|
456
511
|
let tableNames = [];
|
|
457
512
|
let indexerId = "";
|
|
458
513
|
try {
|
|
@@ -464,12 +519,22 @@ function drizzleStorage({
|
|
|
464
519
|
cause: error
|
|
465
520
|
});
|
|
466
521
|
}
|
|
467
|
-
indexer.hooks.hook("run:before", async () => {
|
|
468
|
-
const
|
|
522
|
+
indexer$1.hooks.hook("run:before", async () => {
|
|
523
|
+
const internalContext = plugins$1.useInternalContext();
|
|
524
|
+
const context = indexer.useIndexerContext();
|
|
525
|
+
const logger = plugins.useLogger();
|
|
526
|
+
context[constants.DRIZZLE_STORAGE_DB_PROPERTY] = db;
|
|
527
|
+
const { indexerName: indexerFileName, availableIndexers } = internalContext;
|
|
469
528
|
indexerId = internal.generateIndexerId(indexerFileName, identifier);
|
|
470
529
|
let retries = 0;
|
|
530
|
+
let migrationsApplied = false;
|
|
471
531
|
while (retries <= MAX_RETRIES) {
|
|
472
532
|
try {
|
|
533
|
+
if (migrateOptions && !migrationsApplied) {
|
|
534
|
+
await migrate(db, migrateOptions);
|
|
535
|
+
migrationsApplied = true;
|
|
536
|
+
logger.success("Migrations applied");
|
|
537
|
+
}
|
|
473
538
|
await withTransaction(db, async (tx) => {
|
|
474
539
|
await initializeReorgRollbackTable(tx, indexerId);
|
|
475
540
|
if (enablePersistence) {
|
|
@@ -479,6 +544,9 @@ function drizzleStorage({
|
|
|
479
544
|
break;
|
|
480
545
|
} catch (error) {
|
|
481
546
|
if (retries === MAX_RETRIES) {
|
|
547
|
+
if (error instanceof DrizzleStorageError) {
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
482
550
|
throw new DrizzleStorageError(
|
|
483
551
|
"Initialization failed after 5 retries",
|
|
484
552
|
{
|
|
@@ -491,7 +559,7 @@ function drizzleStorage({
|
|
|
491
559
|
}
|
|
492
560
|
}
|
|
493
561
|
});
|
|
494
|
-
indexer.hooks.hook("connect:before", async ({ request }) => {
|
|
562
|
+
indexer$1.hooks.hook("connect:before", async ({ request }) => {
|
|
495
563
|
if (!enablePersistence) {
|
|
496
564
|
return;
|
|
497
565
|
}
|
|
@@ -508,7 +576,7 @@ function drizzleStorage({
|
|
|
508
576
|
}
|
|
509
577
|
});
|
|
510
578
|
});
|
|
511
|
-
indexer.hooks.hook("connect:after", async ({ request }) => {
|
|
579
|
+
indexer$1.hooks.hook("connect:after", async ({ request }) => {
|
|
512
580
|
const cursor = request.startingCursor;
|
|
513
581
|
if (!cursor) {
|
|
514
582
|
return;
|
|
@@ -520,7 +588,7 @@ function drizzleStorage({
|
|
|
520
588
|
}
|
|
521
589
|
});
|
|
522
590
|
});
|
|
523
|
-
indexer.hooks.hook("connect:factory", async ({ request, endCursor }) => {
|
|
591
|
+
indexer$1.hooks.hook("connect:factory", async ({ request, endCursor }) => {
|
|
524
592
|
if (!enablePersistence) {
|
|
525
593
|
return;
|
|
526
594
|
}
|
|
@@ -534,7 +602,7 @@ function drizzleStorage({
|
|
|
534
602
|
});
|
|
535
603
|
}
|
|
536
604
|
});
|
|
537
|
-
indexer.hooks.hook("message:finalize", async ({ message }) => {
|
|
605
|
+
indexer$1.hooks.hook("message:finalize", async ({ message }) => {
|
|
538
606
|
const { cursor } = message.finalize;
|
|
539
607
|
if (!cursor) {
|
|
540
608
|
throw new DrizzleStorageError("Finalized Cursor is undefined");
|
|
@@ -546,7 +614,7 @@ function drizzleStorage({
|
|
|
546
614
|
}
|
|
547
615
|
});
|
|
548
616
|
});
|
|
549
|
-
indexer.hooks.hook("message:invalidate", async ({ message }) => {
|
|
617
|
+
indexer$1.hooks.hook("message:invalidate", async ({ message }) => {
|
|
550
618
|
const { cursor } = message.invalidate;
|
|
551
619
|
if (!cursor) {
|
|
552
620
|
throw new DrizzleStorageError("Invalidate Cursor is undefined");
|
|
@@ -558,7 +626,7 @@ function drizzleStorage({
|
|
|
558
626
|
}
|
|
559
627
|
});
|
|
560
628
|
});
|
|
561
|
-
indexer.hooks.hook("handler:middleware", async ({ use }) => {
|
|
629
|
+
indexer$1.hooks.hook("handler:middleware", async ({ use }) => {
|
|
562
630
|
use(async (context, next) => {
|
|
563
631
|
try {
|
|
564
632
|
const { endCursor, finality } = context;
|
|
@@ -566,7 +634,7 @@ function drizzleStorage({
|
|
|
566
634
|
throw new DrizzleStorageError("End Cursor is undefined");
|
|
567
635
|
}
|
|
568
636
|
await withTransaction(db, async (tx) => {
|
|
569
|
-
context[DRIZZLE_PROPERTY] = { db: tx };
|
|
637
|
+
context[constants.DRIZZLE_PROPERTY] = { db: tx };
|
|
570
638
|
if (finality !== "finalized") {
|
|
571
639
|
await registerTriggers(
|
|
572
640
|
tx,
|
|
@@ -577,7 +645,7 @@ function drizzleStorage({
|
|
|
577
645
|
);
|
|
578
646
|
}
|
|
579
647
|
await next();
|
|
580
|
-
delete context[DRIZZLE_PROPERTY];
|
|
648
|
+
delete context[constants.DRIZZLE_PROPERTY];
|
|
581
649
|
if (enablePersistence) {
|
|
582
650
|
await persistState({
|
|
583
651
|
tx,
|
|
@@ -600,5 +668,7 @@ function drizzleStorage({
|
|
|
600
668
|
});
|
|
601
669
|
}
|
|
602
670
|
|
|
671
|
+
exports.drizzle = drizzle;
|
|
603
672
|
exports.drizzleStorage = drizzleStorage;
|
|
673
|
+
exports.migrate = migrate;
|
|
604
674
|
exports.useDrizzleStorage = useDrizzleStorage;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,17 +1,144 @@
|
|
|
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 "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
|
+
* @param options - Configuration options for the database connection
|
|
83
|
+
* @returns A configured Drizzle database instance
|
|
84
|
+
* @throws {Error} If an invalid database type is specified
|
|
85
|
+
*/
|
|
86
|
+
declare function drizzle<TSchema extends Record<string, unknown>, TOptions extends DrizzleOptions>(options?: TOptions & {
|
|
87
|
+
/**
|
|
88
|
+
* Schema to use for the database
|
|
89
|
+
* @default {}
|
|
90
|
+
*/
|
|
91
|
+
schema?: TSchema;
|
|
92
|
+
}): Database<TOptions, TSchema>;
|
|
93
|
+
/**
|
|
94
|
+
* Options for database migration
|
|
95
|
+
*/
|
|
96
|
+
type MigrateOptions = MigrationConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Performs database migration based on the provided configuration
|
|
99
|
+
* @param db - The database instance to migrate
|
|
100
|
+
* @param options - Migration configuration options
|
|
101
|
+
*
|
|
102
|
+
* @important This function runs migrations on the database instance provided to the `drizzleStorage` plugin.
|
|
103
|
+
* It automatically detects the type of database and runs the appropriate migrate function
|
|
104
|
+
* (PGLite or Node-Postgres).
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* await migrate(db, { migrationsFolder: "./drizzle" });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function migrate<TSchema extends Record<string, unknown>>(db: PgliteDatabase<TSchema> | NodePgDatabase<TSchema>, options: MigrateOptions): Promise<void>;
|
|
4
112
|
|
|
5
113
|
type DrizzleStorage<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> = {
|
|
6
114
|
db: PgTransaction<TQueryResult, TFullSchema, TSchema>;
|
|
7
115
|
};
|
|
8
116
|
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
117
|
interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> {
|
|
118
|
+
/**
|
|
119
|
+
* The Drizzle database instance.
|
|
120
|
+
*/
|
|
10
121
|
db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
122
|
+
/**
|
|
123
|
+
* Whether to persist the indexer's state. Defaults to true.
|
|
124
|
+
*/
|
|
11
125
|
persistState?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* The name of the indexer. Default value is 'default'.
|
|
128
|
+
*/
|
|
12
129
|
indexerName?: string;
|
|
130
|
+
/**
|
|
131
|
+
* The schema of the database.
|
|
132
|
+
*/
|
|
13
133
|
schema?: Record<string, unknown>;
|
|
134
|
+
/**
|
|
135
|
+
* The column to use as the id. Defaults to 'id'.
|
|
136
|
+
*/
|
|
14
137
|
idColumn?: string;
|
|
138
|
+
/**
|
|
139
|
+
* The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
|
|
140
|
+
*/
|
|
141
|
+
migrate?: MigrateOptions;
|
|
15
142
|
}
|
|
16
143
|
/**
|
|
17
144
|
* Creates a plugin that uses Drizzle as the storage layer.
|
|
@@ -22,7 +149,8 @@ interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSche
|
|
|
22
149
|
* @param options.indexerName - The name of the indexer. Defaults value is 'default'.
|
|
23
150
|
* @param options.schema - The schema of the database.
|
|
24
151
|
* @param options.idColumn - The column to use as the id. Defaults to 'id'.
|
|
152
|
+
* @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
|
|
25
153
|
*/
|
|
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>;
|
|
154
|
+
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
155
|
|
|
28
|
-
export { type DrizzleStorage, type DrizzleStorageOptions, drizzleStorage, useDrizzleStorage };
|
|
156
|
+
export { type Database, type DrizzleOptions, type DrizzleStorage, type DrizzleStorageOptions, type MigrateOptions, type NodePgDatabase, type NodePgDrizzleOptions, type PgliteDatabase, type PgliteDrizzleOptions, drizzle, drizzleStorage, migrate, useDrizzleStorage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,17 +1,144 @@
|
|
|
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 "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
|
+
* @param options - Configuration options for the database connection
|
|
83
|
+
* @returns A configured Drizzle database instance
|
|
84
|
+
* @throws {Error} If an invalid database type is specified
|
|
85
|
+
*/
|
|
86
|
+
declare function drizzle<TSchema extends Record<string, unknown>, TOptions extends DrizzleOptions>(options?: TOptions & {
|
|
87
|
+
/**
|
|
88
|
+
* Schema to use for the database
|
|
89
|
+
* @default {}
|
|
90
|
+
*/
|
|
91
|
+
schema?: TSchema;
|
|
92
|
+
}): Database<TOptions, TSchema>;
|
|
93
|
+
/**
|
|
94
|
+
* Options for database migration
|
|
95
|
+
*/
|
|
96
|
+
type MigrateOptions = MigrationConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Performs database migration based on the provided configuration
|
|
99
|
+
* @param db - The database instance to migrate
|
|
100
|
+
* @param options - Migration configuration options
|
|
101
|
+
*
|
|
102
|
+
* @important This function runs migrations on the database instance provided to the `drizzleStorage` plugin.
|
|
103
|
+
* It automatically detects the type of database and runs the appropriate migrate function
|
|
104
|
+
* (PGLite or Node-Postgres).
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* await migrate(db, { migrationsFolder: "./drizzle" });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function migrate<TSchema extends Record<string, unknown>>(db: PgliteDatabase<TSchema> | NodePgDatabase<TSchema>, options: MigrateOptions): Promise<void>;
|
|
4
112
|
|
|
5
113
|
type DrizzleStorage<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> = {
|
|
6
114
|
db: PgTransaction<TQueryResult, TFullSchema, TSchema>;
|
|
7
115
|
};
|
|
8
116
|
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
117
|
interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> {
|
|
118
|
+
/**
|
|
119
|
+
* The Drizzle database instance.
|
|
120
|
+
*/
|
|
10
121
|
db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
122
|
+
/**
|
|
123
|
+
* Whether to persist the indexer's state. Defaults to true.
|
|
124
|
+
*/
|
|
11
125
|
persistState?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* The name of the indexer. Default value is 'default'.
|
|
128
|
+
*/
|
|
12
129
|
indexerName?: string;
|
|
130
|
+
/**
|
|
131
|
+
* The schema of the database.
|
|
132
|
+
*/
|
|
13
133
|
schema?: Record<string, unknown>;
|
|
134
|
+
/**
|
|
135
|
+
* The column to use as the id. Defaults to 'id'.
|
|
136
|
+
*/
|
|
14
137
|
idColumn?: string;
|
|
138
|
+
/**
|
|
139
|
+
* The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
|
|
140
|
+
*/
|
|
141
|
+
migrate?: MigrateOptions;
|
|
15
142
|
}
|
|
16
143
|
/**
|
|
17
144
|
* Creates a plugin that uses Drizzle as the storage layer.
|
|
@@ -22,7 +149,8 @@ interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSche
|
|
|
22
149
|
* @param options.indexerName - The name of the indexer. Defaults value is 'default'.
|
|
23
150
|
* @param options.schema - The schema of the database.
|
|
24
151
|
* @param options.idColumn - The column to use as the id. Defaults to 'id'.
|
|
152
|
+
* @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
|
|
25
153
|
*/
|
|
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>;
|
|
154
|
+
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
155
|
|
|
28
|
-
export { type DrizzleStorage, type DrizzleStorageOptions, drizzleStorage, useDrizzleStorage };
|
|
156
|
+
export { type Database, type DrizzleOptions, type DrizzleStorage, type DrizzleStorageOptions, type MigrateOptions, type NodePgDatabase, type NodePgDrizzleOptions, type PgliteDatabase, type PgliteDrizzleOptions, drizzle, drizzleStorage, migrate, useDrizzleStorage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,144 @@
|
|
|
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 "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
|
+
* @param options - Configuration options for the database connection
|
|
83
|
+
* @returns A configured Drizzle database instance
|
|
84
|
+
* @throws {Error} If an invalid database type is specified
|
|
85
|
+
*/
|
|
86
|
+
declare function drizzle<TSchema extends Record<string, unknown>, TOptions extends DrizzleOptions>(options?: TOptions & {
|
|
87
|
+
/**
|
|
88
|
+
* Schema to use for the database
|
|
89
|
+
* @default {}
|
|
90
|
+
*/
|
|
91
|
+
schema?: TSchema;
|
|
92
|
+
}): Database<TOptions, TSchema>;
|
|
93
|
+
/**
|
|
94
|
+
* Options for database migration
|
|
95
|
+
*/
|
|
96
|
+
type MigrateOptions = MigrationConfig;
|
|
97
|
+
/**
|
|
98
|
+
* Performs database migration based on the provided configuration
|
|
99
|
+
* @param db - The database instance to migrate
|
|
100
|
+
* @param options - Migration configuration options
|
|
101
|
+
*
|
|
102
|
+
* @important This function runs migrations on the database instance provided to the `drizzleStorage` plugin.
|
|
103
|
+
* It automatically detects the type of database and runs the appropriate migrate function
|
|
104
|
+
* (PGLite or Node-Postgres).
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* await migrate(db, { migrationsFolder: "./drizzle" });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function migrate<TSchema extends Record<string, unknown>>(db: PgliteDatabase<TSchema> | NodePgDatabase<TSchema>, options: MigrateOptions): Promise<void>;
|
|
4
112
|
|
|
5
113
|
type DrizzleStorage<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> = {
|
|
6
114
|
db: PgTransaction<TQueryResult, TFullSchema, TSchema>;
|
|
7
115
|
};
|
|
8
116
|
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
117
|
interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>> {
|
|
118
|
+
/**
|
|
119
|
+
* The Drizzle database instance.
|
|
120
|
+
*/
|
|
10
121
|
db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
122
|
+
/**
|
|
123
|
+
* Whether to persist the indexer's state. Defaults to true.
|
|
124
|
+
*/
|
|
11
125
|
persistState?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* The name of the indexer. Default value is 'default'.
|
|
128
|
+
*/
|
|
12
129
|
indexerName?: string;
|
|
130
|
+
/**
|
|
131
|
+
* The schema of the database.
|
|
132
|
+
*/
|
|
13
133
|
schema?: Record<string, unknown>;
|
|
134
|
+
/**
|
|
135
|
+
* The column to use as the id. Defaults to 'id'.
|
|
136
|
+
*/
|
|
14
137
|
idColumn?: string;
|
|
138
|
+
/**
|
|
139
|
+
* The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
|
|
140
|
+
*/
|
|
141
|
+
migrate?: MigrateOptions;
|
|
15
142
|
}
|
|
16
143
|
/**
|
|
17
144
|
* Creates a plugin that uses Drizzle as the storage layer.
|
|
@@ -22,7 +149,8 @@ interface DrizzleStorageOptions<TQueryResult extends PgQueryResultHKT, TFullSche
|
|
|
22
149
|
* @param options.indexerName - The name of the indexer. Defaults value is 'default'.
|
|
23
150
|
* @param options.schema - The schema of the database.
|
|
24
151
|
* @param options.idColumn - The column to use as the id. Defaults to 'id'.
|
|
152
|
+
* @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
|
|
25
153
|
*/
|
|
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>;
|
|
154
|
+
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
155
|
|
|
28
|
-
export { type DrizzleStorage, type DrizzleStorageOptions, drizzleStorage, useDrizzleStorage };
|
|
156
|
+
export { type Database, type DrizzleOptions, type DrizzleStorage, type DrizzleStorageOptions, type MigrateOptions, type NodePgDatabase, type NodePgDrizzleOptions, type PgliteDatabase, type PgliteDrizzleOptions, drizzle, drizzleStorage, migrate, useDrizzleStorage };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
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 { D as DRIZZLE_PROPERTY, a as DRIZZLE_STORAGE_DB_PROPERTY } from './shared/plugin-drizzle.f8d1b186.mjs';
|
|
6
|
+
import { PGlite } from '@electric-sql/pglite';
|
|
7
|
+
import { drizzle as drizzle$1 } from 'drizzle-orm/node-postgres';
|
|
8
|
+
import { migrate as migrate$2 } from 'drizzle-orm/node-postgres/migrator';
|
|
9
|
+
import { drizzle as drizzle$2 } from 'drizzle-orm/pglite';
|
|
10
|
+
import { migrate as migrate$1 } from 'drizzle-orm/pglite/migrator';
|
|
11
|
+
import pg from 'pg';
|
|
5
12
|
import { normalizeCursor } from '@apibara/protocol';
|
|
6
13
|
import { eq, and, isNull, gt, lt, sql } from 'drizzle-orm';
|
|
7
14
|
import { pgTable, text, integer, primaryKey, serial, char, jsonb } from 'drizzle-orm/pg-core';
|
|
@@ -34,6 +41,50 @@ function sleep(ms) {
|
|
|
34
41
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
function drizzle(options) {
|
|
45
|
+
const {
|
|
46
|
+
connectionString = "memory://",
|
|
47
|
+
schema,
|
|
48
|
+
type = "pglite",
|
|
49
|
+
config,
|
|
50
|
+
poolConfig
|
|
51
|
+
} = options ?? {};
|
|
52
|
+
if (connectionString.startsWith("postgres://") || type === "node-postgres") {
|
|
53
|
+
const pool = new pg.Pool({
|
|
54
|
+
connectionString,
|
|
55
|
+
...poolConfig || {}
|
|
56
|
+
});
|
|
57
|
+
return drizzle$1(pool, { schema, ...config || {} });
|
|
58
|
+
}
|
|
59
|
+
if (type === "pglite") {
|
|
60
|
+
return drizzle$2({
|
|
61
|
+
schema,
|
|
62
|
+
connection: {
|
|
63
|
+
dataDir: connectionString || "memory://pglite"
|
|
64
|
+
},
|
|
65
|
+
...config || {}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
throw new Error("Invalid database type");
|
|
69
|
+
}
|
|
70
|
+
async function migrate(db, options) {
|
|
71
|
+
const isPglite = !!("$client" in db && db.$client instanceof PGlite);
|
|
72
|
+
try {
|
|
73
|
+
if (isPglite) {
|
|
74
|
+
await migrate$1(db, options);
|
|
75
|
+
} else {
|
|
76
|
+
await migrate$2(db, options);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new DrizzleStorageError(
|
|
80
|
+
"Failed to apply migrations! Please check if you have generated migrations using drizzle:generate",
|
|
81
|
+
{
|
|
82
|
+
cause: error
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
37
88
|
const CHECKPOINTS_TABLE_NAME = "__indexer_checkpoints";
|
|
38
89
|
const FILTERS_TABLE_NAME = "__indexer_filters";
|
|
39
90
|
const SCHEMA_VERSION_TABLE_NAME = "__indexer_schema_version";
|
|
@@ -432,7 +483,6 @@ async function finalize(tx, cursor, indexerId) {
|
|
|
432
483
|
}
|
|
433
484
|
}
|
|
434
485
|
|
|
435
|
-
const DRIZZLE_PROPERTY = "_drizzle";
|
|
436
486
|
const MAX_RETRIES = 5;
|
|
437
487
|
function useDrizzleStorage(_db) {
|
|
438
488
|
const context = useIndexerContext();
|
|
@@ -448,7 +498,8 @@ function drizzleStorage({
|
|
|
448
498
|
persistState: enablePersistence = true,
|
|
449
499
|
indexerName: identifier = "default",
|
|
450
500
|
schema,
|
|
451
|
-
idColumn = "id"
|
|
501
|
+
idColumn = "id",
|
|
502
|
+
migrate: migrateOptions
|
|
452
503
|
}) {
|
|
453
504
|
return defineIndexerPlugin((indexer) => {
|
|
454
505
|
let tableNames = [];
|
|
@@ -463,11 +514,21 @@ function drizzleStorage({
|
|
|
463
514
|
});
|
|
464
515
|
}
|
|
465
516
|
indexer.hooks.hook("run:before", async () => {
|
|
466
|
-
const
|
|
517
|
+
const internalContext = useInternalContext();
|
|
518
|
+
const context = useIndexerContext();
|
|
519
|
+
const logger = useLogger();
|
|
520
|
+
context[DRIZZLE_STORAGE_DB_PROPERTY] = db;
|
|
521
|
+
const { indexerName: indexerFileName, availableIndexers } = internalContext;
|
|
467
522
|
indexerId = generateIndexerId(indexerFileName, identifier);
|
|
468
523
|
let retries = 0;
|
|
524
|
+
let migrationsApplied = false;
|
|
469
525
|
while (retries <= MAX_RETRIES) {
|
|
470
526
|
try {
|
|
527
|
+
if (migrateOptions && !migrationsApplied) {
|
|
528
|
+
await migrate(db, migrateOptions);
|
|
529
|
+
migrationsApplied = true;
|
|
530
|
+
logger.success("Migrations applied");
|
|
531
|
+
}
|
|
471
532
|
await withTransaction(db, async (tx) => {
|
|
472
533
|
await initializeReorgRollbackTable(tx, indexerId);
|
|
473
534
|
if (enablePersistence) {
|
|
@@ -477,6 +538,9 @@ function drizzleStorage({
|
|
|
477
538
|
break;
|
|
478
539
|
} catch (error) {
|
|
479
540
|
if (retries === MAX_RETRIES) {
|
|
541
|
+
if (error instanceof DrizzleStorageError) {
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
480
544
|
throw new DrizzleStorageError(
|
|
481
545
|
"Initialization failed after 5 retries",
|
|
482
546
|
{
|
|
@@ -598,4 +662,4 @@ function drizzleStorage({
|
|
|
598
662
|
});
|
|
599
663
|
}
|
|
600
664
|
|
|
601
|
-
export { drizzleStorage, useDrizzleStorage };
|
|
665
|
+
export { drizzle, drizzleStorage, migrate, useDrizzleStorage };
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const constants = require('./shared/plugin-drizzle.e884ca32.cjs');
|
|
4
|
+
|
|
5
|
+
function getTestDatabase(context) {
|
|
6
|
+
const db = context[constants.DRIZZLE_STORAGE_DB_PROPERTY];
|
|
7
|
+
if (!db) {
|
|
8
|
+
throw new Error("Drizzle database not found in context");
|
|
9
|
+
}
|
|
10
|
+
return db;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.getTestDatabase = getTestDatabase;
|
package/dist/testing.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { a as DRIZZLE_STORAGE_DB_PROPERTY } from './shared/plugin-drizzle.f8d1b186.mjs';
|
|
2
|
+
|
|
3
|
+
function getTestDatabase(context) {
|
|
4
|
+
const db = context[DRIZZLE_STORAGE_DB_PROPERTY];
|
|
5
|
+
if (!db) {
|
|
6
|
+
throw new Error("Drizzle database not found in context");
|
|
7
|
+
}
|
|
8
|
+
return db;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { getTestDatabase };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apibara/plugin-drizzle",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"import": "./dist/index.mjs",
|
|
16
16
|
"require": "./dist/index.cjs",
|
|
17
17
|
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./testing": {
|
|
20
|
+
"types": "./dist/testing.d.ts",
|
|
21
|
+
"import": "./dist/testing.mjs",
|
|
22
|
+
"require": "./dist/testing.cjs",
|
|
23
|
+
"default": "./dist/testing.mjs"
|
|
18
24
|
}
|
|
19
25
|
},
|
|
20
26
|
"scripts": {
|
|
@@ -39,8 +45,8 @@
|
|
|
39
45
|
"vitest": "^1.6.0"
|
|
40
46
|
},
|
|
41
47
|
"dependencies": {
|
|
42
|
-
"@apibara/indexer": "2.1.0-beta.
|
|
43
|
-
"@apibara/protocol": "2.1.0-beta.
|
|
48
|
+
"@apibara/indexer": "2.1.0-beta.7",
|
|
49
|
+
"@apibara/protocol": "2.1.0-beta.7",
|
|
44
50
|
"postgres-range": "^1.1.4"
|
|
45
51
|
}
|
|
46
52
|
}
|
package/src/constants.ts
ADDED
package/src/helper.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { PGlite, type PGliteOptions } from "@electric-sql/pglite";
|
|
2
|
+
import type { DrizzleConfig } from "drizzle-orm";
|
|
3
|
+
import type { MigrationConfig } from "drizzle-orm/migrator";
|
|
4
|
+
import {
|
|
5
|
+
type NodePgDatabase as OriginalNodePgDatabase,
|
|
6
|
+
drizzle as drizzleNode,
|
|
7
|
+
} from "drizzle-orm/node-postgres";
|
|
8
|
+
import { migrate as migrateNode } from "drizzle-orm/node-postgres/migrator";
|
|
9
|
+
import {} from "drizzle-orm/pg-core";
|
|
10
|
+
import {
|
|
11
|
+
type PgliteDatabase as OriginalPgliteDatabase,
|
|
12
|
+
drizzle as drizzlePGLite,
|
|
13
|
+
} from "drizzle-orm/pglite";
|
|
14
|
+
import { migrate as migratePGLite } from "drizzle-orm/pglite/migrator";
|
|
15
|
+
import pg from "pg";
|
|
16
|
+
import { DrizzleStorageError } from "./utils";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Union type of all possible drizzle database options
|
|
20
|
+
*/
|
|
21
|
+
export type DrizzleOptions = PgliteDrizzleOptions | NodePgDrizzleOptions;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Configuration options for Node-Postgres database connection
|
|
25
|
+
*/
|
|
26
|
+
export type NodePgDrizzleOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Type of database to use -
|
|
29
|
+
* - "pglite" - PGLite database
|
|
30
|
+
* - "node-postgres" - Node-Postgres database
|
|
31
|
+
* @default "pglite"
|
|
32
|
+
*/
|
|
33
|
+
type: "node-postgres";
|
|
34
|
+
/**
|
|
35
|
+
* Connection string to use for the database
|
|
36
|
+
* @default ""
|
|
37
|
+
*/
|
|
38
|
+
connectionString?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Pool configuration options for Node-Postgres
|
|
41
|
+
*/
|
|
42
|
+
poolConfig?: pg.PoolConfig;
|
|
43
|
+
/**
|
|
44
|
+
* Additional drizzle configuration options
|
|
45
|
+
*/
|
|
46
|
+
config?: Omit<DrizzleConfig, "schema">;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configuration options for PGLite database connection
|
|
51
|
+
*/
|
|
52
|
+
export type PgliteDrizzleOptions = {
|
|
53
|
+
/**
|
|
54
|
+
* Type of database to use -
|
|
55
|
+
* - "pglite" - PGLite database
|
|
56
|
+
* - "node-postgres" - Node-Postgres database
|
|
57
|
+
*/
|
|
58
|
+
type?: "pglite";
|
|
59
|
+
/**
|
|
60
|
+
* Connection string to use for the database
|
|
61
|
+
* @default "memory://pglite"
|
|
62
|
+
*/
|
|
63
|
+
connectionString?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Pool configuration is not supported for PGLite
|
|
66
|
+
*/
|
|
67
|
+
poolConfig?: never;
|
|
68
|
+
/**
|
|
69
|
+
* Additional drizzle configuration options with PGLite specific connection options
|
|
70
|
+
*/
|
|
71
|
+
config?: Omit<DrizzleConfig, "schema"> & {
|
|
72
|
+
connection?:
|
|
73
|
+
| (PGliteOptions & {
|
|
74
|
+
dataDir?: string;
|
|
75
|
+
})
|
|
76
|
+
| string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extended PGLite database type with client information
|
|
82
|
+
*/
|
|
83
|
+
export type PgliteDatabase<TSchema extends Record<string, unknown>> =
|
|
84
|
+
OriginalPgliteDatabase<TSchema> & {
|
|
85
|
+
$client: PGlite;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extended Node-Postgres database type with client information
|
|
90
|
+
*/
|
|
91
|
+
export type NodePgDatabase<TSchema extends Record<string, unknown>> =
|
|
92
|
+
OriginalNodePgDatabase<TSchema> & {
|
|
93
|
+
$client: pg.Pool;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export type Database<
|
|
97
|
+
TOptions extends DrizzleOptions,
|
|
98
|
+
TSchema extends Record<string, unknown>,
|
|
99
|
+
> = TOptions extends PgliteDrizzleOptions
|
|
100
|
+
? PgliteDatabase<TSchema>
|
|
101
|
+
: NodePgDatabase<TSchema>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a new Drizzle database instance based on the provided options
|
|
105
|
+
* @param options - Configuration options for the database connection
|
|
106
|
+
* @returns A configured Drizzle database instance
|
|
107
|
+
* @throws {Error} If an invalid database type is specified
|
|
108
|
+
*/
|
|
109
|
+
export function drizzle<
|
|
110
|
+
TSchema extends Record<string, unknown>,
|
|
111
|
+
TOptions extends DrizzleOptions,
|
|
112
|
+
>(
|
|
113
|
+
options?: TOptions & {
|
|
114
|
+
/**
|
|
115
|
+
* Schema to use for the database
|
|
116
|
+
* @default {}
|
|
117
|
+
*/
|
|
118
|
+
schema?: TSchema;
|
|
119
|
+
},
|
|
120
|
+
): Database<TOptions, TSchema> {
|
|
121
|
+
const {
|
|
122
|
+
connectionString = "memory://",
|
|
123
|
+
schema,
|
|
124
|
+
type = "pglite",
|
|
125
|
+
config,
|
|
126
|
+
poolConfig,
|
|
127
|
+
} = options ?? {};
|
|
128
|
+
|
|
129
|
+
if (connectionString.startsWith("postgres://") || type === "node-postgres") {
|
|
130
|
+
const pool = new pg.Pool({
|
|
131
|
+
connectionString,
|
|
132
|
+
...(poolConfig || {}),
|
|
133
|
+
});
|
|
134
|
+
return drizzleNode(pool, { schema, ...(config || {}) }) as Database<
|
|
135
|
+
TOptions,
|
|
136
|
+
TSchema
|
|
137
|
+
>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (type === "pglite") {
|
|
141
|
+
return drizzlePGLite({
|
|
142
|
+
schema: schema as TSchema,
|
|
143
|
+
connection: {
|
|
144
|
+
dataDir: connectionString || "memory://pglite",
|
|
145
|
+
},
|
|
146
|
+
...(config || {}),
|
|
147
|
+
}) as Database<TOptions, TSchema>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
throw new Error("Invalid database type");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Options for database migration
|
|
155
|
+
*/
|
|
156
|
+
export type MigrateOptions = MigrationConfig;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Performs database migration based on the provided configuration
|
|
160
|
+
* @param db - The database instance to migrate
|
|
161
|
+
* @param options - Migration configuration options
|
|
162
|
+
*
|
|
163
|
+
* @important This function runs migrations on the database instance provided to the `drizzleStorage` plugin.
|
|
164
|
+
* It automatically detects the type of database and runs the appropriate migrate function
|
|
165
|
+
* (PGLite or Node-Postgres).
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* await migrate(db, { migrationsFolder: "./drizzle" });
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export async function migrate<TSchema extends Record<string, unknown>>(
|
|
173
|
+
db: PgliteDatabase<TSchema> | NodePgDatabase<TSchema>,
|
|
174
|
+
options: MigrateOptions,
|
|
175
|
+
) {
|
|
176
|
+
const isPglite = !!("$client" in db && db.$client instanceof PGlite);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
if (isPglite) {
|
|
180
|
+
await migratePGLite(db as PgliteDatabase<TSchema>, options);
|
|
181
|
+
} else {
|
|
182
|
+
await migrateNode(db as NodePgDatabase<TSchema>, options);
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new DrizzleStorageError(
|
|
186
|
+
"Failed to apply migrations! Please check if you have generated migrations using drizzle:generate",
|
|
187
|
+
{
|
|
188
|
+
cause: error,
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useIndexerContext } from "@apibara/indexer";
|
|
2
|
-
import { defineIndexerPlugin } from "@apibara/indexer/plugins";
|
|
2
|
+
import { defineIndexerPlugin, useLogger } from "@apibara/indexer/plugins";
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
ExtractTablesWithRelations,
|
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
PgQueryResultHKT,
|
|
15
15
|
PgTransaction,
|
|
16
16
|
} from "drizzle-orm/pg-core";
|
|
17
|
+
import { DRIZZLE_PROPERTY, DRIZZLE_STORAGE_DB_PROPERTY } from "./constants";
|
|
18
|
+
import { type MigrateOptions, migrate } from "./helper";
|
|
17
19
|
import {
|
|
18
20
|
finalizeState,
|
|
19
21
|
getState,
|
|
@@ -30,7 +32,8 @@ import {
|
|
|
30
32
|
} from "./storage";
|
|
31
33
|
import { DrizzleStorageError, sleep, withTransaction } from "./utils";
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
export * from "./helper";
|
|
36
|
+
|
|
34
37
|
const MAX_RETRIES = 5;
|
|
35
38
|
|
|
36
39
|
export type DrizzleStorage<
|
|
@@ -67,11 +70,30 @@ export interface DrizzleStorageOptions<
|
|
|
67
70
|
TSchema extends
|
|
68
71
|
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
69
72
|
> {
|
|
73
|
+
/**
|
|
74
|
+
* The Drizzle database instance.
|
|
75
|
+
*/
|
|
70
76
|
db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
77
|
+
/**
|
|
78
|
+
* Whether to persist the indexer's state. Defaults to true.
|
|
79
|
+
*/
|
|
71
80
|
persistState?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The name of the indexer. Default value is 'default'.
|
|
83
|
+
*/
|
|
72
84
|
indexerName?: string;
|
|
85
|
+
/**
|
|
86
|
+
* The schema of the database.
|
|
87
|
+
*/
|
|
73
88
|
schema?: Record<string, unknown>;
|
|
89
|
+
/**
|
|
90
|
+
* The column to use as the id. Defaults to 'id'.
|
|
91
|
+
*/
|
|
74
92
|
idColumn?: string;
|
|
93
|
+
/**
|
|
94
|
+
* The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
|
|
95
|
+
*/
|
|
96
|
+
migrate?: MigrateOptions;
|
|
75
97
|
}
|
|
76
98
|
|
|
77
99
|
/**
|
|
@@ -83,6 +105,7 @@ export interface DrizzleStorageOptions<
|
|
|
83
105
|
* @param options.indexerName - The name of the indexer. Defaults value is 'default'.
|
|
84
106
|
* @param options.schema - The schema of the database.
|
|
85
107
|
* @param options.idColumn - The column to use as the id. Defaults to 'id'.
|
|
108
|
+
* @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
|
|
86
109
|
*/
|
|
87
110
|
export function drizzleStorage<
|
|
88
111
|
TFilter,
|
|
@@ -97,6 +120,7 @@ export function drizzleStorage<
|
|
|
97
120
|
indexerName: identifier = "default",
|
|
98
121
|
schema,
|
|
99
122
|
idColumn = "id",
|
|
123
|
+
migrate: migrateOptions,
|
|
100
124
|
}: DrizzleStorageOptions<TQueryResult, TFullSchema, TSchema>) {
|
|
101
125
|
return defineIndexerPlugin<TFilter, TBlock>((indexer) => {
|
|
102
126
|
let tableNames: string[] = [];
|
|
@@ -113,15 +137,31 @@ export function drizzleStorage<
|
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
indexer.hooks.hook("run:before", async () => {
|
|
140
|
+
const internalContext = useInternalContext();
|
|
141
|
+
const context = useIndexerContext();
|
|
142
|
+
const logger = useLogger();
|
|
143
|
+
|
|
144
|
+
// For testing purposes using vcr.
|
|
145
|
+
context[DRIZZLE_STORAGE_DB_PROPERTY] = db;
|
|
146
|
+
|
|
116
147
|
const { indexerName: indexerFileName, availableIndexers } =
|
|
117
|
-
|
|
148
|
+
internalContext;
|
|
118
149
|
|
|
119
150
|
indexerId = generateIndexerId(indexerFileName, identifier);
|
|
120
151
|
|
|
121
152
|
let retries = 0;
|
|
122
153
|
|
|
154
|
+
// incase the migrations are already applied, we don't want to run them again
|
|
155
|
+
let migrationsApplied = false;
|
|
156
|
+
|
|
123
157
|
while (retries <= MAX_RETRIES) {
|
|
124
158
|
try {
|
|
159
|
+
if (migrateOptions && !migrationsApplied) {
|
|
160
|
+
// @ts-ignore type mismatch for db
|
|
161
|
+
await migrate(db, migrateOptions);
|
|
162
|
+
migrationsApplied = true;
|
|
163
|
+
logger.success("Migrations applied");
|
|
164
|
+
}
|
|
125
165
|
await withTransaction(db, async (tx) => {
|
|
126
166
|
await initializeReorgRollbackTable(tx, indexerId);
|
|
127
167
|
if (enablePersistence) {
|
|
@@ -131,6 +171,9 @@ export function drizzleStorage<
|
|
|
131
171
|
break;
|
|
132
172
|
} catch (error) {
|
|
133
173
|
if (retries === MAX_RETRIES) {
|
|
174
|
+
if (error instanceof DrizzleStorageError) {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
134
177
|
throw new DrizzleStorageError(
|
|
135
178
|
"Initialization failed after 5 retries",
|
|
136
179
|
{
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VcrResult } from "@apibara/indexer/testing";
|
|
2
|
+
import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
|
|
3
|
+
import { DRIZZLE_STORAGE_DB_PROPERTY } from "./constants";
|
|
4
|
+
|
|
5
|
+
export function getTestDatabase(context: VcrResult) {
|
|
6
|
+
const db = context[DRIZZLE_STORAGE_DB_PROPERTY];
|
|
7
|
+
|
|
8
|
+
if (!db) {
|
|
9
|
+
throw new Error("Drizzle database not found in context");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return db as PgDatabase<PgQueryResultHKT>;
|
|
13
|
+
}
|