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

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 (66) hide show
  1. package/package.json +30 -19
  2. package/src/context.ts +8 -3
  3. package/src/hooks/index.ts +1 -0
  4. package/src/hooks/useSink.ts +13 -0
  5. package/src/index.ts +1 -0
  6. package/src/indexer.test.ts +70 -41
  7. package/src/indexer.ts +168 -168
  8. package/src/plugins/config.ts +4 -4
  9. package/src/plugins/kv.ts +2 -2
  10. package/src/plugins/persistence.test.ts +10 -6
  11. package/src/plugins/persistence.ts +3 -3
  12. package/src/sink.ts +21 -24
  13. package/src/sinks/csv.test.ts +15 -3
  14. package/src/sinks/csv.ts +68 -7
  15. package/src/sinks/drizzle/Int8Range.ts +52 -0
  16. package/src/sinks/drizzle/delete.ts +42 -0
  17. package/src/sinks/drizzle/drizzle.test.ts +239 -0
  18. package/src/sinks/drizzle/drizzle.ts +115 -0
  19. package/src/sinks/drizzle/index.ts +6 -0
  20. package/src/sinks/drizzle/insert.ts +39 -0
  21. package/src/sinks/drizzle/select.ts +44 -0
  22. package/src/sinks/drizzle/transaction.ts +49 -0
  23. package/src/sinks/drizzle/update.ts +47 -0
  24. package/src/sinks/drizzle/utils.ts +36 -0
  25. package/src/sinks/sqlite.test.ts +13 -1
  26. package/src/sinks/sqlite.ts +65 -5
  27. package/src/testing/indexer.ts +15 -8
  28. package/src/testing/setup.ts +5 -5
  29. package/src/testing/vcr.ts +42 -4
  30. package/src/vcr/record.ts +2 -2
  31. package/src/vcr/replay.ts +3 -3
  32. package/.turbo/turbo-build.log +0 -37
  33. package/CHANGELOG.md +0 -83
  34. package/LICENSE.txt +0 -202
  35. package/build.config.ts +0 -16
  36. package/dist/index.cjs +0 -34
  37. package/dist/index.d.cts +0 -21
  38. package/dist/index.d.mts +0 -21
  39. package/dist/index.d.ts +0 -21
  40. package/dist/index.mjs +0 -19
  41. package/dist/shared/indexer.371c0482.mjs +0 -15
  42. package/dist/shared/indexer.3852a4d3.d.ts +0 -91
  43. package/dist/shared/indexer.50aa7ab0.mjs +0 -268
  44. package/dist/shared/indexer.7c118fb5.d.cts +0 -28
  45. package/dist/shared/indexer.7c118fb5.d.mts +0 -28
  46. package/dist/shared/indexer.7c118fb5.d.ts +0 -28
  47. package/dist/shared/indexer.a27bcb35.d.cts +0 -91
  48. package/dist/shared/indexer.c8ef02ea.cjs +0 -289
  49. package/dist/shared/indexer.e05aedca.cjs +0 -19
  50. package/dist/shared/indexer.f7dd57e5.d.mts +0 -91
  51. package/dist/sinks/csv.cjs +0 -66
  52. package/dist/sinks/csv.d.cts +0 -34
  53. package/dist/sinks/csv.d.mts +0 -34
  54. package/dist/sinks/csv.d.ts +0 -34
  55. package/dist/sinks/csv.mjs +0 -59
  56. package/dist/sinks/sqlite.cjs +0 -71
  57. package/dist/sinks/sqlite.d.cts +0 -41
  58. package/dist/sinks/sqlite.d.mts +0 -41
  59. package/dist/sinks/sqlite.d.ts +0 -41
  60. package/dist/sinks/sqlite.mjs +0 -68
  61. package/dist/testing/index.cjs +0 -63
  62. package/dist/testing/index.d.cts +0 -29
  63. package/dist/testing/index.d.mts +0 -29
  64. package/dist/testing/index.d.ts +0 -29
  65. package/dist/testing/index.mjs +0 -59
  66. package/tsconfig.json +0 -11
@@ -1,268 +0,0 @@
1
- import fs$1 from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { createHooks, createDebugger } from 'hookable';
4
- import { klona } from 'klona/full';
5
- import assert from 'node:assert';
6
- import fs from 'node:fs';
7
- import { MockClient } from '@apibara/protocol/testing';
8
- import consola from 'consola';
9
- import { AsyncLocalStorage } from 'node:async_hooks';
10
- import { getContext } from 'unctx';
11
- import { trace } from '@opentelemetry/api';
12
- import { d as defaultSink, S as Sink } from './indexer.371c0482.mjs';
13
-
14
- const indexerAsyncContext = getContext("indexer", {
15
- asyncContext: true,
16
- AsyncLocalStorage
17
- });
18
- function useIndexerContext() {
19
- return indexerAsyncContext.use();
20
- }
21
-
22
- const tracer = trace.getTracer("@apibara/indexer");
23
-
24
- function defineIndexer(streamConfig) {
25
- return (config) => ({
26
- streamConfig,
27
- ...config
28
- });
29
- }
30
- function createIndexer({
31
- streamConfig,
32
- ...options
33
- }) {
34
- const indexer = {
35
- options,
36
- streamConfig,
37
- hooks: createHooks()
38
- };
39
- if (indexer.options.debug) {
40
- createDebugger(indexer.hooks, { tag: "indexer" });
41
- }
42
- indexer.hooks.addHooks(indexer.options.hooks ?? {});
43
- for (const plugin of indexer.options.plugins ?? []) {
44
- plugin(indexer);
45
- }
46
- return indexer;
47
- }
48
- async function run(client, indexer, sinkArg) {
49
- await indexerAsyncContext.callAsync({}, async () => {
50
- await indexer.hooks.callHook("run:before");
51
- const sink = sinkArg ?? defaultSink();
52
- sink.hook("write", async ({ data }) => {
53
- await indexer.hooks.callHook("sink:write", { data });
54
- });
55
- sink.hook("flush", async ({ endCursor, finality }) => {
56
- await indexer.hooks.callHook("sink:flush", { endCursor, finality });
57
- });
58
- const isFactoryMode = indexer.options.factory !== void 0;
59
- const request = indexer.streamConfig.Request.make({
60
- filter: isFactoryMode ? [indexer.options.filter, {}] : [indexer.options.filter],
61
- finality: indexer.options.finality,
62
- startingCursor: indexer.options.startingCursor
63
- });
64
- const options = {};
65
- await indexer.hooks.callHook("connect:before", { request, options });
66
- let mainFilter;
67
- if (isFactoryMode) {
68
- mainFilter = request.filter[1];
69
- }
70
- let stream = client.streamData(request, options);
71
- await indexer.hooks.callHook("connect:after");
72
- let state = {
73
- _tag: "normal"
74
- };
75
- while (true) {
76
- for await (const message of stream) {
77
- await indexer.hooks.callHook("message", { message });
78
- switch (message._tag) {
79
- case "data": {
80
- await tracer.startActiveSpan("message data", async (span) => {
81
- const blocks = message.data.data;
82
- const { cursor, endCursor, finality } = message.data;
83
- let block;
84
- const output = [];
85
- if (isFactoryMode) {
86
- assert(indexer.options.factory !== void 0);
87
- const [factoryBlock, mainBlock] = blocks;
88
- block = mainBlock;
89
- if (state._tag === "normal" && factoryBlock !== null) {
90
- const { data, filter } = await indexer.options.factory(factoryBlock);
91
- if (!filter) {
92
- output.push(...data ?? []);
93
- } else {
94
- mainFilter = indexer.streamConfig.mergeFilter(
95
- mainFilter,
96
- filter
97
- );
98
- const request2 = indexer.streamConfig.Request.make({
99
- filter: [indexer.options.filter, mainFilter],
100
- finality: indexer.options.finality,
101
- startingCursor: cursor
102
- });
103
- await indexer.hooks.callHook("connect:factory", {
104
- request: request2,
105
- endCursor
106
- });
107
- stream = client.streamData(request2, options);
108
- state = {
109
- _tag: "recover",
110
- data
111
- };
112
- return;
113
- }
114
- } else if (state._tag === "recover") {
115
- output.push(...state.data ?? []);
116
- state = { _tag: "normal" };
117
- }
118
- } else {
119
- block = blocks[0];
120
- }
121
- if (block) {
122
- await tracer.startActiveSpan("handler", async (span2) => {
123
- await indexer.hooks.callHook("handler:before", {
124
- block,
125
- endCursor,
126
- finality
127
- });
128
- try {
129
- const transformOutput = await indexer.options.transform({
130
- block,
131
- cursor,
132
- endCursor,
133
- finality
134
- });
135
- output.push(...transformOutput);
136
- await indexer.hooks.callHook("handler:after", { output });
137
- } catch (error) {
138
- assert(error instanceof Error);
139
- await indexer.hooks.callHook("handler:exception", {
140
- error
141
- });
142
- throw error;
143
- }
144
- span2.end();
145
- });
146
- }
147
- if (output.length > 0) {
148
- await tracer.startActiveSpan("sink write", async (span2) => {
149
- await sink.write({
150
- data: output,
151
- cursor,
152
- endCursor,
153
- finality
154
- });
155
- span2.end();
156
- });
157
- }
158
- span.end();
159
- });
160
- break;
161
- }
162
- default: {
163
- consola.warn("unexpected message", message);
164
- throw new Error("not implemented");
165
- }
166
- }
167
- if (state._tag !== "normal") {
168
- break;
169
- }
170
- }
171
- if (state._tag !== "normal") {
172
- continue;
173
- }
174
- await indexer.hooks.callHook("run:after");
175
- break;
176
- }
177
- });
178
- }
179
-
180
- function deserialize(str) {
181
- return JSON.parse(
182
- str,
183
- (_, value) => typeof value === "string" && value.match(/^\d+n$/) ? BigInt(value.slice(0, -1)) : value
184
- );
185
- }
186
- function serialize(obj) {
187
- return JSON.stringify(
188
- obj,
189
- (_, value) => typeof value === "bigint" ? `${value.toString()}n` : value,
190
- " "
191
- );
192
- }
193
- function isCassetteAvailable(vcrConfig, cassetteName) {
194
- const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
195
- return fs.existsSync(filePath);
196
- }
197
-
198
- async function record(vcrConfig, client, indexerArg, cassetteOptions) {
199
- const indexer = klona(indexerArg);
200
- const messages = [];
201
- indexer.hooks.addHooks({
202
- "connect:before"({ options, request }) {
203
- request.startingCursor = cassetteOptions.startingCursor;
204
- options.endingCursor = cassetteOptions.endingCursor;
205
- },
206
- message({ message }) {
207
- messages.push(message);
208
- },
209
- async "run:after"() {
210
- const output = {
211
- filter: indexer.options.filter,
212
- messages
213
- };
214
- const filePath = path.join(
215
- vcrConfig.cassetteDir,
216
- `${cassetteOptions.name}.json`
217
- );
218
- await fs$1.writeFile(filePath, serialize(output), { flag: "w" });
219
- }
220
- });
221
- await run(client, indexer);
222
- }
223
-
224
- var __defProp = Object.defineProperty;
225
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
226
- var __publicField = (obj, key, value) => {
227
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
228
- return value;
229
- };
230
- class VcrSink extends Sink {
231
- constructor() {
232
- super(...arguments);
233
- __publicField(this, "result", []);
234
- }
235
- async write({ data, endCursor, finality }) {
236
- await this.callHook("write", { data });
237
- this.result.push({ data, endCursor });
238
- await this.callHook("flush", { endCursor, finality });
239
- }
240
- }
241
- function vcr() {
242
- return new VcrSink();
243
- }
244
-
245
- async function replay(vcrConfig, indexer, cassetteName) {
246
- const client = loadCassette(vcrConfig, cassetteName);
247
- const sink = vcr();
248
- await run(client, indexer, sink);
249
- return {
250
- outputs: sink.result
251
- };
252
- }
253
- function loadCassette(vcrConfig, cassetteName) {
254
- const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
255
- const data = fs.readFileSync(filePath, "utf8");
256
- const cassetteData = deserialize(data);
257
- const { filter, messages } = cassetteData;
258
- return new MockClient((request, options) => {
259
- assert.deepStrictEqual(
260
- request.filter,
261
- filter,
262
- "Request and Cassette filter mismatch"
263
- );
264
- return messages;
265
- });
266
- }
267
-
268
- export { VcrSink as V, deserialize as a, record as b, createIndexer as c, defineIndexer as d, replay as e, isCassetteAvailable as i, loadCassette as l, run as r, serialize as s, useIndexerContext as u, vcr as v };
@@ -1,28 +0,0 @@
1
- import { Cursor, DataFinality } from '@apibara/protocol';
2
- import { Hookable } from 'hookable';
3
-
4
- type SinkData = Record<string, unknown>;
5
- interface SinkEvents {
6
- write({ data }: {
7
- data: SinkData[];
8
- }): void;
9
- flush({ endCursor, finality, }: {
10
- endCursor?: Cursor;
11
- finality: DataFinality;
12
- }): void;
13
- }
14
- type SinkWriteArgs = {
15
- data: SinkData[];
16
- cursor?: Cursor | undefined;
17
- endCursor?: Cursor | undefined;
18
- finality: DataFinality;
19
- };
20
- declare abstract class Sink extends Hookable<SinkEvents> {
21
- abstract write({ data, cursor, endCursor, finality, }: SinkWriteArgs): Promise<void>;
22
- }
23
- declare class DefaultSink extends Sink {
24
- write({ data, endCursor, finality }: SinkWriteArgs): Promise<void>;
25
- }
26
- declare function defaultSink(): DefaultSink;
27
-
28
- export { DefaultSink as D, type SinkData as S, type SinkEvents as a, type SinkWriteArgs as b, Sink as c, defaultSink as d };
@@ -1,28 +0,0 @@
1
- import { Cursor, DataFinality } from '@apibara/protocol';
2
- import { Hookable } from 'hookable';
3
-
4
- type SinkData = Record<string, unknown>;
5
- interface SinkEvents {
6
- write({ data }: {
7
- data: SinkData[];
8
- }): void;
9
- flush({ endCursor, finality, }: {
10
- endCursor?: Cursor;
11
- finality: DataFinality;
12
- }): void;
13
- }
14
- type SinkWriteArgs = {
15
- data: SinkData[];
16
- cursor?: Cursor | undefined;
17
- endCursor?: Cursor | undefined;
18
- finality: DataFinality;
19
- };
20
- declare abstract class Sink extends Hookable<SinkEvents> {
21
- abstract write({ data, cursor, endCursor, finality, }: SinkWriteArgs): Promise<void>;
22
- }
23
- declare class DefaultSink extends Sink {
24
- write({ data, endCursor, finality }: SinkWriteArgs): Promise<void>;
25
- }
26
- declare function defaultSink(): DefaultSink;
27
-
28
- export { DefaultSink as D, type SinkData as S, type SinkEvents as a, type SinkWriteArgs as b, Sink as c, defaultSink as d };
@@ -1,28 +0,0 @@
1
- import { Cursor, DataFinality } from '@apibara/protocol';
2
- import { Hookable } from 'hookable';
3
-
4
- type SinkData = Record<string, unknown>;
5
- interface SinkEvents {
6
- write({ data }: {
7
- data: SinkData[];
8
- }): void;
9
- flush({ endCursor, finality, }: {
10
- endCursor?: Cursor;
11
- finality: DataFinality;
12
- }): void;
13
- }
14
- type SinkWriteArgs = {
15
- data: SinkData[];
16
- cursor?: Cursor | undefined;
17
- endCursor?: Cursor | undefined;
18
- finality: DataFinality;
19
- };
20
- declare abstract class Sink extends Hookable<SinkEvents> {
21
- abstract write({ data, cursor, endCursor, finality, }: SinkWriteArgs): Promise<void>;
22
- }
23
- declare class DefaultSink extends Sink {
24
- write({ data, endCursor, finality }: SinkWriteArgs): Promise<void>;
25
- }
26
- declare function defaultSink(): DefaultSink;
27
-
28
- export { DefaultSink as D, type SinkData as S, type SinkEvents as a, type SinkWriteArgs as b, Sink as c, defaultSink as d };
@@ -1,91 +0,0 @@
1
- import { StreamDataRequest, StreamDataOptions, Cursor, DataFinality, StreamDataResponse, StreamConfig, Client } from '@apibara/protocol';
2
- import { NestedHooks, Hookable } from 'hookable';
3
- import { c as Sink, S as SinkData } from './indexer.7c118fb5.cjs';
4
-
5
- type IndexerPlugin<TFilter, TBlock, TRet> = (indexer: Indexer<TFilter, TBlock, TRet>) => void;
6
- declare function defineIndexerPlugin<TFilter, TBlock, TRet>(def: IndexerPlugin<TFilter, TBlock, TRet>): IndexerPlugin<TFilter, TBlock, TRet>;
7
-
8
- interface IndexerHooks<TFilter, TBlock, TRet> {
9
- "run:before": () => void;
10
- "run:after": () => void;
11
- "connect:before": ({ request, options, }: {
12
- request: StreamDataRequest<TFilter>;
13
- options: StreamDataOptions;
14
- }) => void;
15
- "connect:after": () => void;
16
- "connect:factory": ({ request, endCursor, }: {
17
- request: StreamDataRequest<TFilter>;
18
- endCursor?: Cursor;
19
- }) => void;
20
- "handler:before": ({ block, finality, endCursor, }: {
21
- block: TBlock;
22
- finality: DataFinality;
23
- endCursor?: Cursor;
24
- }) => void;
25
- "handler:after": ({ output }: {
26
- output: TRet[];
27
- }) => void;
28
- "handler:exception": ({ error }: {
29
- error: Error;
30
- }) => void;
31
- "sink:write": ({ data }: {
32
- data: TRet[];
33
- }) => void;
34
- "sink:flush": ({ endCursor, finality, }: {
35
- endCursor?: Cursor;
36
- finality: DataFinality;
37
- }) => void;
38
- message: ({ message }: {
39
- message: StreamDataResponse<TBlock>;
40
- }) => void;
41
- }
42
- interface IndexerConfig<TFilter, TBlock, TRet> {
43
- streamUrl: string;
44
- filter: TFilter;
45
- finality?: DataFinality;
46
- startingCursor?: Cursor;
47
- factory?: (block: TBlock) => Promise<{
48
- filter?: TFilter;
49
- data?: TRet[];
50
- }>;
51
- transform: (args: {
52
- block: TBlock;
53
- cursor?: Cursor | undefined;
54
- endCursor?: Cursor | undefined;
55
- finality: DataFinality;
56
- }) => Promise<TRet[]>;
57
- hooks?: NestedHooks<IndexerHooks<TFilter, TBlock, TRet>>;
58
- plugins?: ReadonlyArray<IndexerPlugin<TFilter, TBlock, TRet>>;
59
- debug?: boolean;
60
- }
61
- interface IndexerWithStreamConfig<TFilter, TBlock, TRet> extends IndexerConfig<TFilter, TBlock, TRet> {
62
- streamConfig: StreamConfig<TFilter, TBlock>;
63
- }
64
- declare function defineIndexer<TFilter, TBlock>(streamConfig: StreamConfig<TFilter, TBlock>): <TRet>(config: IndexerConfig<TFilter, TBlock, TRet>) => IndexerWithStreamConfig<TFilter, TBlock, TRet>;
65
- interface Indexer<TFilter, TBlock, TRet> {
66
- streamConfig: StreamConfig<TFilter, TBlock>;
67
- options: IndexerConfig<TFilter, TBlock, TRet>;
68
- hooks: Hookable<IndexerHooks<TFilter, TBlock, TRet>>;
69
- }
70
- declare function createIndexer<TFilter, TBlock, TRet>({ streamConfig, ...options }: IndexerWithStreamConfig<TFilter, TBlock, TRet>): Indexer<TFilter, TBlock, TRet>;
71
- declare function run<TFilter, TBlock, TRet>(client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock, TRet>, sinkArg?: Sink): Promise<void>;
72
-
73
- type VcrConfig = {
74
- cassetteDir: string;
75
- };
76
- type CassetteOptions = {
77
- name: string;
78
- startingCursor: Cursor;
79
- endingCursor: Cursor;
80
- };
81
-
82
- declare function replay<TFilter, TBlock, TRet>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock, TRet>, cassetteName: string): Promise<VcrReplayResult>;
83
- type VcrReplayResult = {
84
- outputs: Array<{
85
- endCursor?: Cursor;
86
- data: SinkData[];
87
- }>;
88
- };
89
- declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
90
-
91
- export { type CassetteOptions as C, type Indexer as I, type VcrConfig as V, type IndexerHooks as a, type IndexerConfig as b, type IndexerWithStreamConfig as c, defineIndexer as d, createIndexer as e, type IndexerPlugin as f, defineIndexerPlugin as g, replay as h, type VcrReplayResult as i, loadCassette as l, run as r };