@apibara/indexer 2.0.0-beta.4 → 2.0.0-beta.40

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 (87) hide show
  1. package/dist/index.cjs +278 -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 +267 -0
  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 +43 -0
  22. package/dist/plugins/index.d.cts +18 -0
  23. package/dist/plugins/index.d.mts +18 -0
  24. package/dist/plugins/index.d.ts +18 -0
  25. package/dist/plugins/index.mjs +38 -0
  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 +66 -0
  36. package/dist/testing/index.d.cts +12 -0
  37. package/dist/testing/index.d.mts +12 -0
  38. package/dist/testing/index.d.ts +12 -0
  39. package/dist/testing/index.mjs +60 -0
  40. package/dist/vcr/index.cjs +92 -0
  41. package/dist/vcr/index.d.cts +27 -0
  42. package/dist/vcr/index.d.mts +27 -0
  43. package/dist/vcr/index.d.ts +27 -0
  44. package/dist/vcr/index.mjs +78 -0
  45. package/package.json +43 -41
  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 +274 -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 +6 -4
  62. package/src/vcr/replay.ts +8 -18
  63. package/src/hooks/index.ts +0 -2
  64. package/src/hooks/useKVStore.ts +0 -12
  65. package/src/hooks/useSink.ts +0 -13
  66. package/src/plugins/kv.test.ts +0 -120
  67. package/src/plugins/kv.ts +0 -132
  68. package/src/plugins/persistence.test.ts +0 -151
  69. package/src/sink.ts +0 -36
  70. package/src/sinks/csv.test.ts +0 -65
  71. package/src/sinks/csv.ts +0 -159
  72. package/src/sinks/drizzle/Int8Range.ts +0 -52
  73. package/src/sinks/drizzle/delete.ts +0 -42
  74. package/src/sinks/drizzle/drizzle.test.ts +0 -239
  75. package/src/sinks/drizzle/drizzle.ts +0 -115
  76. package/src/sinks/drizzle/index.ts +0 -6
  77. package/src/sinks/drizzle/insert.ts +0 -39
  78. package/src/sinks/drizzle/select.ts +0 -44
  79. package/src/sinks/drizzle/transaction.ts +0 -49
  80. package/src/sinks/drizzle/update.ts +0 -47
  81. package/src/sinks/drizzle/utils.ts +0 -36
  82. package/src/sinks/sqlite.test.ts +0 -99
  83. package/src/sinks/sqlite.ts +0 -170
  84. package/src/testing/helper.ts +0 -13
  85. package/src/testing/indexer.ts +0 -35
  86. package/src/testing/setup.ts +0 -59
  87. package/src/testing/vcr.ts +0 -54
@@ -0,0 +1,78 @@
1
+ import fs$1 from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { run } from '../index.mjs';
4
+ import assert from 'node:assert';
5
+ import fs from 'node:fs';
6
+ import { MockClient } from '@apibara/protocol/testing';
7
+ import '@apibara/protocol';
8
+ import 'consola';
9
+ import 'hookable';
10
+ import '../shared/indexer.a55ad619.mjs';
11
+ import 'node:async_hooks';
12
+ import 'unctx';
13
+ import '@opentelemetry/api';
14
+
15
+ function deserialize(str) {
16
+ return JSON.parse(
17
+ str,
18
+ (_, value) => typeof value === "string" && value.match(/^\d+n$/) ? BigInt(value.slice(0, -1)) : value
19
+ );
20
+ }
21
+ function serialize(obj) {
22
+ return JSON.stringify(
23
+ obj,
24
+ (_, value) => typeof value === "bigint" ? `${value.toString()}n` : value,
25
+ " "
26
+ );
27
+ }
28
+ function isCassetteAvailable(vcrConfig, cassetteName) {
29
+ const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
30
+ return fs.existsSync(filePath);
31
+ }
32
+
33
+ async function record(vcrConfig, client, indexer, cassetteOptions) {
34
+ const messages = [];
35
+ indexer.hooks.addHooks({
36
+ "connect:before"({ options, request }) {
37
+ request.startingCursor = cassetteOptions.startingCursor;
38
+ options.endingCursor = cassetteOptions.endingCursor;
39
+ },
40
+ message({ message }) {
41
+ messages.push(message);
42
+ },
43
+ async "run:after"() {
44
+ const output = {
45
+ filter: indexer.options.filter,
46
+ messages
47
+ };
48
+ await fs$1.mkdir(vcrConfig.cassetteDir, { recursive: true });
49
+ const filePath = path.join(
50
+ vcrConfig.cassetteDir,
51
+ `${cassetteOptions.name}.json`
52
+ );
53
+ await fs$1.writeFile(filePath, serialize(output), { flag: "w" });
54
+ }
55
+ });
56
+ await run(client, indexer);
57
+ }
58
+
59
+ async function replay(vcrConfig, indexer, cassetteName) {
60
+ const client = loadCassette(vcrConfig, cassetteName);
61
+ await run(client, indexer);
62
+ }
63
+ function loadCassette(vcrConfig, cassetteName) {
64
+ const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
65
+ const data = fs.readFileSync(filePath, "utf8");
66
+ const cassetteData = deserialize(data);
67
+ const { filter, messages } = cassetteData;
68
+ return new MockClient((request, options) => {
69
+ assert.deepStrictEqual(
70
+ request.filter,
71
+ [filter],
72
+ "Indexer and cassette filter mismatch. Hint: delete the cassette and run again."
73
+ );
74
+ return messages;
75
+ });
76
+ }
77
+
78
+ export { deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@apibara/indexer",
3
- "version": "2.0.0-beta.4",
3
+ "version": "2.0.0-beta.40",
4
4
  "type": "module",
5
- "source": "./src/index.ts",
5
+ "files": [
6
+ "dist",
7
+ "src",
8
+ "README.md"
9
+ ],
6
10
  "main": "./dist/index.mjs",
11
+ "types": "./dist/index.d.ts",
7
12
  "exports": {
8
13
  ".": {
9
14
  "types": "./dist/index.d.ts",
@@ -11,56 +16,63 @@
11
16
  "require": "./dist/index.cjs",
12
17
  "default": "./dist/index.mjs"
13
18
  },
14
- "./sinks/sqlite": {
15
- "types": "./dist/sinks/sqlite.d.ts",
16
- "import": "./dist/sinks/sqlite.mjs",
17
- "require": "./dist/sinks/sqlite.cjs",
18
- "default": "./dist/sinks/sqlite.mjs"
19
- },
20
- "./sinks/csv": {
21
- "types": "./dist/sinks/csv.d.ts",
22
- "import": "./dist/sinks/csv.mjs",
23
- "require": "./dist/sinks/csv.cjs",
24
- "default": "./dist/sinks/csv.mjs"
25
- },
26
- "./sinks/drizzle": {
27
- "types": "./dist/sinks/drizzle/index.d.ts",
28
- "import": "./dist/sinks/drizzle/index.mjs",
29
- "require": "./dist/sinks/drizzle/index.cjs",
30
- "default": "./dist/sinks/drizzle/index.mjs"
31
- },
32
19
  "./testing": {
33
20
  "types": "./dist/testing/index.d.ts",
34
21
  "import": "./dist/testing/index.mjs",
35
22
  "require": "./dist/testing/index.cjs",
36
23
  "default": "./dist/testing/index.mjs"
24
+ },
25
+ "./vcr": {
26
+ "types": "./dist/vcr/index.d.ts",
27
+ "import": "./dist/vcr/index.mjs",
28
+ "require": "./dist/vcr/index.cjs",
29
+ "default": "./dist/vcr/index.mjs"
30
+ },
31
+ "./plugins": {
32
+ "types": "./dist/plugins/index.d.ts",
33
+ "import": "./dist/plugins/index.mjs",
34
+ "require": "./dist/plugins/index.cjs",
35
+ "default": "./dist/plugins/index.mjs"
36
+ },
37
+ "./internal": {
38
+ "types": "./dist/internal/index.d.ts",
39
+ "import": "./dist/internal/index.mjs",
40
+ "require": "./dist/internal/index.cjs",
41
+ "default": "./dist/internal/index.mjs"
42
+ },
43
+ "./internal/testing": {
44
+ "types": "./dist/internal/testing.d.ts",
45
+ "import": "./dist/internal/testing.mjs",
46
+ "require": "./dist/internal/testing.cjs",
47
+ "default": "./dist/internal/testing.mjs"
48
+ },
49
+ "./internal/plugins": {
50
+ "types": "./dist/internal/plugins.d.ts",
51
+ "import": "./dist/internal/plugins.mjs",
52
+ "require": "./dist/internal/plugins.cjs",
53
+ "default": "./dist/internal/plugins.mjs"
37
54
  }
38
55
  },
39
- "publishConfig": {},
40
56
  "scripts": {
41
57
  "build": "unbuild",
42
- "lint": "biome check .",
43
58
  "typecheck": "tsc --noEmit",
59
+ "lint": "biome check .",
44
60
  "lint:fix": "pnpm lint --write",
45
61
  "test": "vitest",
46
- "test:ci": "vitest run",
47
- "format": "biome format . --write"
62
+ "test:ci": "vitest run"
48
63
  },
49
64
  "devDependencies": {
65
+ "@electric-sql/pglite": "^0.2.14",
50
66
  "@types/better-sqlite3": "^7.6.11",
51
67
  "@types/node": "^20.14.0",
52
68
  "@types/pg": "^8.11.10",
53
- "better-sqlite3": "^11.1.2",
54
- "csv-stringify": "^6.5.0",
55
- "drizzle-orm": "^0.33.0",
56
- "pg": "^8.12.0",
57
- "postgres-range": "^1.1.4",
58
69
  "unbuild": "^2.0.0",
59
70
  "vitest": "^1.6.0"
60
71
  },
61
72
  "dependencies": {
62
- "@apibara/protocol": "2.0.0-beta.4",
73
+ "@apibara/protocol": "2.0.0-beta.40",
63
74
  "@opentelemetry/api": "^1.9.0",
75
+ "ci-info": "^4.1.0",
64
76
  "consola": "^3.2.3",
65
77
  "hookable": "^5.5.3",
66
78
  "klona": "^2.0.6",
@@ -68,16 +80,6 @@
68
80
  "unctx": "^2.3.1"
69
81
  },
70
82
  "peerDependencies": {
71
- "better-sqlite3": "^11.1.2",
72
- "csv-stringify": "^6.5.0",
73
- "drizzle-orm": "^0.33.0",
74
- "postgres-range": "^1.1.4",
75
83
  "vitest": "^1.6.0"
76
- },
77
- "files": [
78
- "dist",
79
- "src",
80
- "README.md"
81
- ],
82
- "types": "./dist/index.d.ts"
84
+ }
83
85
  }
@@ -0,0 +1,76 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { type MiddlewareFunction, type NextFunction, compose } from "./compose";
3
+
4
+ type C = {
5
+ bag: Record<string, unknown>;
6
+ finalized: boolean;
7
+ };
8
+
9
+ type MiddlewareTuple = MiddlewareFunction<C>;
10
+
11
+ describe("compose", () => {
12
+ async function a(context: C, next: NextFunction) {
13
+ context.bag.log = "log";
14
+ await next();
15
+ }
16
+
17
+ async function b(context: C, next: NextFunction) {
18
+ await next();
19
+ context.bag.headers = "custom-header";
20
+ }
21
+
22
+ async function c(context: C, next: NextFunction) {
23
+ context.bag.xxx = "yyy";
24
+ await next();
25
+ context.bag.zzz = context.bag.xxx;
26
+ }
27
+
28
+ async function handler(context: C, next: NextFunction) {
29
+ context.bag.log = `${context.bag.log} message`;
30
+ await next();
31
+ context.bag.message = "new response";
32
+ }
33
+
34
+ const middleware: MiddlewareTuple[] = [];
35
+
36
+ middleware.push(a);
37
+ middleware.push(b);
38
+ middleware.push(c);
39
+ middleware.push(handler);
40
+
41
+ it("composes", async () => {
42
+ const context: C = {
43
+ bag: {},
44
+ finalized: false,
45
+ };
46
+
47
+ const composed = compose<C>(middleware);
48
+ await composed(context);
49
+
50
+ expect(context.bag.log).toBeDefined();
51
+ expect(context.bag.log).toBe("log message");
52
+ expect(context.bag.headers).toBe("custom-header");
53
+ expect(context.bag.xxx).toBe("yyy");
54
+ expect(context.bag.zzz).toBe("yyy");
55
+ expect(context.finalized).toBe(false);
56
+ });
57
+
58
+ it("accepts a next function", async () => {
59
+ const context: C = {
60
+ bag: {},
61
+ finalized: false,
62
+ };
63
+
64
+ const composed = compose<C>(middleware);
65
+ await composed(context, async () => {
66
+ context.finalized = true;
67
+ });
68
+
69
+ expect(context.bag.log).toBeDefined();
70
+ expect(context.bag.log).toBe("log message");
71
+ expect(context.bag.headers).toBe("custom-header");
72
+ expect(context.bag.xxx).toBe("yyy");
73
+ expect(context.bag.zzz).toBe("yyy");
74
+ expect(context.finalized).toBe(true);
75
+ });
76
+ });
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";