@apibara/indexer 2.0.0-beta.9 → 2.1.0-beta.2
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 +271 -39
- package/dist/index.d.cts +1 -16
- package/dist/index.d.mts +1 -16
- package/dist/index.d.ts +1 -16
- package/dist/index.mjs +262 -25
- package/dist/internal/index.cjs +10 -0
- package/dist/internal/index.d.cts +3 -0
- package/dist/internal/index.d.mts +3 -0
- package/dist/internal/index.d.ts +3 -0
- package/dist/internal/index.mjs +8 -0
- package/dist/internal/plugins.cjs +38 -0
- package/dist/internal/plugins.d.cts +13 -0
- package/dist/internal/plugins.d.mts +13 -0
- package/dist/internal/plugins.d.ts +13 -0
- package/dist/internal/plugins.mjs +34 -0
- package/dist/internal/testing.cjs +118 -0
- package/dist/internal/testing.d.cts +42 -0
- package/dist/internal/testing.d.mts +42 -0
- package/dist/internal/testing.d.ts +42 -0
- package/dist/internal/testing.mjs +113 -0
- package/dist/plugins/index.cjs +39 -3
- package/dist/plugins/index.d.cts +16 -2
- package/dist/plugins/index.d.mts +16 -2
- package/dist/plugins/index.d.ts +16 -2
- package/dist/plugins/index.mjs +36 -3
- package/dist/shared/indexer.077335f3.cjs +15 -0
- package/dist/shared/indexer.2416906c.cjs +29 -0
- package/dist/shared/indexer.601ceab0.cjs +7 -0
- package/dist/shared/indexer.9b21ddd2.mjs +5 -0
- package/dist/shared/indexer.a55ad619.mjs +12 -0
- package/dist/shared/indexer.fedcd831.d.cts +100 -0
- package/dist/shared/indexer.fedcd831.d.mts +100 -0
- package/dist/shared/indexer.fedcd831.d.ts +100 -0
- package/dist/shared/indexer.ff25c953.mjs +26 -0
- package/dist/testing/index.cjs +52 -50
- package/dist/testing/index.d.cts +8 -36
- package/dist/testing/index.d.mts +8 -36
- package/dist/testing/index.d.ts +8 -36
- package/dist/testing/index.mjs +47 -47
- package/dist/vcr/index.cjs +84 -17
- package/dist/vcr/index.d.cts +16 -7
- package/dist/vcr/index.d.mts +16 -7
- package/dist/vcr/index.d.ts +16 -7
- package/dist/vcr/index.mjs +75 -11
- package/package.json +22 -42
- package/src/compose.test.ts +76 -0
- package/src/compose.ts +71 -0
- package/src/context.ts +14 -8
- package/src/index.ts +0 -5
- package/src/indexer.test.ts +125 -186
- package/src/indexer.ts +278 -151
- package/src/internal/index.ts +6 -0
- package/src/internal/plugins.ts +1 -0
- package/src/internal/testing.ts +148 -0
- package/src/plugins/config.ts +4 -4
- package/src/plugins/context.ts +40 -0
- package/src/plugins/index.ts +8 -1
- package/src/plugins/logger.ts +30 -0
- package/src/plugins/persistence.ts +24 -187
- package/src/testing/index.ts +58 -3
- package/src/vcr/record.ts +5 -3
- package/src/vcr/replay.ts +8 -18
- package/dist/plugins/kv.cjs +0 -131
- package/dist/plugins/kv.d.cts +0 -32
- package/dist/plugins/kv.d.mts +0 -32
- package/dist/plugins/kv.d.ts +0 -32
- package/dist/plugins/kv.mjs +0 -124
- package/dist/plugins/persistence.cjs +0 -182
- package/dist/plugins/persistence.d.cts +0 -50
- package/dist/plugins/persistence.d.mts +0 -50
- package/dist/plugins/persistence.d.ts +0 -50
- package/dist/plugins/persistence.mjs +0 -179
- package/dist/shared/indexer.2c23c9cd.mjs +0 -35
- package/dist/shared/indexer.318d3617.cjs +0 -47
- package/dist/shared/indexer.36530330.mjs +0 -249
- package/dist/shared/indexer.500fd281.d.cts +0 -23
- package/dist/shared/indexer.541d43eb.cjs +0 -266
- package/dist/shared/indexer.93d6b2eb.mjs +0 -17
- package/dist/shared/indexer.a8b7ab1f.cjs +0 -25
- package/dist/shared/indexer.b9c8f0d8.d.cts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.mts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.ts +0 -19
- package/dist/shared/indexer.c7ed6b83.d.cts +0 -82
- package/dist/shared/indexer.e1856641.d.mts +0 -23
- package/dist/shared/indexer.e4f2430f.d.ts +0 -23
- package/dist/shared/indexer.e8bd138d.d.mts +0 -82
- package/dist/shared/indexer.f761abcd.d.ts +0 -82
- package/dist/sinks/csv.cjs +0 -85
- package/dist/sinks/csv.d.cts +0 -66
- package/dist/sinks/csv.d.mts +0 -66
- package/dist/sinks/csv.d.ts +0 -66
- package/dist/sinks/csv.mjs +0 -78
- package/dist/sinks/drizzle/index.cjs +0 -212
- package/dist/sinks/drizzle/index.d.cts +0 -153
- package/dist/sinks/drizzle/index.d.mts +0 -153
- package/dist/sinks/drizzle/index.d.ts +0 -153
- package/dist/sinks/drizzle/index.mjs +0 -198
- package/dist/sinks/sqlite.cjs +0 -90
- package/dist/sinks/sqlite.d.cts +0 -71
- package/dist/sinks/sqlite.d.mts +0 -71
- package/dist/sinks/sqlite.d.ts +0 -71
- package/dist/sinks/sqlite.mjs +0 -87
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useKVStore.ts +0 -12
- package/src/hooks/useSink.ts +0 -13
- package/src/plugins/kv.test.ts +0 -120
- package/src/plugins/kv.ts +0 -132
- package/src/plugins/persistence.test.ts +0 -151
- package/src/sink.ts +0 -36
- package/src/sinks/csv.test.ts +0 -65
- package/src/sinks/csv.ts +0 -159
- package/src/sinks/drizzle/Int8Range.ts +0 -52
- package/src/sinks/drizzle/delete.ts +0 -42
- package/src/sinks/drizzle/drizzle.test.ts +0 -239
- package/src/sinks/drizzle/drizzle.ts +0 -115
- package/src/sinks/drizzle/index.ts +0 -6
- package/src/sinks/drizzle/insert.ts +0 -42
- package/src/sinks/drizzle/select.ts +0 -44
- package/src/sinks/drizzle/transaction.ts +0 -49
- package/src/sinks/drizzle/update.ts +0 -47
- package/src/sinks/drizzle/utils.ts +0 -99
- package/src/sinks/sqlite.test.ts +0 -99
- package/src/sinks/sqlite.ts +0 -170
- package/src/testing/helper.ts +0 -13
- package/src/testing/indexer.ts +0 -35
- package/src/testing/setup.ts +0 -59
- package/src/testing/vcr.ts +0 -54
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import {
|
|
3
|
-
type MockBlock,
|
|
4
|
-
MockClient,
|
|
5
|
-
type MockFilter,
|
|
6
|
-
} from "@apibara/protocol/testing";
|
|
7
|
-
import { asc, eq, sql } from "drizzle-orm";
|
|
8
|
-
import { drizzle } from "drizzle-orm/node-postgres";
|
|
9
|
-
import { serial, text } from "drizzle-orm/pg-core";
|
|
10
|
-
import { Client } from "pg";
|
|
11
|
-
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
12
|
-
import {
|
|
13
|
-
type Int8Range,
|
|
14
|
-
drizzle as drizzleSink,
|
|
15
|
-
getDrizzleCursor,
|
|
16
|
-
pgTable,
|
|
17
|
-
} from ".";
|
|
18
|
-
import { useSink } from "../../hooks";
|
|
19
|
-
import { run } from "../../indexer";
|
|
20
|
-
import { generateMockMessages } from "../../testing";
|
|
21
|
-
import { getMockIndexer } from "../../testing/indexer";
|
|
22
|
-
|
|
23
|
-
const testTable = pgTable("test_table", {
|
|
24
|
-
id: serial("id").primaryKey(),
|
|
25
|
-
data: text("data"),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const client = new Client({
|
|
29
|
-
connectionString: "postgres://postgres:postgres@localhost:5432/postgres",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
await client.connect();
|
|
33
|
-
|
|
34
|
-
const db = drizzle(client);
|
|
35
|
-
|
|
36
|
-
describe("Drizzle Test", () => {
|
|
37
|
-
beforeAll(async () => {
|
|
38
|
-
// drop test_table if exists
|
|
39
|
-
await db.execute(sql`DROP TABLE IF EXISTS test_table`);
|
|
40
|
-
// create test_table with db
|
|
41
|
-
await db.execute(
|
|
42
|
-
sql`CREATE TABLE test_table (id SERIAL PRIMARY KEY, data TEXT, _cursor INT8RANGE)`,
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
beforeEach(async () => {
|
|
47
|
-
await db.delete(testTable).execute();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should insert data", async () => {
|
|
51
|
-
const client = new MockClient<MockFilter, MockBlock>((request, options) => {
|
|
52
|
-
return generateMockMessages(5);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const sink = drizzleSink({ database: db, tables: [testTable] });
|
|
56
|
-
|
|
57
|
-
const indexer = getMockIndexer({
|
|
58
|
-
sink,
|
|
59
|
-
override: {
|
|
60
|
-
transform: async ({ context, endCursor, block: { data } }) => {
|
|
61
|
-
const { db } = useSink({ context });
|
|
62
|
-
// Insert a new row into the test_table
|
|
63
|
-
// The id is set to the current cursor's orderKey
|
|
64
|
-
// The data is set to the block data
|
|
65
|
-
await db
|
|
66
|
-
.insert(testTable)
|
|
67
|
-
.values([{ id: Number(endCursor?.orderKey), data }]);
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
await run(client, indexer);
|
|
73
|
-
|
|
74
|
-
const result = await db.select().from(testTable).orderBy(asc(testTable.id));
|
|
75
|
-
|
|
76
|
-
expect(result).toHaveLength(5);
|
|
77
|
-
expect(result[0].data).toBe("5000000");
|
|
78
|
-
expect(result[2].data).toBe("5000002");
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should update data", async () => {
|
|
82
|
-
const client = new MockClient<MockFilter, MockBlock>((request, options) => {
|
|
83
|
-
return generateMockMessages(5);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const sink = drizzleSink({ database: db, tables: [testTable] });
|
|
87
|
-
|
|
88
|
-
const indexer = getMockIndexer({
|
|
89
|
-
sink,
|
|
90
|
-
override: {
|
|
91
|
-
transform: async ({ context, endCursor, block: { data } }) => {
|
|
92
|
-
const { db } = useSink({ context });
|
|
93
|
-
|
|
94
|
-
// insert data for each message in db
|
|
95
|
-
await db
|
|
96
|
-
.insert(testTable)
|
|
97
|
-
.values([{ id: Number(endCursor?.orderKey), data }]);
|
|
98
|
-
|
|
99
|
-
// update data for id 5000002 when orderKey is 5000004
|
|
100
|
-
// this is to test if the update query is working
|
|
101
|
-
if (endCursor?.orderKey === 5000004n) {
|
|
102
|
-
await db
|
|
103
|
-
.update(testTable)
|
|
104
|
-
.set({ data: "0000000" })
|
|
105
|
-
.where(eq(testTable.id, 5000002));
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
await run(client, indexer);
|
|
112
|
-
|
|
113
|
-
const result = await db.select().from(testTable).orderBy(asc(testTable.id));
|
|
114
|
-
|
|
115
|
-
expect(result).toHaveLength(5);
|
|
116
|
-
expect(result[2].data).toBe("0000000");
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should delete data", async () => {
|
|
120
|
-
const client = new MockClient<MockFilter, MockBlock>((request, options) => {
|
|
121
|
-
return generateMockMessages(5);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const sink = drizzleSink({ database: db, tables: [testTable] });
|
|
125
|
-
|
|
126
|
-
const indexer = getMockIndexer({
|
|
127
|
-
sink,
|
|
128
|
-
override: {
|
|
129
|
-
transform: async ({ context, endCursor, block: { data } }) => {
|
|
130
|
-
const { db } = useSink({ context });
|
|
131
|
-
|
|
132
|
-
// insert data for each message in db
|
|
133
|
-
await db
|
|
134
|
-
.insert(testTable)
|
|
135
|
-
.values([{ id: Number(endCursor?.orderKey), data }]);
|
|
136
|
-
|
|
137
|
-
// delete data for id 5000002 when orderKey is 5000004
|
|
138
|
-
// this is to test if the delete query is working
|
|
139
|
-
if (endCursor?.orderKey === 5000004n) {
|
|
140
|
-
await db.delete(testTable).where(eq(testTable.id, 5000002));
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
await run(client, indexer);
|
|
147
|
-
|
|
148
|
-
const result = await db.select().from(testTable).orderBy(asc(testTable.id));
|
|
149
|
-
|
|
150
|
-
expect(result).toHaveLength(5);
|
|
151
|
-
|
|
152
|
-
// as when you run delete query on a data, it isnt literally deleted from the db,
|
|
153
|
-
// instead, we just update the upper bound of that row to the current cursor
|
|
154
|
-
// check if the cursor upper bound has been set correctly
|
|
155
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
156
|
-
expect(((result[2] as any)._cursor as Int8Range).range.upper).toBe(5000004);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("should select data", async () => {
|
|
160
|
-
const client = new MockClient<MockFilter, MockBlock>((request, options) => {
|
|
161
|
-
return generateMockMessages(5);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const sink = drizzleSink({ database: db, tables: [testTable] });
|
|
165
|
-
|
|
166
|
-
let result: (typeof testTable.$inferSelect)[] = [];
|
|
167
|
-
|
|
168
|
-
const indexer = getMockIndexer({
|
|
169
|
-
sink,
|
|
170
|
-
override: {
|
|
171
|
-
transform: async ({ context, endCursor, block: { data } }) => {
|
|
172
|
-
const { db } = useSink({ context });
|
|
173
|
-
|
|
174
|
-
// insert data for each message in db
|
|
175
|
-
await db
|
|
176
|
-
.insert(testTable)
|
|
177
|
-
.values([{ id: Number(endCursor?.orderKey), data }]);
|
|
178
|
-
|
|
179
|
-
// delete data for id 5000002 when orderKey is 5000004
|
|
180
|
-
// this will update the upper bound of the row with id 5000002 from infinity to 5000004
|
|
181
|
-
// so when we select all rows, row with id 5000002 will not be included
|
|
182
|
-
// as when we run select query it should only return rows with upper bound infinity
|
|
183
|
-
if (endCursor?.orderKey === 5000003n) {
|
|
184
|
-
await db.delete(testTable).where(eq(testTable.id, 5000002));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// when on last message of mock stream, select all rows from db
|
|
188
|
-
if (endCursor?.orderKey === 5000004n) {
|
|
189
|
-
result = await db
|
|
190
|
-
.select()
|
|
191
|
-
.from(testTable)
|
|
192
|
-
.orderBy(asc(testTable.id));
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
await run(client, indexer);
|
|
199
|
-
|
|
200
|
-
expect(result).toHaveLength(4);
|
|
201
|
-
expect(result.find((r) => r.id === 5000002)).toBeUndefined();
|
|
202
|
-
// check if all rows are still in db
|
|
203
|
-
const allRows = await db.select().from(testTable);
|
|
204
|
-
expect(allRows).toHaveLength(5);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("should invalidate data correctly", async () => {
|
|
208
|
-
const sink = drizzleSink({ database: db, tables: [testTable] });
|
|
209
|
-
|
|
210
|
-
// Insert some test data
|
|
211
|
-
await db.insert(testTable).values(
|
|
212
|
-
// @ts-ignore
|
|
213
|
-
[
|
|
214
|
-
{ id: 1, data: "data1", _cursor: getDrizzleCursor([1n, 5n]) },
|
|
215
|
-
{ id: 2, data: "data2", _cursor: getDrizzleCursor([2n, 5n]) },
|
|
216
|
-
{ id: 3, data: "data3", _cursor: getDrizzleCursor(3n) },
|
|
217
|
-
{ id: 4, data: "data4", _cursor: getDrizzleCursor(4n) },
|
|
218
|
-
{ id: 5, data: "data5", _cursor: getDrizzleCursor(5n) },
|
|
219
|
-
],
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
// Create a cursor at position 3
|
|
223
|
-
const cursor: Cursor = { orderKey: 3n };
|
|
224
|
-
|
|
225
|
-
// Invalidate data
|
|
226
|
-
await sink.invalidate(cursor);
|
|
227
|
-
|
|
228
|
-
// Check the results
|
|
229
|
-
const result = await db.select().from(testTable).orderBy(asc(testTable.id));
|
|
230
|
-
|
|
231
|
-
expect(result).toHaveLength(3);
|
|
232
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
233
|
-
expect(((result[0] as any)._cursor as Int8Range).range.upper).toBe(null);
|
|
234
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
235
|
-
expect(((result[1] as any)._cursor as Int8Range).range.upper).toBe(null);
|
|
236
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
237
|
-
expect(((result[2] as any)._cursor as Int8Range).range.upper).toBe(null);
|
|
238
|
-
});
|
|
239
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import {
|
|
3
|
-
type ExtractTablesWithRelations,
|
|
4
|
-
type TablesRelationalConfig,
|
|
5
|
-
gt,
|
|
6
|
-
sql,
|
|
7
|
-
} from "drizzle-orm";
|
|
8
|
-
import type {
|
|
9
|
-
AnyPgTable,
|
|
10
|
-
PgDatabase,
|
|
11
|
-
PgQueryResultHKT,
|
|
12
|
-
PgTableWithColumns,
|
|
13
|
-
TableConfig,
|
|
14
|
-
} from "drizzle-orm/pg-core";
|
|
15
|
-
import { Sink, type SinkCursorParams } from "../../sink";
|
|
16
|
-
import { DrizzleSinkTransaction } from "./transaction";
|
|
17
|
-
|
|
18
|
-
export type DrizzleSinkTables<
|
|
19
|
-
TTableConfig extends Record<string, TableConfig>,
|
|
20
|
-
> = {
|
|
21
|
-
[K in keyof TTableConfig]: PgTableWithColumns<TTableConfig[K]>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type DrizzleSinkOptions<
|
|
25
|
-
TQueryResult extends PgQueryResultHKT,
|
|
26
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
27
|
-
TSchema extends
|
|
28
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
29
|
-
> = {
|
|
30
|
-
/**
|
|
31
|
-
* Database instance of drizzle-orm
|
|
32
|
-
*/
|
|
33
|
-
database: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
34
|
-
tables: AnyPgTable[];
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* A sink that writes data to a PostgreSQL database using Drizzle ORM.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
*
|
|
42
|
-
* ```ts
|
|
43
|
-
* const sink = drizzle({
|
|
44
|
-
* database: db,
|
|
45
|
-
* });
|
|
46
|
-
*
|
|
47
|
-
* ...
|
|
48
|
-
* async transform({context, endCursor}){
|
|
49
|
-
* const { transaction } = useSink(context);
|
|
50
|
-
* const db = transaction(endCursor);
|
|
51
|
-
*
|
|
52
|
-
* db.insert(users).values([
|
|
53
|
-
* { id: 1, name: "John" },
|
|
54
|
-
* { id: 2, name: "Jane" },
|
|
55
|
-
* ]);
|
|
56
|
-
* }
|
|
57
|
-
*
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export class DrizzleSink<
|
|
61
|
-
TQueryResult extends PgQueryResultHKT,
|
|
62
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
63
|
-
TSchema extends
|
|
64
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
65
|
-
> extends Sink {
|
|
66
|
-
private _db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
67
|
-
private _tables: AnyPgTable[];
|
|
68
|
-
constructor(options: DrizzleSinkOptions<TQueryResult, TFullSchema, TSchema>) {
|
|
69
|
-
super();
|
|
70
|
-
const { database, tables } = options;
|
|
71
|
-
this._db = database;
|
|
72
|
-
this._tables = tables;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async transaction(
|
|
76
|
-
{ cursor, endCursor, finality }: SinkCursorParams,
|
|
77
|
-
cb: (params: {
|
|
78
|
-
db: DrizzleSinkTransaction<TQueryResult, TFullSchema, TSchema>;
|
|
79
|
-
}) => Promise<void>,
|
|
80
|
-
): Promise<void> {
|
|
81
|
-
await this._db.transaction(async (db) => {
|
|
82
|
-
await cb({ db: new DrizzleSinkTransaction(db, endCursor) });
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async invalidate(cursor?: Cursor) {
|
|
87
|
-
await this._db.transaction(async (db) => {
|
|
88
|
-
for (const table of this._tables) {
|
|
89
|
-
// delete all rows whose lowerbound of "_cursor" (int8range) column is greater than the invalidate cursor
|
|
90
|
-
await db
|
|
91
|
-
.delete(table)
|
|
92
|
-
.where(gt(sql`lower(_cursor)`, sql`${Number(cursor?.orderKey)}`))
|
|
93
|
-
.returning();
|
|
94
|
-
// and for rows whose upperbound of "_cursor" (int8range) column is greater than the invalidate cursor, set the upperbound to infinity
|
|
95
|
-
await db
|
|
96
|
-
.update(table)
|
|
97
|
-
.set({
|
|
98
|
-
_cursor: sql`int8range(lower(_cursor), NULL, '[)')`,
|
|
99
|
-
})
|
|
100
|
-
.where(gt(sql`upper(_cursor)`, sql`${Number(cursor?.orderKey)}`));
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export const drizzle = <
|
|
107
|
-
TQueryResult extends PgQueryResultHKT,
|
|
108
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
109
|
-
TSchema extends
|
|
110
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
111
|
-
>(
|
|
112
|
-
args: DrizzleSinkOptions<TQueryResult, TFullSchema, TSchema>,
|
|
113
|
-
) => {
|
|
114
|
-
return new DrizzleSink(args);
|
|
115
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import type {
|
|
3
|
-
ExtractTablesWithRelations,
|
|
4
|
-
TablesRelationalConfig,
|
|
5
|
-
} from "drizzle-orm";
|
|
6
|
-
import type {
|
|
7
|
-
PgInsertValue as DrizzleInsertValue,
|
|
8
|
-
PgQueryResultHKT,
|
|
9
|
-
PgTable,
|
|
10
|
-
PgTransaction,
|
|
11
|
-
} from "drizzle-orm/pg-core";
|
|
12
|
-
import { type PgInsertValue, getDrizzleCursor } from "./utils";
|
|
13
|
-
|
|
14
|
-
export class DrizzleSinkInsert<
|
|
15
|
-
TTable extends PgTable,
|
|
16
|
-
TQueryResult extends PgQueryResultHKT,
|
|
17
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
18
|
-
TSchema extends
|
|
19
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
20
|
-
> {
|
|
21
|
-
constructor(
|
|
22
|
-
private db: PgTransaction<TQueryResult, TFullSchema, TSchema>,
|
|
23
|
-
private table: TTable,
|
|
24
|
-
private endCursor?: Cursor,
|
|
25
|
-
) {}
|
|
26
|
-
|
|
27
|
-
values(values: PgInsertValue<TTable> | PgInsertValue<TTable>[]) {
|
|
28
|
-
const originalInsert = this.db.insert(this.table);
|
|
29
|
-
const cursoredValues = (Array.isArray(values) ? values : [values]).map(
|
|
30
|
-
(v) => {
|
|
31
|
-
return {
|
|
32
|
-
...v,
|
|
33
|
-
_cursor: getDrizzleCursor(this.endCursor?.orderKey),
|
|
34
|
-
};
|
|
35
|
-
},
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
return originalInsert.values(
|
|
39
|
-
cursoredValues as DrizzleInsertValue<TTable>[],
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import {
|
|
3
|
-
type ExtractTablesWithRelations,
|
|
4
|
-
type SQL,
|
|
5
|
-
type Subquery,
|
|
6
|
-
type TablesRelationalConfig,
|
|
7
|
-
sql,
|
|
8
|
-
} from "drizzle-orm";
|
|
9
|
-
import type {
|
|
10
|
-
PgQueryResultHKT,
|
|
11
|
-
PgTable,
|
|
12
|
-
PgTransaction,
|
|
13
|
-
SelectedFields,
|
|
14
|
-
} from "drizzle-orm/pg-core";
|
|
15
|
-
import type { PgViewBase } from "drizzle-orm/pg-core/view-base";
|
|
16
|
-
|
|
17
|
-
export class DrizzleSinkSelect<
|
|
18
|
-
TSelection extends SelectedFields,
|
|
19
|
-
TQueryResult extends PgQueryResultHKT,
|
|
20
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
21
|
-
TSchema extends
|
|
22
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
23
|
-
> {
|
|
24
|
-
constructor(
|
|
25
|
-
private db: PgTransaction<TQueryResult, TFullSchema, TSchema>,
|
|
26
|
-
private fields?: TSelection,
|
|
27
|
-
private endCursor?: Cursor,
|
|
28
|
-
) {}
|
|
29
|
-
|
|
30
|
-
from<TFrom extends PgTable | Subquery | PgViewBase | SQL>(source: TFrom) {
|
|
31
|
-
if (this.fields) {
|
|
32
|
-
const originalFrom = this.db.select(this.fields).from(source);
|
|
33
|
-
return {
|
|
34
|
-
...originalFrom,
|
|
35
|
-
where: (where?: SQL) => {
|
|
36
|
-
const combinedWhere = sql`${where ? sql`${where} AND ` : sql``}upper_inf(_cursor)`;
|
|
37
|
-
return originalFrom.where(combinedWhere);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return this.db.select().from(source).where(sql`upper_inf(_cursor)`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import type {
|
|
3
|
-
ExtractTablesWithRelations,
|
|
4
|
-
TablesRelationalConfig,
|
|
5
|
-
} from "drizzle-orm";
|
|
6
|
-
import type {
|
|
7
|
-
PgQueryResultHKT,
|
|
8
|
-
PgSelectBuilder,
|
|
9
|
-
PgTable,
|
|
10
|
-
PgTransaction,
|
|
11
|
-
SelectedFields,
|
|
12
|
-
} from "drizzle-orm/pg-core";
|
|
13
|
-
import { DrizzleSinkDelete } from "./delete";
|
|
14
|
-
import { DrizzleSinkInsert } from "./insert";
|
|
15
|
-
import { DrizzleSinkSelect } from "./select";
|
|
16
|
-
import { DrizzleSinkUpdate } from "./update";
|
|
17
|
-
|
|
18
|
-
export class DrizzleSinkTransaction<
|
|
19
|
-
TQueryResult extends PgQueryResultHKT,
|
|
20
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
21
|
-
TSchema extends
|
|
22
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
23
|
-
> {
|
|
24
|
-
constructor(
|
|
25
|
-
private db: PgTransaction<TQueryResult, TFullSchema, TSchema>,
|
|
26
|
-
private endCursor?: Cursor,
|
|
27
|
-
) {}
|
|
28
|
-
|
|
29
|
-
insert<TTable extends PgTable>(table: TTable) {
|
|
30
|
-
return new DrizzleSinkInsert(this.db, table, this.endCursor);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
update<TTable extends PgTable>(table: TTable) {
|
|
34
|
-
return new DrizzleSinkUpdate(this.db, table, this.endCursor);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
delete<TTable extends PgTable>(table: TTable) {
|
|
38
|
-
return new DrizzleSinkDelete(this.db, table, this.endCursor);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// @ts-ignore
|
|
42
|
-
select(): PgSelectBuilder<undefined>;
|
|
43
|
-
select<TSelection extends SelectedFields>(
|
|
44
|
-
fields: TSelection,
|
|
45
|
-
): PgSelectBuilder<TSelection>;
|
|
46
|
-
select(fields?: SelectedFields) {
|
|
47
|
-
return new DrizzleSinkSelect(this.db, fields, this.endCursor);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { Cursor } from "@apibara/protocol";
|
|
2
|
-
import {
|
|
3
|
-
type ExtractTablesWithRelations,
|
|
4
|
-
type SQL,
|
|
5
|
-
type TablesRelationalConfig,
|
|
6
|
-
sql,
|
|
7
|
-
} from "drizzle-orm";
|
|
8
|
-
import type {
|
|
9
|
-
PgQueryResultHKT,
|
|
10
|
-
PgTable,
|
|
11
|
-
PgTransaction,
|
|
12
|
-
PgUpdateBase,
|
|
13
|
-
PgUpdateSetSource,
|
|
14
|
-
} from "drizzle-orm/pg-core";
|
|
15
|
-
|
|
16
|
-
export class DrizzleSinkUpdate<
|
|
17
|
-
TTable extends PgTable,
|
|
18
|
-
TQueryResult extends PgQueryResultHKT,
|
|
19
|
-
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
20
|
-
TSchema extends
|
|
21
|
-
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
22
|
-
> {
|
|
23
|
-
constructor(
|
|
24
|
-
private db: PgTransaction<TQueryResult, TFullSchema, TSchema>,
|
|
25
|
-
private table: TTable,
|
|
26
|
-
private endCursor?: Cursor,
|
|
27
|
-
) {}
|
|
28
|
-
|
|
29
|
-
set(values: PgUpdateSetSource<TTable>): PgUpdateBase<TTable, TQueryResult> {
|
|
30
|
-
const originalUpdate = this.db.update(this.table);
|
|
31
|
-
const originalSet = originalUpdate.set(values);
|
|
32
|
-
return {
|
|
33
|
-
...originalSet,
|
|
34
|
-
where: async (where: SQL | undefined) => {
|
|
35
|
-
await this.db
|
|
36
|
-
.update(this.table)
|
|
37
|
-
.set({
|
|
38
|
-
_cursor: sql`int8range(lower(_cursor), ${Number(this.endCursor?.orderKey!)}, '[)')`,
|
|
39
|
-
} as PgUpdateSetSource<TTable>)
|
|
40
|
-
.where(sql`${where ? sql`${where} AND ` : sql``}upper_inf(_cursor)`)
|
|
41
|
-
.execute();
|
|
42
|
-
|
|
43
|
-
return originalSet.where(where);
|
|
44
|
-
},
|
|
45
|
-
} as PgUpdateBase<TTable, TQueryResult>;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BuildColumns,
|
|
3
|
-
BuildExtraConfigColumns,
|
|
4
|
-
NotNull,
|
|
5
|
-
Placeholder,
|
|
6
|
-
SQL,
|
|
7
|
-
} from "drizzle-orm";
|
|
8
|
-
import type {
|
|
9
|
-
PgColumnBuilderBase,
|
|
10
|
-
PgCustomColumnBuilder,
|
|
11
|
-
PgTable,
|
|
12
|
-
PgTableExtraConfig,
|
|
13
|
-
PgTableWithColumns,
|
|
14
|
-
} from "drizzle-orm/pg-core";
|
|
15
|
-
import { pgTable as drizzlePgTable } from "drizzle-orm/pg-core";
|
|
16
|
-
import range from "postgres-range";
|
|
17
|
-
import { Int8Range, int8range } from "./Int8Range";
|
|
18
|
-
|
|
19
|
-
export type CursorColumnBuilder = NotNull<
|
|
20
|
-
PgCustomColumnBuilder<{
|
|
21
|
-
name: "_cursor";
|
|
22
|
-
dataType: "custom";
|
|
23
|
-
columnType: "PgCustomColumn";
|
|
24
|
-
data: Int8Range;
|
|
25
|
-
driverParam: undefined;
|
|
26
|
-
enumValues: undefined;
|
|
27
|
-
generated: undefined;
|
|
28
|
-
}>
|
|
29
|
-
>;
|
|
30
|
-
|
|
31
|
-
// Redefining the type of `pgTable` to include the `_cursor` column.
|
|
32
|
-
export type PgTableWithCursorFn<
|
|
33
|
-
TSchema extends string | undefined = undefined,
|
|
34
|
-
> = <
|
|
35
|
-
TTableName extends string,
|
|
36
|
-
TColumnsMap extends Record<string, PgColumnBuilderBase>,
|
|
37
|
-
>(
|
|
38
|
-
name: TTableName,
|
|
39
|
-
columns: TColumnsMap,
|
|
40
|
-
extraConfig?: (
|
|
41
|
-
self: BuildExtraConfigColumns<
|
|
42
|
-
TTableName,
|
|
43
|
-
TColumnsMap & { _cursor: CursorColumnBuilder },
|
|
44
|
-
"pg"
|
|
45
|
-
>,
|
|
46
|
-
) => PgTableExtraConfig,
|
|
47
|
-
) => PgTableWithColumns<{
|
|
48
|
-
name: TTableName;
|
|
49
|
-
schema: TSchema;
|
|
50
|
-
columns: BuildColumns<
|
|
51
|
-
TTableName,
|
|
52
|
-
TColumnsMap & { _cursor: CursorColumnBuilder },
|
|
53
|
-
"pg"
|
|
54
|
-
>;
|
|
55
|
-
dialect: "pg";
|
|
56
|
-
}>;
|
|
57
|
-
|
|
58
|
-
// Same as the drizzle's `PgInsertValue` type, but without the `_cursor` column.
|
|
59
|
-
export type PgInsertValue<TTable extends PgTable> = Omit<
|
|
60
|
-
{
|
|
61
|
-
[Key in keyof TTable["$inferInsert"]]:
|
|
62
|
-
| TTable["$inferInsert"][Key]
|
|
63
|
-
| SQL
|
|
64
|
-
| Placeholder;
|
|
65
|
-
} & {},
|
|
66
|
-
"_cursor"
|
|
67
|
-
>;
|
|
68
|
-
|
|
69
|
-
export const pgTable: PgTableWithCursorFn = (name, columns, extraConfig?) => {
|
|
70
|
-
return drizzlePgTable(
|
|
71
|
-
name,
|
|
72
|
-
{
|
|
73
|
-
...columns,
|
|
74
|
-
_cursor: int8range("_cursor").notNull(),
|
|
75
|
-
},
|
|
76
|
-
extraConfig,
|
|
77
|
-
);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const getDrizzleCursor = (
|
|
81
|
-
cursor_range: [bigint | undefined, bigint | undefined] | bigint | undefined,
|
|
82
|
-
) => {
|
|
83
|
-
const isArray = Array.isArray(cursor_range);
|
|
84
|
-
const [lower, upper] = isArray ? cursor_range : [cursor_range, undefined];
|
|
85
|
-
let isNoUpperBound = false;
|
|
86
|
-
if (!lower) {
|
|
87
|
-
throw new Error("Lower bound cursor is required");
|
|
88
|
-
}
|
|
89
|
-
if (!upper) {
|
|
90
|
-
isNoUpperBound = true;
|
|
91
|
-
}
|
|
92
|
-
return new Int8Range(
|
|
93
|
-
new range.Range(
|
|
94
|
-
Number(lower),
|
|
95
|
-
Number(upper),
|
|
96
|
-
range.RANGE_LB_INC | (isNoUpperBound ? range.RANGE_UB_INF : 0),
|
|
97
|
-
),
|
|
98
|
-
);
|
|
99
|
-
};
|