@apibara/plugin-drizzle 2.0.0-beta.27

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/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # `@apibara/plugin-drizzle`
2
+
3
+ TODO
4
+
5
+ ## Installation
6
+
7
+ TODO
package/dist/index.cjs ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ class DrizzleStorageError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "DrizzleStorageError";
7
+ }
8
+ }
9
+
10
+ function drizzleStorage() {
11
+ throw new DrizzleStorageError("Not implemented");
12
+ }
13
+
14
+ exports.drizzleStorage = drizzleStorage;
@@ -0,0 +1,3 @@
1
+ declare function drizzleStorage<TFilter, TBlock>(): void;
2
+
3
+ export { drizzleStorage };
@@ -0,0 +1,3 @@
1
+ declare function drizzleStorage<TFilter, TBlock>(): void;
2
+
3
+ export { drizzleStorage };
@@ -0,0 +1,3 @@
1
+ declare function drizzleStorage<TFilter, TBlock>(): void;
2
+
3
+ export { drizzleStorage };
package/dist/index.mjs ADDED
@@ -0,0 +1,12 @@
1
+ class DrizzleStorageError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "DrizzleStorageError";
5
+ }
6
+ }
7
+
8
+ function drizzleStorage() {
9
+ throw new DrizzleStorageError("Not implemented");
10
+ }
11
+
12
+ export { drizzleStorage };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@apibara/plugin-drizzle",
3
+ "version": "2.0.0-beta.27",
4
+ "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "src",
8
+ "README.md"
9
+ ],
10
+ "main": "./dist/index.mjs",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.cjs",
17
+ "default": "./dist/index.mjs"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "unbuild",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "biome check .",
24
+ "lint:fix": "pnpm lint --write",
25
+ "test": "vitest",
26
+ "test:ci": "vitest run"
27
+ },
28
+ "devDependencies": {
29
+ "@electric-sql/pglite": "^0.2.14",
30
+ "@types/node": "^20.14.0",
31
+ "unbuild": "^2.0.0",
32
+ "vitest": "^1.6.0"
33
+ },
34
+ "dependencies": {
35
+ "@apibara/indexer": "2.0.0-beta.28",
36
+ "@apibara/protocol": "2.0.0-beta.28",
37
+ "drizzle-orm": "^0.37.0",
38
+ "pg": "^8.13.1",
39
+ "postgres-range": "^1.1.4"
40
+ }
41
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { DrizzleStorageError } from "./utils";
2
+
3
+ export function drizzleStorage<TFilter, TBlock>() {
4
+ throw new DrizzleStorageError("Not implemented");
5
+ }
@@ -0,0 +1,7 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ describe("drizzleStorage", () => {
4
+ it("should be implemented", () => {
5
+ expect(true).toBe(true);
6
+ });
7
+ });
@@ -0,0 +1,194 @@
1
+ /*
2
+ import type { Cursor } from "@apibara/protocol";
3
+ import {
4
+ type ExtractTablesWithRelations,
5
+ type TablesRelationalConfig,
6
+ and,
7
+ eq,
8
+ isNull,
9
+ } from "drizzle-orm";
10
+ import {
11
+ type PgDatabase,
12
+ type PgQueryResultHKT,
13
+ integer,
14
+ pgTable,
15
+ primaryKey,
16
+ text,
17
+ } from "drizzle-orm/pg-core";
18
+ import { deserialize, serialize } from "../vcr";
19
+ import { defineIndexerPlugin } from "./config";
20
+
21
+ export const checkpoints = pgTable("checkpoints", {
22
+ id: text("id").notNull().primaryKey(),
23
+ orderKey: integer("order_key").notNull(),
24
+ uniqueKey: text("unique_key")
25
+ .$type<`0x${string}` | undefined>()
26
+ .notNull()
27
+ .default(undefined),
28
+ });
29
+
30
+ export const filters = pgTable(
31
+ "filters",
32
+ {
33
+ id: text("id").notNull(),
34
+ filter: text("filter").notNull(),
35
+ fromBlock: integer("from_block").notNull(),
36
+ toBlock: integer("to_block"),
37
+ },
38
+ (table) => ({
39
+ pk: primaryKey({ columns: [table.id, table.fromBlock] }),
40
+ }),
41
+ );
42
+
43
+ export function drizzlePersistence<
44
+ TFilter,
45
+ TBlock,
46
+ TTxnParams,
47
+ TQueryResult extends PgQueryResultHKT,
48
+ TFullSchema extends Record<string, unknown> = Record<string, never>,
49
+ TSchema extends
50
+ TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
51
+ >({
52
+ database,
53
+ indexerName = "default",
54
+ }: {
55
+ database: PgDatabase<TQueryResult, TFullSchema, TSchema>;
56
+ indexerName?: string;
57
+ }) {
58
+ return defineIndexerPlugin<TFilter, TBlock, TTxnParams>((indexer) => {
59
+ let store: DrizzlePersistence<TFilter, TQueryResult, TFullSchema, TSchema>;
60
+
61
+ indexer.hooks.hook("run:before", async () => {
62
+ store = new DrizzlePersistence(database, indexerName);
63
+ // Tables are created by user via migrations in Drizzle
64
+ });
65
+
66
+ indexer.hooks.hook("connect:before", async ({ request }) => {
67
+ const { cursor, filter } = await store.get();
68
+
69
+ if (cursor) {
70
+ request.startingCursor = cursor;
71
+ }
72
+
73
+ if (filter) {
74
+ request.filter[1] = filter;
75
+ }
76
+ });
77
+
78
+ indexer.hooks.hook("transaction:commit", async ({ endCursor }) => {
79
+ if (endCursor) {
80
+ await store.put({ cursor: endCursor });
81
+ }
82
+ });
83
+
84
+ indexer.hooks.hook("connect:factory", async ({ request, endCursor }) => {
85
+ if (request.filter[1]) {
86
+ await store.put({ cursor: endCursor, filter: request.filter[1] });
87
+ }
88
+ });
89
+ });
90
+ }
91
+
92
+ export class DrizzlePersistence<
93
+ TFilter,
94
+ TQueryResult extends PgQueryResultHKT,
95
+ TFullSchema extends Record<string, unknown> = Record<string, never>,
96
+ TSchema extends
97
+ TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
98
+ > {
99
+ constructor(
100
+ private _db: PgDatabase<TQueryResult, TFullSchema, TSchema>,
101
+ private _indexerName: string,
102
+ ) {}
103
+
104
+ public async get(): Promise<{ cursor?: Cursor; filter?: TFilter }> {
105
+ const cursor = await this._getCheckpoint();
106
+ const filter = await this._getFilter();
107
+
108
+ return { cursor, filter };
109
+ }
110
+
111
+ public async put({ cursor, filter }: { cursor?: Cursor; filter?: TFilter }) {
112
+ if (cursor) {
113
+ await this._putCheckpoint(cursor);
114
+
115
+ if (filter) {
116
+ await this._putFilter(filter, cursor);
117
+ }
118
+ }
119
+ }
120
+
121
+ // --- CHECKPOINTS TABLE METHODS ---
122
+
123
+ private async _getCheckpoint(): Promise<Cursor | undefined> {
124
+ const rows = await this._db
125
+ .select()
126
+ .from(checkpoints)
127
+ .where(eq(checkpoints.id, this._indexerName));
128
+
129
+ const row = rows[0];
130
+ if (!row) return undefined;
131
+
132
+ return {
133
+ orderKey: BigInt(row.orderKey),
134
+ uniqueKey: row.uniqueKey,
135
+ };
136
+ }
137
+
138
+ private async _putCheckpoint(cursor: Cursor) {
139
+ await this._db
140
+ .insert(checkpoints)
141
+ .values({
142
+ id: this._indexerName,
143
+ orderKey: Number(cursor.orderKey),
144
+ uniqueKey: cursor.uniqueKey,
145
+ })
146
+ .onConflictDoUpdate({
147
+ target: checkpoints.id,
148
+ set: {
149
+ orderKey: Number(cursor.orderKey),
150
+ uniqueKey: cursor.uniqueKey,
151
+ },
152
+ });
153
+ }
154
+
155
+ // --- FILTERS TABLE METHODS ---
156
+
157
+ private async _getFilter(): Promise<TFilter | undefined> {
158
+ const rows = await this._db
159
+ .select()
160
+ .from(filters)
161
+ .where(and(eq(filters.id, this._indexerName), isNull(filters.toBlock)));
162
+
163
+ const row = rows[0];
164
+
165
+ if (!row) return undefined;
166
+
167
+ return deserialize(row.filter) as TFilter;
168
+ }
169
+
170
+ private async _putFilter(filter: TFilter, endCursor: Cursor) {
171
+ // Update existing filter's to_block
172
+ await this._db
173
+ .update(filters)
174
+ .set({ toBlock: Number(endCursor.orderKey) })
175
+ .where(and(eq(filters.id, this._indexerName), isNull(filters.toBlock)));
176
+
177
+ // Insert new filter
178
+ await this._db
179
+ .insert(filters)
180
+ .values({
181
+ id: this._indexerName,
182
+ filter: serialize(filter as Record<string, unknown>),
183
+ fromBlock: Number(endCursor.orderKey),
184
+ })
185
+ .onConflictDoUpdate({
186
+ target: [filters.id, filters.fromBlock],
187
+ set: {
188
+ filter: serialize(filter as Record<string, unknown>),
189
+ fromBlock: Number(endCursor.orderKey),
190
+ },
191
+ });
192
+ }
193
+ }
194
+ */
package/src/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ export class DrizzleStorageError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = "DrizzleStorageError";
5
+ }
6
+ }