@apibara/indexer 2.0.0-beta.5 → 2.0.0-beta.6

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.
@@ -0,0 +1,300 @@
1
+ 'use strict';
2
+
3
+ const fs$1 = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const consola = require('consola');
6
+ const full = require('klona/full');
7
+ const assert = require('node:assert');
8
+ const fs = require('node:fs');
9
+ const testing = require('@apibara/protocol/testing');
10
+ const hookable = require('hookable');
11
+ const node_async_hooks = require('node:async_hooks');
12
+ const unctx = require('unctx');
13
+ const api = require('@opentelemetry/api');
14
+ const sink = require('./indexer.a8b7ab1f.cjs');
15
+
16
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
17
+
18
+ const fs__default$1 = /*#__PURE__*/_interopDefaultCompat(fs$1);
19
+ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
20
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
21
+ const assert__default = /*#__PURE__*/_interopDefaultCompat(assert);
22
+ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
23
+
24
+ const indexerAsyncContext = unctx.getContext("indexer", {
25
+ asyncContext: true,
26
+ AsyncLocalStorage: node_async_hooks.AsyncLocalStorage
27
+ });
28
+ function useIndexerContext() {
29
+ return indexerAsyncContext.use();
30
+ }
31
+
32
+ const tracer = api.trace.getTracer("@apibara/indexer");
33
+
34
+ function defineIndexer(streamConfig) {
35
+ return (config) => ({
36
+ streamConfig,
37
+ ...config
38
+ });
39
+ }
40
+ function createIndexer({
41
+ streamConfig,
42
+ ...options
43
+ }) {
44
+ const indexer = {
45
+ options,
46
+ streamConfig,
47
+ hooks: hookable.createHooks()
48
+ };
49
+ if (indexer.options.debug) {
50
+ hookable.createDebugger(indexer.hooks, { tag: "indexer" });
51
+ }
52
+ indexer.hooks.addHooks(indexer.options.hooks ?? {});
53
+ for (const plugin of indexer.options.plugins ?? []) {
54
+ plugin(indexer);
55
+ }
56
+ return indexer;
57
+ }
58
+ async function run(client, indexer) {
59
+ await indexerAsyncContext.callAsync({}, async () => {
60
+ const context = useIndexerContext();
61
+ const sink$1 = indexer.options.sink ?? sink.defaultSink();
62
+ context.sink = sink$1;
63
+ await indexer.hooks.callHook("run:before");
64
+ const isFactoryMode = indexer.options.factory !== void 0;
65
+ const request = indexer.streamConfig.Request.make({
66
+ filter: isFactoryMode ? [indexer.options.filter, {}] : [indexer.options.filter],
67
+ finality: indexer.options.finality,
68
+ startingCursor: indexer.options.startingCursor
69
+ });
70
+ const options = {};
71
+ await indexer.hooks.callHook("connect:before", { request, options });
72
+ let mainFilter;
73
+ if (isFactoryMode) {
74
+ mainFilter = request.filter[1];
75
+ }
76
+ let stream = client.streamData(request, options)[Symbol.asyncIterator]();
77
+ await indexer.hooks.callHook("connect:after");
78
+ while (true) {
79
+ const { value: message, done } = await stream.next();
80
+ if (done) {
81
+ break;
82
+ }
83
+ await indexer.hooks.callHook("message", { message });
84
+ switch (message._tag) {
85
+ case "data": {
86
+ await tracer.startActiveSpan("message data", async (span) => {
87
+ const blocks = message.data.data;
88
+ const { cursor, endCursor, finality } = message.data;
89
+ await sink$1.transaction(
90
+ { cursor, endCursor, finality },
91
+ async (txn) => {
92
+ context.sinkTransaction = txn;
93
+ let block;
94
+ if (isFactoryMode) {
95
+ assert__default(indexer.options.factory !== void 0);
96
+ const [factoryBlock, mainBlock] = blocks;
97
+ block = mainBlock;
98
+ if (factoryBlock !== null) {
99
+ const { filter } = await indexer.options.factory({
100
+ block: factoryBlock,
101
+ context
102
+ });
103
+ if (filter) {
104
+ mainFilter = indexer.streamConfig.mergeFilter(
105
+ mainFilter,
106
+ filter
107
+ );
108
+ const request2 = indexer.streamConfig.Request.make({
109
+ filter: [indexer.options.filter, mainFilter],
110
+ finality: indexer.options.finality,
111
+ startingCursor: cursor
112
+ });
113
+ await indexer.hooks.callHook("connect:factory", {
114
+ request: request2,
115
+ endCursor
116
+ });
117
+ stream = client.streamData(request2, options)[Symbol.asyncIterator]();
118
+ const { value: message2 } = await stream.next();
119
+ assert__default(message2._tag === "data");
120
+ const [_factoryBlock, _block] = message2.data.data;
121
+ block = _block;
122
+ }
123
+ }
124
+ } else {
125
+ block = blocks[0];
126
+ }
127
+ if (block) {
128
+ await tracer.startActiveSpan("handler", async (span2) => {
129
+ await indexer.hooks.callHook("handler:before", {
130
+ block,
131
+ endCursor,
132
+ finality
133
+ });
134
+ try {
135
+ await indexer.options.transform({
136
+ block,
137
+ cursor,
138
+ endCursor,
139
+ finality,
140
+ context
141
+ });
142
+ await indexer.hooks.callHook("handler:after", {
143
+ block,
144
+ finality,
145
+ endCursor
146
+ });
147
+ } catch (error) {
148
+ assert__default(error instanceof Error);
149
+ await indexer.hooks.callHook("handler:exception", {
150
+ error
151
+ });
152
+ throw error;
153
+ }
154
+ span2.end();
155
+ });
156
+ }
157
+ }
158
+ );
159
+ await indexer.hooks.callHook("transaction:commit", {
160
+ finality,
161
+ endCursor
162
+ });
163
+ span.end();
164
+ });
165
+ break;
166
+ }
167
+ case "invalidate": {
168
+ await tracer.startActiveSpan("message invalidate", async (span) => {
169
+ await sink$1.invalidate(message.invalidate.cursor);
170
+ });
171
+ break;
172
+ }
173
+ default: {
174
+ consola__default.warn("unexpected message", message);
175
+ throw new Error("not implemented");
176
+ }
177
+ }
178
+ await indexer.hooks.callHook("run:after");
179
+ }
180
+ });
181
+ }
182
+
183
+ function deserialize(str) {
184
+ return JSON.parse(
185
+ str,
186
+ (_, value) => typeof value === "string" && value.match(/^\d+n$/) ? BigInt(value.slice(0, -1)) : value
187
+ );
188
+ }
189
+ function serialize(obj) {
190
+ return JSON.stringify(
191
+ obj,
192
+ (_, value) => typeof value === "bigint" ? `${value.toString()}n` : value,
193
+ " "
194
+ );
195
+ }
196
+ function isCassetteAvailable(vcrConfig, cassetteName) {
197
+ const filePath = path__default.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
198
+ return fs__default.existsSync(filePath);
199
+ }
200
+
201
+ async function record(vcrConfig, client, indexerArg, cassetteOptions) {
202
+ const indexer = full.klona(indexerArg);
203
+ const messages = [];
204
+ indexer.hooks.addHooks({
205
+ "connect:before"({ options, request }) {
206
+ request.startingCursor = cassetteOptions.startingCursor;
207
+ options.endingCursor = cassetteOptions.endingCursor;
208
+ },
209
+ message({ message }) {
210
+ messages.push(message);
211
+ },
212
+ async "run:after"() {
213
+ const output = {
214
+ filter: indexer.options.filter,
215
+ messages
216
+ };
217
+ const filePath = path__default.join(
218
+ vcrConfig.cassetteDir,
219
+ `${cassetteOptions.name}.json`
220
+ );
221
+ await fs__default$1.writeFile(filePath, serialize(output), { flag: "w" });
222
+ }
223
+ });
224
+ await run(client, indexer);
225
+ }
226
+
227
+ var __defProp = Object.defineProperty;
228
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
229
+ var __publicField = (obj, key, value) => {
230
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
231
+ return value;
232
+ };
233
+ const transactionHelper = (context) => {
234
+ return {
235
+ insert: (data) => {
236
+ context.buffer.push(...data);
237
+ }
238
+ };
239
+ };
240
+ class VcrSink extends sink.Sink {
241
+ constructor() {
242
+ super(...arguments);
243
+ __publicField(this, "result", []);
244
+ }
245
+ write({ data, endCursor }) {
246
+ if (data.length === 0)
247
+ return;
248
+ this.result.push({ data, endCursor });
249
+ }
250
+ async transaction({ cursor, endCursor, finality }, cb) {
251
+ const context = {
252
+ buffer: []
253
+ };
254
+ const writer = transactionHelper(context);
255
+ await cb({ writer });
256
+ this.write({ data: context.buffer, endCursor });
257
+ }
258
+ async invalidate(cursor) {
259
+ throw new Error("Not implemented");
260
+ }
261
+ }
262
+ function vcr() {
263
+ return new VcrSink();
264
+ }
265
+
266
+ async function replay(vcrConfig, indexer, cassetteName) {
267
+ const client = loadCassette(vcrConfig, cassetteName);
268
+ const sink = vcr();
269
+ await run(client, indexer);
270
+ return {
271
+ outputs: sink.result
272
+ };
273
+ }
274
+ function loadCassette(vcrConfig, cassetteName) {
275
+ const filePath = path__default.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
276
+ const data = fs__default.readFileSync(filePath, "utf8");
277
+ const cassetteData = deserialize(data);
278
+ const { filter, messages } = cassetteData;
279
+ return new testing.MockClient((request, options) => {
280
+ assert__default.deepStrictEqual(
281
+ request.filter,
282
+ filter,
283
+ "Request and Cassette filter mismatch"
284
+ );
285
+ return messages;
286
+ });
287
+ }
288
+
289
+ exports.VcrSink = VcrSink;
290
+ exports.createIndexer = createIndexer;
291
+ exports.defineIndexer = defineIndexer;
292
+ exports.deserialize = deserialize;
293
+ exports.isCassetteAvailable = isCassetteAvailable;
294
+ exports.loadCassette = loadCassette;
295
+ exports.record = record;
296
+ exports.replay = replay;
297
+ exports.run = run;
298
+ exports.serialize = serialize;
299
+ exports.useIndexerContext = useIndexerContext;
300
+ exports.vcr = vcr;
@@ -0,0 +1,100 @@
1
+ import { StreamDataRequest, StreamDataOptions, Cursor, DataFinality, StreamDataResponse, StreamConfig, Client } from '@apibara/protocol';
2
+ import { NestedHooks, Hookable } from 'hookable';
3
+ import { b as Sink, S as SinkData } from './indexer.d227f25c.mjs';
4
+
5
+ interface IndexerContext<TTxnParams = any> extends Record<string, any> {
6
+ sink?: Sink<TTxnParams>;
7
+ sinkTransaction?: TTxnParams;
8
+ }
9
+ declare function useIndexerContext<TTxnParams = any>(): IndexerContext<TTxnParams>;
10
+
11
+ type IndexerPlugin<TFilter, TBlock, TTxnParams> = (indexer: Indexer<TFilter, TBlock, TTxnParams>) => void;
12
+ declare function defineIndexerPlugin<TFilter, TBlock, TTxnParams>(def: IndexerPlugin<TFilter, TBlock, TTxnParams>): IndexerPlugin<TFilter, TBlock, TTxnParams>;
13
+
14
+ interface IndexerHooks<TFilter, TBlock> {
15
+ "run:before": () => void;
16
+ "run:after": () => void;
17
+ "connect:before": ({ request, options, }: {
18
+ request: StreamDataRequest<TFilter>;
19
+ options: StreamDataOptions;
20
+ }) => void;
21
+ "connect:after": () => void;
22
+ "connect:factory": ({ request, endCursor, }: {
23
+ request: StreamDataRequest<TFilter>;
24
+ endCursor?: Cursor;
25
+ }) => void;
26
+ "handler:before": ({ block, finality, endCursor, }: {
27
+ block: TBlock;
28
+ finality: DataFinality;
29
+ endCursor?: Cursor;
30
+ }) => void;
31
+ "handler:after": ({ block, finality, endCursor, }: {
32
+ block: TBlock;
33
+ finality: DataFinality;
34
+ endCursor?: Cursor;
35
+ }) => void;
36
+ "transaction:commit": ({ finality, endCursor, }: {
37
+ finality: DataFinality;
38
+ endCursor?: Cursor;
39
+ }) => void;
40
+ "handler:exception": ({ error }: {
41
+ error: Error;
42
+ }) => void;
43
+ message: ({ message }: {
44
+ message: StreamDataResponse<TBlock>;
45
+ }) => void;
46
+ }
47
+ interface IndexerConfig<TFilter, TBlock, TTxnParams> {
48
+ streamUrl: string;
49
+ filter: TFilter;
50
+ finality?: DataFinality;
51
+ startingCursor?: Cursor;
52
+ sink?: Sink<TTxnParams>;
53
+ factory?: ({ block, context, }: {
54
+ block: TBlock;
55
+ context: IndexerContext<TTxnParams>;
56
+ }) => Promise<{
57
+ filter?: TFilter;
58
+ }>;
59
+ transform: (args: {
60
+ block: TBlock;
61
+ cursor?: Cursor | undefined;
62
+ endCursor?: Cursor | undefined;
63
+ finality: DataFinality;
64
+ context: IndexerContext<TTxnParams>;
65
+ }) => Promise<void>;
66
+ hooks?: NestedHooks<IndexerHooks<TFilter, TBlock>>;
67
+ plugins?: ReadonlyArray<IndexerPlugin<TFilter, TBlock, TTxnParams>>;
68
+ debug?: boolean;
69
+ }
70
+ interface IndexerWithStreamConfig<TFilter, TBlock, TTxnParams> extends IndexerConfig<TFilter, TBlock, TTxnParams> {
71
+ streamConfig: StreamConfig<TFilter, TBlock>;
72
+ }
73
+ declare function defineIndexer<TFilter, TBlock>(streamConfig: StreamConfig<TFilter, TBlock>): <TTxnParams>(config: IndexerConfig<TFilter, TBlock, TTxnParams>) => IndexerWithStreamConfig<TFilter, TBlock, TTxnParams>;
74
+ interface Indexer<TFilter, TBlock, TTxnParams> {
75
+ streamConfig: StreamConfig<TFilter, TBlock>;
76
+ options: IndexerConfig<TFilter, TBlock, TTxnParams>;
77
+ hooks: Hookable<IndexerHooks<TFilter, TBlock>>;
78
+ }
79
+ declare function createIndexer<TFilter, TBlock, TTxnParams>({ streamConfig, ...options }: IndexerWithStreamConfig<TFilter, TBlock, TTxnParams>): Indexer<TFilter, TBlock, TTxnParams>;
80
+ declare function run<TFilter, TBlock, TTxnParams>(client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock, TTxnParams>): Promise<void>;
81
+
82
+ type VcrConfig = {
83
+ cassetteDir: string;
84
+ };
85
+ type CassetteOptions = {
86
+ name: string;
87
+ startingCursor: Cursor;
88
+ endingCursor: Cursor;
89
+ };
90
+
91
+ declare function replay<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock, TTxnParams>, cassetteName: string): Promise<VcrReplayResult>;
92
+ type VcrReplayResult = {
93
+ outputs: Array<{
94
+ endCursor?: Cursor;
95
+ data: SinkData[];
96
+ }>;
97
+ };
98
+ declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
99
+
100
+ export { type CassetteOptions as C, type Indexer as I, type VcrConfig as V, type IndexerContext as a, type IndexerHooks as b, type IndexerConfig as c, type IndexerWithStreamConfig as d, defineIndexer as e, createIndexer as f, type IndexerPlugin as g, defineIndexerPlugin as h, replay as i, type VcrReplayResult as j, loadCassette as l, run as r, useIndexerContext as u };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const consola = require('consola');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
+
7
+ const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
8
+
9
+ class Sink {
10
+ }
11
+ class DefaultSink extends Sink {
12
+ async transaction({ cursor, endCursor, finality }, cb) {
13
+ await cb({});
14
+ }
15
+ async invalidate(cursor) {
16
+ consola__default.info(`Invalidating cursor ${cursor?.orderKey}`);
17
+ }
18
+ }
19
+ function defaultSink() {
20
+ return new DefaultSink();
21
+ }
22
+
23
+ exports.DefaultSink = DefaultSink;
24
+ exports.Sink = Sink;
25
+ exports.defaultSink = defaultSink;
@@ -0,0 +1,19 @@
1
+ import { Cursor, DataFinality } from '@apibara/protocol';
2
+
3
+ type SinkData = Record<string, unknown>;
4
+ type SinkCursorParams = {
5
+ cursor?: Cursor;
6
+ endCursor?: Cursor;
7
+ finality: DataFinality;
8
+ };
9
+ declare abstract class Sink<TTxnParams = unknown> {
10
+ abstract transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TTxnParams) => Promise<void>): Promise<void>;
11
+ abstract invalidate(cursor?: Cursor): Promise<void>;
12
+ }
13
+ declare class DefaultSink extends Sink<unknown> {
14
+ transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: unknown) => Promise<void>): Promise<void>;
15
+ invalidate(cursor?: Cursor): Promise<void>;
16
+ }
17
+ declare function defaultSink(): DefaultSink;
18
+
19
+ export { DefaultSink as D, type SinkData as S, type SinkCursorParams as a, Sink as b, defaultSink as d };
@@ -0,0 +1,19 @@
1
+ import { Cursor, DataFinality } from '@apibara/protocol';
2
+
3
+ type SinkData = Record<string, unknown>;
4
+ type SinkCursorParams = {
5
+ cursor?: Cursor;
6
+ endCursor?: Cursor;
7
+ finality: DataFinality;
8
+ };
9
+ declare abstract class Sink<TTxnParams = unknown> {
10
+ abstract transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TTxnParams) => Promise<void>): Promise<void>;
11
+ abstract invalidate(cursor?: Cursor): Promise<void>;
12
+ }
13
+ declare class DefaultSink extends Sink<unknown> {
14
+ transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: unknown) => Promise<void>): Promise<void>;
15
+ invalidate(cursor?: Cursor): Promise<void>;
16
+ }
17
+ declare function defaultSink(): DefaultSink;
18
+
19
+ export { DefaultSink as D, type SinkData as S, type SinkCursorParams as a, Sink as b, defaultSink as d };
@@ -0,0 +1,19 @@
1
+ import { Cursor, DataFinality } from '@apibara/protocol';
2
+
3
+ type SinkData = Record<string, unknown>;
4
+ type SinkCursorParams = {
5
+ cursor?: Cursor;
6
+ endCursor?: Cursor;
7
+ finality: DataFinality;
8
+ };
9
+ declare abstract class Sink<TTxnParams = unknown> {
10
+ abstract transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: TTxnParams) => Promise<void>): Promise<void>;
11
+ abstract invalidate(cursor?: Cursor): Promise<void>;
12
+ }
13
+ declare class DefaultSink extends Sink<unknown> {
14
+ transaction({ cursor, endCursor, finality }: SinkCursorParams, cb: (params: unknown) => Promise<void>): Promise<void>;
15
+ invalidate(cursor?: Cursor): Promise<void>;
16
+ }
17
+ declare function defaultSink(): DefaultSink;
18
+
19
+ export { DefaultSink as D, type SinkData as S, type SinkCursorParams as a, Sink as b, defaultSink as d };
@@ -0,0 +1,100 @@
1
+ import { StreamDataRequest, StreamDataOptions, Cursor, DataFinality, StreamDataResponse, StreamConfig, Client } from '@apibara/protocol';
2
+ import { NestedHooks, Hookable } from 'hookable';
3
+ import { b as Sink, S as SinkData } from './indexer.d227f25c.cjs';
4
+
5
+ interface IndexerContext<TTxnParams = any> extends Record<string, any> {
6
+ sink?: Sink<TTxnParams>;
7
+ sinkTransaction?: TTxnParams;
8
+ }
9
+ declare function useIndexerContext<TTxnParams = any>(): IndexerContext<TTxnParams>;
10
+
11
+ type IndexerPlugin<TFilter, TBlock, TTxnParams> = (indexer: Indexer<TFilter, TBlock, TTxnParams>) => void;
12
+ declare function defineIndexerPlugin<TFilter, TBlock, TTxnParams>(def: IndexerPlugin<TFilter, TBlock, TTxnParams>): IndexerPlugin<TFilter, TBlock, TTxnParams>;
13
+
14
+ interface IndexerHooks<TFilter, TBlock> {
15
+ "run:before": () => void;
16
+ "run:after": () => void;
17
+ "connect:before": ({ request, options, }: {
18
+ request: StreamDataRequest<TFilter>;
19
+ options: StreamDataOptions;
20
+ }) => void;
21
+ "connect:after": () => void;
22
+ "connect:factory": ({ request, endCursor, }: {
23
+ request: StreamDataRequest<TFilter>;
24
+ endCursor?: Cursor;
25
+ }) => void;
26
+ "handler:before": ({ block, finality, endCursor, }: {
27
+ block: TBlock;
28
+ finality: DataFinality;
29
+ endCursor?: Cursor;
30
+ }) => void;
31
+ "handler:after": ({ block, finality, endCursor, }: {
32
+ block: TBlock;
33
+ finality: DataFinality;
34
+ endCursor?: Cursor;
35
+ }) => void;
36
+ "transaction:commit": ({ finality, endCursor, }: {
37
+ finality: DataFinality;
38
+ endCursor?: Cursor;
39
+ }) => void;
40
+ "handler:exception": ({ error }: {
41
+ error: Error;
42
+ }) => void;
43
+ message: ({ message }: {
44
+ message: StreamDataResponse<TBlock>;
45
+ }) => void;
46
+ }
47
+ interface IndexerConfig<TFilter, TBlock, TTxnParams> {
48
+ streamUrl: string;
49
+ filter: TFilter;
50
+ finality?: DataFinality;
51
+ startingCursor?: Cursor;
52
+ sink?: Sink<TTxnParams>;
53
+ factory?: ({ block, context, }: {
54
+ block: TBlock;
55
+ context: IndexerContext<TTxnParams>;
56
+ }) => Promise<{
57
+ filter?: TFilter;
58
+ }>;
59
+ transform: (args: {
60
+ block: TBlock;
61
+ cursor?: Cursor | undefined;
62
+ endCursor?: Cursor | undefined;
63
+ finality: DataFinality;
64
+ context: IndexerContext<TTxnParams>;
65
+ }) => Promise<void>;
66
+ hooks?: NestedHooks<IndexerHooks<TFilter, TBlock>>;
67
+ plugins?: ReadonlyArray<IndexerPlugin<TFilter, TBlock, TTxnParams>>;
68
+ debug?: boolean;
69
+ }
70
+ interface IndexerWithStreamConfig<TFilter, TBlock, TTxnParams> extends IndexerConfig<TFilter, TBlock, TTxnParams> {
71
+ streamConfig: StreamConfig<TFilter, TBlock>;
72
+ }
73
+ declare function defineIndexer<TFilter, TBlock>(streamConfig: StreamConfig<TFilter, TBlock>): <TTxnParams>(config: IndexerConfig<TFilter, TBlock, TTxnParams>) => IndexerWithStreamConfig<TFilter, TBlock, TTxnParams>;
74
+ interface Indexer<TFilter, TBlock, TTxnParams> {
75
+ streamConfig: StreamConfig<TFilter, TBlock>;
76
+ options: IndexerConfig<TFilter, TBlock, TTxnParams>;
77
+ hooks: Hookable<IndexerHooks<TFilter, TBlock>>;
78
+ }
79
+ declare function createIndexer<TFilter, TBlock, TTxnParams>({ streamConfig, ...options }: IndexerWithStreamConfig<TFilter, TBlock, TTxnParams>): Indexer<TFilter, TBlock, TTxnParams>;
80
+ declare function run<TFilter, TBlock, TTxnParams>(client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock, TTxnParams>): Promise<void>;
81
+
82
+ type VcrConfig = {
83
+ cassetteDir: string;
84
+ };
85
+ type CassetteOptions = {
86
+ name: string;
87
+ startingCursor: Cursor;
88
+ endingCursor: Cursor;
89
+ };
90
+
91
+ declare function replay<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock, TTxnParams>, cassetteName: string): Promise<VcrReplayResult>;
92
+ type VcrReplayResult = {
93
+ outputs: Array<{
94
+ endCursor?: Cursor;
95
+ data: SinkData[];
96
+ }>;
97
+ };
98
+ declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
99
+
100
+ export { type CassetteOptions as C, type Indexer as I, type VcrConfig as V, type IndexerContext as a, type IndexerHooks as b, type IndexerConfig as c, type IndexerWithStreamConfig as d, defineIndexer as e, createIndexer as f, type IndexerPlugin as g, defineIndexerPlugin as h, replay as i, type VcrReplayResult as j, loadCassette as l, run as r, useIndexerContext as u };
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const csvStringify = require('csv-stringify');
5
+ const sink = require('../shared/indexer.a8b7ab1f.cjs');
6
+ require('consola');
7
+
8
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
9
+
10
+ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
11
+
12
+ const transactionHelper = (context) => {
13
+ return {
14
+ insert: (data) => {
15
+ context.buffer.push(...data);
16
+ }
17
+ };
18
+ };
19
+ class CsvSink extends sink.Sink {
20
+ constructor(_stringifier, _config) {
21
+ super();
22
+ this._stringifier = _stringifier;
23
+ this._config = _config;
24
+ }
25
+ async write({
26
+ data,
27
+ endCursor
28
+ }) {
29
+ data = this.processCursorColumn(data, endCursor);
30
+ await this.insertToCSV(data);
31
+ }
32
+ async transaction({ cursor, endCursor, finality }, cb) {
33
+ const context = {
34
+ buffer: []
35
+ };
36
+ const writer = transactionHelper(context);
37
+ await cb({ writer });
38
+ await this.write({ data: context.buffer, endCursor });
39
+ }
40
+ async invalidate(cursor) {
41
+ throw new Error("Not implemented");
42
+ }
43
+ async insertToCSV(data) {
44
+ if (data.length === 0)
45
+ return;
46
+ return await new Promise((resolve, reject) => {
47
+ for (const row of data) {
48
+ this._stringifier.write(row, (err) => {
49
+ if (err)
50
+ throw new Error(err.message);
51
+ if (row === data[data.length - 1]) {
52
+ resolve();
53
+ }
54
+ });
55
+ }
56
+ });
57
+ }
58
+ processCursorColumn(data, endCursor) {
59
+ const { cursorColumn } = this._config;
60
+ if (cursorColumn && data.some(
61
+ (row) => Number(row[cursorColumn]) !== Number(endCursor?.orderKey)
62
+ )) {
63
+ throw new Error(
64
+ `Mismatch of ${cursorColumn} and Cursor ${Number(endCursor?.orderKey)}`
65
+ );
66
+ }
67
+ if (cursorColumn) {
68
+ return data;
69
+ }
70
+ return data.map((row) => ({
71
+ ...row,
72
+ _cursor: Number(endCursor?.orderKey)
73
+ }));
74
+ }
75
+ }
76
+ const csv = (args) => {
77
+ const { csvOptions, filepath, ...sinkOptions } = args;
78
+ const stringifier = csvStringify.stringify({ ...csvOptions });
79
+ const writeStream = fs__default.createWriteStream(filepath, { flags: "a" });
80
+ stringifier.pipe(writeStream);
81
+ return new CsvSink(stringifier, sinkOptions);
82
+ };
83
+
84
+ exports.CsvSink = CsvSink;
85
+ exports.csv = csv;