@apibara/indexer 2.0.0-beta.3 → 2.0.0-beta.30

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 (74) hide show
  1. package/dist/index.cjs +270 -0
  2. package/dist/index.d.cts +3 -0
  3. package/dist/index.d.mts +3 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.mjs +259 -0
  6. package/dist/internal/testing.cjs +109 -0
  7. package/dist/internal/testing.d.cts +40 -0
  8. package/dist/internal/testing.d.mts +40 -0
  9. package/dist/internal/testing.d.ts +40 -0
  10. package/dist/internal/testing.mjs +104 -0
  11. package/dist/plugins/index.cjs +43 -0
  12. package/dist/plugins/index.d.cts +18 -0
  13. package/dist/plugins/index.d.mts +18 -0
  14. package/dist/plugins/index.d.ts +18 -0
  15. package/dist/plugins/index.mjs +38 -0
  16. package/dist/shared/indexer.077335f3.cjs +15 -0
  17. package/dist/shared/indexer.2416906c.cjs +29 -0
  18. package/dist/shared/indexer.601ceab0.cjs +7 -0
  19. package/dist/shared/indexer.8939ecc8.d.cts +91 -0
  20. package/dist/shared/indexer.8939ecc8.d.mts +91 -0
  21. package/dist/shared/indexer.8939ecc8.d.ts +91 -0
  22. package/dist/shared/indexer.9b21ddd2.mjs +5 -0
  23. package/dist/shared/indexer.a55ad619.mjs +12 -0
  24. package/dist/shared/indexer.ff25c953.mjs +26 -0
  25. package/dist/testing/index.cjs +58 -0
  26. package/dist/testing/index.d.cts +12 -0
  27. package/dist/testing/index.d.mts +12 -0
  28. package/dist/testing/index.d.ts +12 -0
  29. package/dist/testing/index.mjs +52 -0
  30. package/dist/vcr/index.cjs +92 -0
  31. package/dist/vcr/index.d.cts +27 -0
  32. package/dist/vcr/index.d.mts +27 -0
  33. package/dist/vcr/index.d.ts +27 -0
  34. package/dist/vcr/index.mjs +78 -0
  35. package/package.json +31 -41
  36. package/src/compose.test.ts +76 -0
  37. package/src/compose.ts +71 -0
  38. package/src/context.ts +14 -8
  39. package/src/index.ts +0 -5
  40. package/src/indexer.test.ts +109 -186
  41. package/src/indexer.ts +244 -144
  42. package/src/internal/testing.ts +135 -0
  43. package/src/plugins/config.ts +4 -4
  44. package/src/plugins/index.ts +8 -1
  45. package/src/plugins/logger.ts +30 -0
  46. package/src/plugins/persistence.ts +24 -187
  47. package/src/testing/index.ts +50 -3
  48. package/src/vcr/record.ts +6 -4
  49. package/src/vcr/replay.ts +8 -18
  50. package/src/hooks/index.ts +0 -2
  51. package/src/hooks/useKVStore.ts +0 -12
  52. package/src/hooks/useSink.ts +0 -13
  53. package/src/plugins/kv.test.ts +0 -120
  54. package/src/plugins/kv.ts +0 -132
  55. package/src/plugins/persistence.test.ts +0 -151
  56. package/src/sink.ts +0 -36
  57. package/src/sinks/csv.test.ts +0 -65
  58. package/src/sinks/csv.ts +0 -159
  59. package/src/sinks/drizzle/Int8Range.ts +0 -52
  60. package/src/sinks/drizzle/delete.ts +0 -42
  61. package/src/sinks/drizzle/drizzle.test.ts +0 -239
  62. package/src/sinks/drizzle/drizzle.ts +0 -115
  63. package/src/sinks/drizzle/index.ts +0 -6
  64. package/src/sinks/drizzle/insert.ts +0 -39
  65. package/src/sinks/drizzle/select.ts +0 -44
  66. package/src/sinks/drizzle/transaction.ts +0 -49
  67. package/src/sinks/drizzle/update.ts +0 -47
  68. package/src/sinks/drizzle/utils.ts +0 -36
  69. package/src/sinks/sqlite.test.ts +0 -99
  70. package/src/sinks/sqlite.ts +0 -170
  71. package/src/testing/helper.ts +0 -13
  72. package/src/testing/indexer.ts +0 -35
  73. package/src/testing/setup.ts +0 -59
  74. package/src/testing/vcr.ts +0 -54
package/src/compose.ts ADDED
@@ -0,0 +1,71 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2021 - present, Yusuke Wada and Hono contributors
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import type { IndexerContext } from "./context";
26
+
27
+ export type NextFunction = () => Promise<void>;
28
+ export type MiddlewareFunction<C> = (
29
+ context: C,
30
+ next: NextFunction,
31
+ ) => Promise<void>;
32
+
33
+ export function compose<C extends IndexerContext>(
34
+ middleware: MiddlewareFunction<C>[],
35
+ ): (context: C, next?: NextFunction) => Promise<void> {
36
+ return (context, next) => {
37
+ let index = -1;
38
+
39
+ return dispatch(0);
40
+
41
+ /// Dispatch the middleware functions.
42
+ async function dispatch(i: number): Promise<void> {
43
+ if (i <= index) {
44
+ throw new Error("next() called multiple times");
45
+ }
46
+ index = i;
47
+
48
+ let handler: MiddlewareFunction<C> | undefined;
49
+
50
+ if (i >= middleware.length) {
51
+ if (next) {
52
+ await next();
53
+ }
54
+
55
+ return;
56
+ }
57
+
58
+ if (middleware[i]) {
59
+ handler = middleware[i];
60
+ } else {
61
+ handler = i === middleware.length ? next : undefined;
62
+ }
63
+
64
+ if (!handler) {
65
+ throw new Error("Handler not found");
66
+ }
67
+
68
+ await handler(context, () => dispatch(i + 1));
69
+ }
70
+ };
71
+ }
package/src/context.ts CHANGED
@@ -1,19 +1,25 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import type { Cursor, DataFinality } from "@apibara/protocol";
2
3
  import { getContext } from "unctx";
3
- import type { Sink } from "./sink";
4
4
 
5
5
  // biome-ignore lint/suspicious/noExplicitAny: context type
6
- export interface IndexerContext<TTxnParams = any> extends Record<string, any> {
7
- sink?: Sink<TTxnParams>;
8
- sinkTransaction?: TTxnParams;
9
- }
6
+ export interface IndexerContext extends Record<string, any> {}
10
7
 
11
8
  export const indexerAsyncContext = getContext<IndexerContext>("indexer", {
12
9
  asyncContext: true,
13
10
  AsyncLocalStorage,
14
11
  });
15
12
 
16
- // biome-ignore lint/suspicious/noExplicitAny: <explanation>
17
- export function useIndexerContext<TTxnParams = any>() {
18
- return indexerAsyncContext.use() as IndexerContext<TTxnParams>;
13
+ export function useIndexerContext() {
14
+ return indexerAsyncContext.use() as IndexerContext;
15
+ }
16
+
17
+ export interface MessageMetadataContext extends IndexerContext {
18
+ cursor?: Cursor;
19
+ endCursor?: Cursor;
20
+ finality?: DataFinality;
21
+ }
22
+
23
+ export function useMessageMetadataContext(): MessageMetadataContext {
24
+ return useIndexerContext() as MessageMetadataContext;
19
25
  }
package/src/index.ts CHANGED
@@ -1,7 +1,2 @@
1
1
  export * from "./indexer";
2
- export * from "./sink";
3
2
  export { useIndexerContext } from "./context";
4
-
5
- export * from "./plugins";
6
- export * from "./vcr";
7
- export * from "./hooks";
@@ -1,15 +1,37 @@
1
+ import type { Cursor, DataFinality } from "@apibara/protocol";
1
2
  import {
2
3
  type MockBlock,
3
4
  MockClient,
4
5
  type MockFilter,
5
6
  } from "@apibara/protocol/testing";
6
- import Database from "better-sqlite3";
7
7
  import { describe, expect, it } from "vitest";
8
- import { useSink } from "./hooks";
8
+ import { type IndexerContext, useMessageMetadataContext } from "./context";
9
9
  import { run } from "./indexer";
10
- import { SqlitePersistence, sqlitePersistence } from "./plugins/persistence";
11
- import { generateMockMessages, vcr } from "./testing";
12
- import { getMockIndexer } from "./testing/indexer";
10
+ import {
11
+ generateMockMessages,
12
+ getMockIndexer,
13
+ mockSink,
14
+ useMockSink,
15
+ } from "./internal/testing";
16
+
17
+ async function transform<TData>({
18
+ block: { data },
19
+ }: {
20
+ block: { data?: TData };
21
+ cursor?: Cursor;
22
+ endCursor?: Cursor;
23
+ finality?: DataFinality;
24
+ context: IndexerContext;
25
+ }) {
26
+ const { cursor, endCursor, finality } = useMessageMetadataContext();
27
+ const { output } = useMockSink();
28
+ output.push({
29
+ data,
30
+ cursor: cursor?.orderKey,
31
+ endCursor: endCursor?.orderKey,
32
+ finality,
33
+ });
34
+ }
13
35
 
14
36
  describe("Run Test", () => {
15
37
  it("should stream messages", async () => {
@@ -17,121 +39,78 @@ describe("Run Test", () => {
17
39
  return generateMockMessages();
18
40
  });
19
41
 
20
- const sink = vcr();
42
+ const output: unknown[] = [];
21
43
 
22
44
  const indexer = getMockIndexer({
23
- sink,
24
45
  override: {
25
- transform: async ({ context, endCursor, block: { data } }) => {
26
- const { writer } = useSink({ context });
27
- writer.insert([{ data }]);
28
- },
46
+ plugins: [mockSink({ output })],
47
+ transform,
29
48
  },
30
49
  });
31
50
 
32
51
  await run(client, indexer);
33
52
 
34
- expect(sink.result).toMatchInlineSnapshot(`
53
+ expect(output).toMatchInlineSnapshot(`
35
54
  [
36
55
  {
37
- "data": [
38
- {
39
- "data": "5000000",
40
- },
41
- ],
42
- "endCursor": {
43
- "orderKey": 5000000n,
44
- },
56
+ "cursor": 4999999n,
57
+ "data": "5000000",
58
+ "endCursor": 5000000n,
59
+ "finality": "accepted",
45
60
  },
46
61
  {
47
- "data": [
48
- {
49
- "data": "5000001",
50
- },
51
- ],
52
- "endCursor": {
53
- "orderKey": 5000001n,
54
- },
62
+ "cursor": 5000000n,
63
+ "data": "5000001",
64
+ "endCursor": 5000001n,
65
+ "finality": "accepted",
55
66
  },
56
67
  {
57
- "data": [
58
- {
59
- "data": "5000002",
60
- },
61
- ],
62
- "endCursor": {
63
- "orderKey": 5000002n,
64
- },
68
+ "cursor": 5000001n,
69
+ "data": "5000002",
70
+ "endCursor": 5000002n,
71
+ "finality": "accepted",
65
72
  },
66
73
  {
67
- "data": [
68
- {
69
- "data": "5000003",
70
- },
71
- ],
72
- "endCursor": {
73
- "orderKey": 5000003n,
74
- },
74
+ "cursor": 5000002n,
75
+ "data": "5000003",
76
+ "endCursor": 5000003n,
77
+ "finality": "accepted",
75
78
  },
76
79
  {
77
- "data": [
78
- {
79
- "data": "5000004",
80
- },
81
- ],
82
- "endCursor": {
83
- "orderKey": 5000004n,
84
- },
80
+ "cursor": 5000003n,
81
+ "data": "5000004",
82
+ "endCursor": 5000004n,
83
+ "finality": "accepted",
85
84
  },
86
85
  {
87
- "data": [
88
- {
89
- "data": "5000005",
90
- },
91
- ],
92
- "endCursor": {
93
- "orderKey": 5000005n,
94
- },
86
+ "cursor": 5000004n,
87
+ "data": "5000005",
88
+ "endCursor": 5000005n,
89
+ "finality": "accepted",
95
90
  },
96
91
  {
97
- "data": [
98
- {
99
- "data": "5000006",
100
- },
101
- ],
102
- "endCursor": {
103
- "orderKey": 5000006n,
104
- },
92
+ "cursor": 5000005n,
93
+ "data": "5000006",
94
+ "endCursor": 5000006n,
95
+ "finality": "accepted",
105
96
  },
106
97
  {
107
- "data": [
108
- {
109
- "data": "5000007",
110
- },
111
- ],
112
- "endCursor": {
113
- "orderKey": 5000007n,
114
- },
98
+ "cursor": 5000006n,
99
+ "data": "5000007",
100
+ "endCursor": 5000007n,
101
+ "finality": "accepted",
115
102
  },
116
103
  {
117
- "data": [
118
- {
119
- "data": "5000008",
120
- },
121
- ],
122
- "endCursor": {
123
- "orderKey": 5000008n,
124
- },
104
+ "cursor": 5000007n,
105
+ "data": "5000008",
106
+ "endCursor": 5000008n,
107
+ "finality": "accepted",
125
108
  },
126
109
  {
127
- "data": [
128
- {
129
- "data": "5000009",
130
- },
131
- ],
132
- "endCursor": {
133
- "orderKey": 5000009n,
134
- },
110
+ "cursor": 5000008n,
111
+ "data": "5000009",
112
+ "endCursor": 5000009n,
113
+ "finality": "accepted",
135
114
  },
136
115
  ]
137
116
  `);
@@ -255,18 +234,11 @@ describe("Run Test", () => {
255
234
  return [];
256
235
  });
257
236
 
258
- const db = Database(":memory:");
237
+ const output: unknown[] = [];
238
+ const metadata: Record<string, unknown> = {};
259
239
 
260
- const sink = vcr();
261
-
262
- // create mock indexer with persistence plugin
263
240
  const indexer = getMockIndexer({
264
- plugins: [
265
- sqlitePersistence({
266
- database: db,
267
- }),
268
- ],
269
- sink,
241
+ plugins: [mockSink({ output, metadata })],
270
242
  override: {
271
243
  startingCursor: { orderKey: 100n },
272
244
  factory: async ({ block }) => {
@@ -280,88 +252,55 @@ describe("Run Test", () => {
280
252
 
281
253
  return {};
282
254
  },
283
- transform: async ({ context, endCursor, block: { data } }) => {
284
- const { writer } = useSink({ context });
285
- writer.insert([{ data }]);
286
- },
255
+ transform,
287
256
  },
288
257
  });
289
258
 
290
259
  await run(client, indexer);
291
260
 
292
- const store = new SqlitePersistence<MockFilter>(db);
293
-
294
- const latest = store.get();
261
+ expect((metadata.lastCursor as Cursor).orderKey).toEqual(108n);
262
+ expect((metadata.lastFilter as { filter: unknown }).filter).toEqual("BC");
295
263
 
296
- expect(latest.cursor?.orderKey).toEqual(108n);
297
- expect(latest.filter?.filter).toEqual("BC");
298
-
299
- expect(sink.result).toMatchInlineSnapshot(`
264
+ expect(output).toMatchInlineSnapshot(`
300
265
  [
301
266
  {
302
- "data": [
303
- {
304
- "data": "103B",
305
- },
306
- ],
307
- "endCursor": {
308
- "orderKey": 103n,
309
- },
267
+ "cursor": 102n,
268
+ "data": "103B",
269
+ "endCursor": 103n,
270
+ "finality": "accepted",
310
271
  },
311
272
  {
312
- "data": [
313
- {
314
- "data": "104B",
315
- },
316
- ],
317
- "endCursor": {
318
- "orderKey": 104n,
319
- },
273
+ "cursor": 103n,
274
+ "data": "104B",
275
+ "endCursor": 104n,
276
+ "finality": "accepted",
320
277
  },
321
278
  {
322
- "data": [
323
- {
324
- "data": "105B",
325
- },
326
- ],
327
- "endCursor": {
328
- "orderKey": 105n,
329
- },
279
+ "cursor": 104n,
280
+ "data": "105B",
281
+ "endCursor": 105n,
282
+ "finality": "accepted",
330
283
  },
331
284
  {
332
- "data": [
333
- {
334
- "data": "106BC",
335
- },
336
- ],
337
- "endCursor": {
338
- "orderKey": 106n,
339
- },
285
+ "cursor": 105n,
286
+ "data": "106BC",
287
+ "endCursor": 106n,
288
+ "finality": "accepted",
340
289
  },
341
290
  {
342
- "data": [
343
- {
344
- "data": "107BC",
345
- },
346
- ],
347
- "endCursor": {
348
- "orderKey": 107n,
349
- },
291
+ "cursor": 106n,
292
+ "data": "107BC",
293
+ "endCursor": 107n,
294
+ "finality": "accepted",
350
295
  },
351
296
  {
352
- "data": [
353
- {
354
- "data": "108BC",
355
- },
356
- ],
357
- "endCursor": {
358
- "orderKey": 108n,
359
- },
297
+ "cursor": 107n,
298
+ "data": "108BC",
299
+ "endCursor": 108n,
300
+ "finality": "accepted",
360
301
  },
361
302
  ]
362
303
  `);
363
-
364
- db.close();
365
304
  });
366
305
 
367
306
  it("factory mode: last cursor should persist when error is thrown in indexer", async () => {
@@ -441,18 +380,11 @@ describe("Run Test", () => {
441
380
  return [];
442
381
  });
443
382
 
444
- const db = Database(":memory:");
383
+ const output: unknown[] = [];
384
+ const metadata: Record<string, unknown> = {};
445
385
 
446
- const sink = vcr();
447
-
448
- // create mock indexer with persistence plugin
449
386
  const indexer = getMockIndexer({
450
- plugins: [
451
- sqlitePersistence({
452
- database: db,
453
- }),
454
- ],
455
- sink,
387
+ plugins: [mockSink({ output, metadata })],
456
388
  override: {
457
389
  startingCursor: { orderKey: 100n },
458
390
  factory: async ({ block }) => {
@@ -466,10 +398,7 @@ describe("Run Test", () => {
466
398
 
467
399
  return {};
468
400
  },
469
- transform: async ({ context, endCursor, block: { data } }) => {
470
- const { writer } = useSink({ context });
471
- writer.insert([{ data }]);
472
- },
401
+ transform,
473
402
  },
474
403
  });
475
404
 
@@ -477,15 +406,9 @@ describe("Run Test", () => {
477
406
  "this error should occurr!",
478
407
  );
479
408
 
480
- const store = new SqlitePersistence<MockFilter>(db);
481
-
482
- const latest = store.get();
483
-
484
- expect(latest.cursor?.orderKey).toEqual(103n);
485
- expect(latest.filter?.filter).toEqual("B");
486
-
487
- expect(sink.result).toMatchInlineSnapshot("[]");
409
+ expect((metadata.lastCursor as Cursor).orderKey).toEqual(103n);
410
+ expect((metadata.lastFilter as { filter: unknown }).filter).toEqual("B");
488
411
 
489
- db.close();
412
+ expect(output).toMatchInlineSnapshot("[]");
490
413
  });
491
414
  });