@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.
Files changed (127) hide show
  1. package/dist/index.cjs +271 -39
  2. package/dist/index.d.cts +1 -16
  3. package/dist/index.d.mts +1 -16
  4. package/dist/index.d.ts +1 -16
  5. package/dist/index.mjs +262 -25
  6. package/dist/internal/index.cjs +10 -0
  7. package/dist/internal/index.d.cts +3 -0
  8. package/dist/internal/index.d.mts +3 -0
  9. package/dist/internal/index.d.ts +3 -0
  10. package/dist/internal/index.mjs +8 -0
  11. package/dist/internal/plugins.cjs +38 -0
  12. package/dist/internal/plugins.d.cts +13 -0
  13. package/dist/internal/plugins.d.mts +13 -0
  14. package/dist/internal/plugins.d.ts +13 -0
  15. package/dist/internal/plugins.mjs +34 -0
  16. package/dist/internal/testing.cjs +118 -0
  17. package/dist/internal/testing.d.cts +42 -0
  18. package/dist/internal/testing.d.mts +42 -0
  19. package/dist/internal/testing.d.ts +42 -0
  20. package/dist/internal/testing.mjs +113 -0
  21. package/dist/plugins/index.cjs +39 -3
  22. package/dist/plugins/index.d.cts +16 -2
  23. package/dist/plugins/index.d.mts +16 -2
  24. package/dist/plugins/index.d.ts +16 -2
  25. package/dist/plugins/index.mjs +36 -3
  26. package/dist/shared/indexer.077335f3.cjs +15 -0
  27. package/dist/shared/indexer.2416906c.cjs +29 -0
  28. package/dist/shared/indexer.601ceab0.cjs +7 -0
  29. package/dist/shared/indexer.9b21ddd2.mjs +5 -0
  30. package/dist/shared/indexer.a55ad619.mjs +12 -0
  31. package/dist/shared/indexer.fedcd831.d.cts +100 -0
  32. package/dist/shared/indexer.fedcd831.d.mts +100 -0
  33. package/dist/shared/indexer.fedcd831.d.ts +100 -0
  34. package/dist/shared/indexer.ff25c953.mjs +26 -0
  35. package/dist/testing/index.cjs +52 -50
  36. package/dist/testing/index.d.cts +8 -36
  37. package/dist/testing/index.d.mts +8 -36
  38. package/dist/testing/index.d.ts +8 -36
  39. package/dist/testing/index.mjs +47 -47
  40. package/dist/vcr/index.cjs +84 -17
  41. package/dist/vcr/index.d.cts +16 -7
  42. package/dist/vcr/index.d.mts +16 -7
  43. package/dist/vcr/index.d.ts +16 -7
  44. package/dist/vcr/index.mjs +75 -11
  45. package/package.json +22 -42
  46. package/src/compose.test.ts +76 -0
  47. package/src/compose.ts +71 -0
  48. package/src/context.ts +14 -8
  49. package/src/index.ts +0 -5
  50. package/src/indexer.test.ts +125 -186
  51. package/src/indexer.ts +278 -151
  52. package/src/internal/index.ts +6 -0
  53. package/src/internal/plugins.ts +1 -0
  54. package/src/internal/testing.ts +148 -0
  55. package/src/plugins/config.ts +4 -4
  56. package/src/plugins/context.ts +40 -0
  57. package/src/plugins/index.ts +8 -1
  58. package/src/plugins/logger.ts +30 -0
  59. package/src/plugins/persistence.ts +24 -187
  60. package/src/testing/index.ts +58 -3
  61. package/src/vcr/record.ts +5 -3
  62. package/src/vcr/replay.ts +8 -18
  63. package/dist/plugins/kv.cjs +0 -131
  64. package/dist/plugins/kv.d.cts +0 -32
  65. package/dist/plugins/kv.d.mts +0 -32
  66. package/dist/plugins/kv.d.ts +0 -32
  67. package/dist/plugins/kv.mjs +0 -124
  68. package/dist/plugins/persistence.cjs +0 -182
  69. package/dist/plugins/persistence.d.cts +0 -50
  70. package/dist/plugins/persistence.d.mts +0 -50
  71. package/dist/plugins/persistence.d.ts +0 -50
  72. package/dist/plugins/persistence.mjs +0 -179
  73. package/dist/shared/indexer.2c23c9cd.mjs +0 -35
  74. package/dist/shared/indexer.318d3617.cjs +0 -47
  75. package/dist/shared/indexer.36530330.mjs +0 -249
  76. package/dist/shared/indexer.500fd281.d.cts +0 -23
  77. package/dist/shared/indexer.541d43eb.cjs +0 -266
  78. package/dist/shared/indexer.93d6b2eb.mjs +0 -17
  79. package/dist/shared/indexer.a8b7ab1f.cjs +0 -25
  80. package/dist/shared/indexer.b9c8f0d8.d.cts +0 -19
  81. package/dist/shared/indexer.b9c8f0d8.d.mts +0 -19
  82. package/dist/shared/indexer.b9c8f0d8.d.ts +0 -19
  83. package/dist/shared/indexer.c7ed6b83.d.cts +0 -82
  84. package/dist/shared/indexer.e1856641.d.mts +0 -23
  85. package/dist/shared/indexer.e4f2430f.d.ts +0 -23
  86. package/dist/shared/indexer.e8bd138d.d.mts +0 -82
  87. package/dist/shared/indexer.f761abcd.d.ts +0 -82
  88. package/dist/sinks/csv.cjs +0 -85
  89. package/dist/sinks/csv.d.cts +0 -66
  90. package/dist/sinks/csv.d.mts +0 -66
  91. package/dist/sinks/csv.d.ts +0 -66
  92. package/dist/sinks/csv.mjs +0 -78
  93. package/dist/sinks/drizzle/index.cjs +0 -212
  94. package/dist/sinks/drizzle/index.d.cts +0 -153
  95. package/dist/sinks/drizzle/index.d.mts +0 -153
  96. package/dist/sinks/drizzle/index.d.ts +0 -153
  97. package/dist/sinks/drizzle/index.mjs +0 -198
  98. package/dist/sinks/sqlite.cjs +0 -90
  99. package/dist/sinks/sqlite.d.cts +0 -71
  100. package/dist/sinks/sqlite.d.mts +0 -71
  101. package/dist/sinks/sqlite.d.ts +0 -71
  102. package/dist/sinks/sqlite.mjs +0 -87
  103. package/src/hooks/index.ts +0 -2
  104. package/src/hooks/useKVStore.ts +0 -12
  105. package/src/hooks/useSink.ts +0 -13
  106. package/src/plugins/kv.test.ts +0 -120
  107. package/src/plugins/kv.ts +0 -132
  108. package/src/plugins/persistence.test.ts +0 -151
  109. package/src/sink.ts +0 -36
  110. package/src/sinks/csv.test.ts +0 -65
  111. package/src/sinks/csv.ts +0 -159
  112. package/src/sinks/drizzle/Int8Range.ts +0 -52
  113. package/src/sinks/drizzle/delete.ts +0 -42
  114. package/src/sinks/drizzle/drizzle.test.ts +0 -239
  115. package/src/sinks/drizzle/drizzle.ts +0 -115
  116. package/src/sinks/drizzle/index.ts +0 -6
  117. package/src/sinks/drizzle/insert.ts +0 -42
  118. package/src/sinks/drizzle/select.ts +0 -44
  119. package/src/sinks/drizzle/transaction.ts +0 -49
  120. package/src/sinks/drizzle/update.ts +0 -47
  121. package/src/sinks/drizzle/utils.ts +0 -99
  122. package/src/sinks/sqlite.test.ts +0 -99
  123. package/src/sinks/sqlite.ts +0 -170
  124. package/src/testing/helper.ts +0 -13
  125. package/src/testing/indexer.ts +0 -35
  126. package/src/testing/setup.ts +0 -59
  127. package/src/testing/vcr.ts +0 -54
@@ -1,99 +0,0 @@
1
- import {
2
- type MockBlock,
3
- MockClient,
4
- type MockFilter,
5
- } from "@apibara/protocol/testing";
6
- import Database from "better-sqlite3";
7
- import { describe, expect, it } from "vitest";
8
- import { useSink } from "../hooks";
9
- import { run } from "../indexer";
10
- import {} from "../plugins/persistence";
11
- import { generateMockMessages } from "../testing";
12
- import { getMockIndexer } from "../testing/indexer";
13
- import { sqlite } from "./sqlite";
14
-
15
- describe("Run Test", () => {
16
- it("should store in sqlite db via sqlitesink", async () => {
17
- const client = new MockClient<MockFilter, MockBlock>((request, options) => {
18
- return generateMockMessages();
19
- });
20
-
21
- const db = new Database(":memory:");
22
-
23
- db.prepare("DROP TABLE IF EXISTS test;").run();
24
-
25
- db.prepare(
26
- `
27
- CREATE TABLE IF NOT EXISTS test (
28
- data BLOB,
29
- _cursor BIGINT
30
- );`,
31
- ).run();
32
-
33
- const sink = sqlite({
34
- database: db,
35
- tableName: "test",
36
- });
37
- await run(
38
- client,
39
- getMockIndexer({
40
- sink,
41
- override: {
42
- transform: async ({ context, endCursor, block: { data } }) => {
43
- const { writer } = useSink({ context });
44
- writer.insert([{ data }]);
45
- },
46
- },
47
- }),
48
- );
49
-
50
- const sinkData = db.prepare("SELECT * FROM test").all();
51
-
52
- expect(sinkData).toMatchInlineSnapshot(`
53
- [
54
- {
55
- "_cursor": 5000000,
56
- "data": "5000000",
57
- },
58
- {
59
- "_cursor": 5000001,
60
- "data": "5000001",
61
- },
62
- {
63
- "_cursor": 5000002,
64
- "data": "5000002",
65
- },
66
- {
67
- "_cursor": 5000003,
68
- "data": "5000003",
69
- },
70
- {
71
- "_cursor": 5000004,
72
- "data": "5000004",
73
- },
74
- {
75
- "_cursor": 5000005,
76
- "data": "5000005",
77
- },
78
- {
79
- "_cursor": 5000006,
80
- "data": "5000006",
81
- },
82
- {
83
- "_cursor": 5000007,
84
- "data": "5000007",
85
- },
86
- {
87
- "_cursor": 5000008,
88
- "data": "5000008",
89
- },
90
- {
91
- "_cursor": 5000009,
92
- "data": "5000009",
93
- },
94
- ]
95
- `);
96
-
97
- db.close();
98
- });
99
- });
@@ -1,170 +0,0 @@
1
- import type { Cursor } from "@apibara/protocol";
2
- import type { Database as SqliteDatabase } from "better-sqlite3";
3
- import { Sink, type SinkCursorParams, type SinkData } from "../sink";
4
-
5
- export type SqliteSinkOptions = {
6
- /**
7
- * Database instance of better-sqlite3
8
- */
9
- database: SqliteDatabase;
10
- /**
11
- * The name of the table where data will be inserted.
12
- */
13
- tableName: string;
14
- /**
15
- * An optional column name used to store the cursor value. If specified,
16
- * the value of this column must match the `endCursor.orderKey` for each row.
17
- */
18
- cursorColumn?: string;
19
- /**
20
- * An optional configuration to handle conflicts during data insertion.
21
- * - `on`: The column name on which conflicts are detected.
22
- * - `update`: An array of column names to be updated if a conflict occurs.
23
- */
24
- onConflict?: { on: string; update: string[] };
25
- };
26
-
27
- type TxnContext = {
28
- buffer: SinkData[];
29
- };
30
-
31
- type TxnParams = {
32
- writer: {
33
- insert: (data: SinkData[]) => void;
34
- };
35
- };
36
- const transactionHelper = (context: TxnContext) => {
37
- return {
38
- insert: (data: SinkData[]) => {
39
- context.buffer.push(...data);
40
- },
41
- };
42
- };
43
-
44
- /**
45
- * A sink that writes data to a SQLite database.
46
- *
47
- * @example
48
- *
49
- * ```ts
50
- * const sink = sqlite({
51
- * database: db,
52
- * tableName: "test",
53
- * });
54
- *
55
- * ...
56
- * async transform({context, endCursor}){
57
- * const { writer } = useSink(context);
58
- * const insertHelper = writer(endCursor);
59
- *
60
- * insertHelper.insert([
61
- * { id: 1, name: "John" },
62
- * { id: 2, name: "Jane" },
63
- * ]);
64
- * }
65
- *
66
- * ```
67
- */
68
-
69
- export class SqliteSink extends Sink {
70
- private _config: Omit<SqliteSinkOptions, "database">;
71
- private _db: SqliteDatabase;
72
-
73
- constructor(options: SqliteSinkOptions) {
74
- super();
75
- const { database, ...config } = options;
76
- this._config = config;
77
- this._db = database;
78
- }
79
-
80
- private async write({
81
- data,
82
- endCursor,
83
- }: { data: SinkData[]; endCursor?: Cursor }) {
84
- data = this.processCursorColumn(data, endCursor);
85
- await this.insertJsonArray(data);
86
- }
87
-
88
- async transaction(
89
- { cursor, endCursor, finality }: SinkCursorParams,
90
- cb: (params: TxnParams) => Promise<void>,
91
- ) {
92
- const context: TxnContext = {
93
- buffer: [],
94
- };
95
-
96
- const writer = transactionHelper(context);
97
-
98
- await cb({ writer });
99
- await this.write({ data: context.buffer, endCursor });
100
- }
101
-
102
- async invalidate(cursor?: Cursor) {
103
- // TODO: Implement
104
- throw new Error("Not implemented");
105
- }
106
-
107
- private async insertJsonArray(data: SinkData[]) {
108
- if (data.length === 0) return;
109
-
110
- // Get columns from the first row of the object array
111
- const columns = Object.keys(data[0]);
112
- const columnNames = columns.join(", ");
113
- const placeholders = columns.map(() => "?").join(", ");
114
-
115
- // Handle onConflict option
116
- const conflictClause = this.buildConflictClause();
117
-
118
- // Build the SQL insert statement with multiple rows
119
- const insertSQL = `INSERT INTO ${this._config.tableName} (${columnNames}) VALUES `;
120
- const valuePlaceholders = data.map(() => `(${placeholders})`).join(", ");
121
- const statement = insertSQL + valuePlaceholders + conflictClause;
122
-
123
- // Prepare and execute the SQL statement
124
- const values = data.flatMap((row) => columns.map((col) => row[col]));
125
-
126
- this._db.prepare(statement).run(values);
127
- }
128
-
129
- private processCursorColumn(
130
- data: SinkData[],
131
- endCursor?: Cursor,
132
- ): SinkData[] {
133
- const { cursorColumn } = this._config;
134
-
135
- if (
136
- cursorColumn &&
137
- data.some(
138
- (row) => Number(row[cursorColumn]) !== Number(endCursor?.orderKey),
139
- )
140
- ) {
141
- throw new Error(
142
- `Mismatch of ${cursorColumn} and Cursor ${Number(endCursor?.orderKey)}`,
143
- );
144
- }
145
-
146
- if (cursorColumn) {
147
- return data;
148
- }
149
-
150
- return data.map((row) => ({
151
- ...row,
152
- _cursor: Number(endCursor?.orderKey),
153
- }));
154
- }
155
-
156
- private buildConflictClause(): string {
157
- const { on, update } = this._config.onConflict || {};
158
- if (on && update && update.length > 0) {
159
- const updateColumns = update
160
- .map((col) => `${col}=excluded.${col}`)
161
- .join(", ");
162
- return ` ON CONFLICT(${on}) DO UPDATE SET ${updateColumns}`;
163
- }
164
- return "";
165
- }
166
- }
167
-
168
- export const sqlite = (args: SqliteSinkOptions) => {
169
- return new SqliteSink(args);
170
- };
@@ -1,13 +0,0 @@
1
- import type { MockStreamResponse } from "@apibara/protocol/testing";
2
-
3
- export function generateMockMessages(count = 10): MockStreamResponse[] {
4
- return [...Array(count)].map((_, i) => ({
5
- _tag: "data",
6
- data: {
7
- cursor: { orderKey: BigInt(5_000_000 - 1) },
8
- finality: "accepted",
9
- data: [{ data: `${5_000_000 + i}` }],
10
- endCursor: { orderKey: BigInt(5_000_000 + i) },
11
- },
12
- }));
13
- }
@@ -1,35 +0,0 @@
1
- import {
2
- type MockBlock,
3
- type MockFilter,
4
- MockStream,
5
- } from "@apibara/protocol/testing";
6
- import { type IndexerConfig, createIndexer, defineIndexer } from "../indexer";
7
- import type { IndexerPlugin } from "../plugins";
8
- import type { Sink } from "../sink";
9
-
10
- export const getMockIndexer = <TTxnParams>({
11
- plugins,
12
- sink,
13
- override,
14
- }: {
15
- plugins?: ReadonlyArray<IndexerPlugin<MockFilter, MockBlock, TTxnParams>>;
16
- sink?: Sink<TTxnParams>;
17
- override?: Partial<IndexerConfig<MockFilter, MockBlock, TTxnParams>>;
18
- } = {}) =>
19
- createIndexer(
20
- defineIndexer(MockStream)({
21
- streamUrl: "https://sepolia.ethereum.a5a.ch",
22
- finality: "accepted",
23
- filter: {},
24
- async transform({ block: { data }, context }) {
25
- // TODO
26
- },
27
- sink,
28
- plugins,
29
- ...override,
30
- }),
31
- );
32
-
33
- export type MockRet = {
34
- data: string;
35
- };
@@ -1,59 +0,0 @@
1
- import { test as viTest } from "vitest";
2
- import type { Indexer } from "../indexer";
3
- import {
4
- type CassetteOptions,
5
- type VcrConfig,
6
- type VcrReplayResult,
7
- isCassetteAvailable,
8
- loadCassette,
9
- record,
10
- replay,
11
- } from "../vcr";
12
-
13
- export const test = viTest.extend({
14
- vcr: {
15
- withClient,
16
- },
17
- });
18
-
19
- type WithClientContext<TFilter, TBlock, TTxnParams> = {
20
- run: (
21
- indexerArgs: Indexer<TFilter, TBlock, TTxnParams>,
22
- ) => Promise<VcrReplayResult>;
23
- };
24
-
25
- async function withClient<TFilter, TBlock, TTxnParams>(
26
- cassetteName: string,
27
- range: { fromBlock: bigint; toBlock: bigint },
28
- callback: (
29
- context: WithClientContext<TFilter, TBlock, TTxnParams>,
30
- ) => Promise<void>,
31
- ) {
32
- const vcrConfig: VcrConfig = {
33
- cassetteDir: "cassettes",
34
- };
35
-
36
- const cassetteOptions: CassetteOptions = {
37
- name: cassetteName,
38
- startingCursor: {
39
- orderKey: range.fromBlock,
40
- },
41
- endingCursor: {
42
- orderKey: range.toBlock,
43
- },
44
- };
45
-
46
- const context: WithClientContext<TFilter, TBlock, TTxnParams> = {
47
- async run(indexer) {
48
- const client = loadCassette<TFilter, TBlock>(vcrConfig, cassetteName);
49
-
50
- if (!isCassetteAvailable(vcrConfig, cassetteName)) {
51
- await record(vcrConfig, client, indexer, cassetteOptions);
52
- }
53
-
54
- return await replay(vcrConfig, indexer, cassetteName);
55
- },
56
- };
57
-
58
- await callback(context);
59
- }
@@ -1,54 +0,0 @@
1
- import type { Cursor } from "@apibara/protocol";
2
- import { Sink, type SinkCursorParams, type SinkData } from "../sink";
3
- import type { VcrReplayResult } from "../vcr";
4
-
5
- type TxnContext = {
6
- buffer: SinkData[];
7
- };
8
-
9
- type TxnParams = {
10
- writer: {
11
- insert: (data: SinkData[]) => void;
12
- };
13
- };
14
-
15
- const transactionHelper = (context: TxnContext) => {
16
- return {
17
- insert: (data: SinkData[]) => {
18
- context.buffer.push(...data);
19
- },
20
- };
21
- };
22
-
23
- export class VcrSink extends Sink {
24
- public result: VcrReplayResult["outputs"] = [];
25
-
26
- write({ data, endCursor }: { data: SinkData[]; endCursor?: Cursor }) {
27
- if (data.length === 0) return;
28
-
29
- this.result.push({ data, endCursor });
30
- }
31
-
32
- async transaction(
33
- { cursor, endCursor, finality }: SinkCursorParams,
34
- cb: (params: TxnParams) => Promise<void>,
35
- ) {
36
- const context: TxnContext = {
37
- buffer: [],
38
- };
39
-
40
- const writer = transactionHelper(context);
41
-
42
- await cb({ writer });
43
- this.write({ data: context.buffer, endCursor });
44
- }
45
-
46
- async invalidate(cursor?: Cursor) {
47
- // TODO: Implement
48
- throw new Error("Not implemented");
49
- }
50
- }
51
-
52
- export function vcr() {
53
- return new VcrSink();
54
- }