@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.
package/dist/index.cjs ADDED
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ const replay = require('./shared/indexer.99ec3128.cjs');
4
+ const sink = require('./shared/indexer.a8b7ab1f.cjs');
5
+ require('node:fs/promises');
6
+ require('node:path');
7
+ require('consola');
8
+ require('klona/full');
9
+ require('node:assert');
10
+ require('node:fs');
11
+ require('@apibara/protocol/testing');
12
+ require('hookable');
13
+ require('node:async_hooks');
14
+ require('unctx');
15
+ require('@opentelemetry/api');
16
+
17
+ function defineIndexerPlugin(def) {
18
+ return def;
19
+ }
20
+
21
+ function useKVStore() {
22
+ const ctx = replay.useIndexerContext();
23
+ if (!ctx?.kv)
24
+ throw new Error("KV Plugin is not available in context!");
25
+ return ctx.kv;
26
+ }
27
+
28
+ function useSink({
29
+ context
30
+ }) {
31
+ if (!context.sinkTransaction) {
32
+ throw new Error("Transaction context doesn't exist!");
33
+ }
34
+ return context.sinkTransaction;
35
+ }
36
+
37
+ exports.createIndexer = replay.createIndexer;
38
+ exports.defineIndexer = replay.defineIndexer;
39
+ exports.deserialize = replay.deserialize;
40
+ exports.isCassetteAvailable = replay.isCassetteAvailable;
41
+ exports.loadCassette = replay.loadCassette;
42
+ exports.record = replay.record;
43
+ exports.replay = replay.replay;
44
+ exports.run = replay.run;
45
+ exports.serialize = replay.serialize;
46
+ exports.useIndexerContext = replay.useIndexerContext;
47
+ exports.DefaultSink = sink.DefaultSink;
48
+ exports.Sink = sink.Sink;
49
+ exports.defaultSink = sink.defaultSink;
50
+ exports.defineIndexerPlugin = defineIndexerPlugin;
51
+ exports.useKVStore = useKVStore;
52
+ exports.useSink = useSink;
@@ -0,0 +1,47 @@
1
+ import { V as VcrConfig, I as Indexer, C as CassetteOptions, a as IndexerContext } from './shared/indexer.ff960804.cjs';
2
+ export { c as IndexerConfig, b as IndexerHooks, g as IndexerPlugin, d as IndexerWithStreamConfig, j as VcrReplayResult, f as createIndexer, e as defineIndexer, h as defineIndexerPlugin, l as loadCassette, i as replay, r as run, u as useIndexerContext } from './shared/indexer.ff960804.cjs';
3
+ export { D as DefaultSink, b as Sink, a as SinkCursorParams, S as SinkData, d as defaultSink } from './shared/indexer.d227f25c.cjs';
4
+ import { StreamDataResponse, Client, DataFinality, Cursor } from '@apibara/protocol';
5
+ import { Database } from 'better-sqlite3';
6
+ import 'hookable';
7
+
8
+ declare function deserialize(str: string): any;
9
+ declare function serialize(obj: Record<string, unknown>): string;
10
+ declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
11
+
12
+ type CassetteDataType<TFilter, TBlock> = {
13
+ filter: TFilter;
14
+ messages: StreamDataResponse<TBlock>[];
15
+ };
16
+ declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
17
+
18
+ declare class KVStore {
19
+ private _db;
20
+ private _finality;
21
+ private _endCursor;
22
+ /** Sqlite Queries Prepare Statements */
23
+ private _beginTxnQuery;
24
+ private _commitTxnQuery;
25
+ private _rollbackTxnQuery;
26
+ private _getQuery;
27
+ private _updateToBlockQuery;
28
+ private _insertIntoKvsQuery;
29
+ private _delQuery;
30
+ constructor(_db: Database, _finality: DataFinality, _endCursor: Cursor);
31
+ static initialize(db: Database): void;
32
+ beginTransaction(): void;
33
+ commitTransaction(): void;
34
+ rollbackTransaction(): void;
35
+ get<T>(key: string): T;
36
+ put<T>(key: string, value: T): void;
37
+ del(key: string): void;
38
+ }
39
+
40
+ type UseKVStoreResult = InstanceType<typeof KVStore>;
41
+ declare function useKVStore(): UseKVStoreResult;
42
+
43
+ declare function useSink<TTxnParams>({ context, }: {
44
+ context: IndexerContext<TTxnParams>;
45
+ }): NonNullable<TTxnParams>;
46
+
47
+ export { type CassetteDataType, CassetteOptions, Indexer, type UseKVStoreResult, VcrConfig, deserialize, isCassetteAvailable, record, serialize, useKVStore, useSink };
@@ -0,0 +1,47 @@
1
+ import { V as VcrConfig, I as Indexer, C as CassetteOptions, a as IndexerContext } from './shared/indexer.9aa22867.mjs';
2
+ export { c as IndexerConfig, b as IndexerHooks, g as IndexerPlugin, d as IndexerWithStreamConfig, j as VcrReplayResult, f as createIndexer, e as defineIndexer, h as defineIndexerPlugin, l as loadCassette, i as replay, r as run, u as useIndexerContext } from './shared/indexer.9aa22867.mjs';
3
+ export { D as DefaultSink, b as Sink, a as SinkCursorParams, S as SinkData, d as defaultSink } from './shared/indexer.d227f25c.mjs';
4
+ import { StreamDataResponse, Client, DataFinality, Cursor } from '@apibara/protocol';
5
+ import { Database } from 'better-sqlite3';
6
+ import 'hookable';
7
+
8
+ declare function deserialize(str: string): any;
9
+ declare function serialize(obj: Record<string, unknown>): string;
10
+ declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
11
+
12
+ type CassetteDataType<TFilter, TBlock> = {
13
+ filter: TFilter;
14
+ messages: StreamDataResponse<TBlock>[];
15
+ };
16
+ declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
17
+
18
+ declare class KVStore {
19
+ private _db;
20
+ private _finality;
21
+ private _endCursor;
22
+ /** Sqlite Queries Prepare Statements */
23
+ private _beginTxnQuery;
24
+ private _commitTxnQuery;
25
+ private _rollbackTxnQuery;
26
+ private _getQuery;
27
+ private _updateToBlockQuery;
28
+ private _insertIntoKvsQuery;
29
+ private _delQuery;
30
+ constructor(_db: Database, _finality: DataFinality, _endCursor: Cursor);
31
+ static initialize(db: Database): void;
32
+ beginTransaction(): void;
33
+ commitTransaction(): void;
34
+ rollbackTransaction(): void;
35
+ get<T>(key: string): T;
36
+ put<T>(key: string, value: T): void;
37
+ del(key: string): void;
38
+ }
39
+
40
+ type UseKVStoreResult = InstanceType<typeof KVStore>;
41
+ declare function useKVStore(): UseKVStoreResult;
42
+
43
+ declare function useSink<TTxnParams>({ context, }: {
44
+ context: IndexerContext<TTxnParams>;
45
+ }): NonNullable<TTxnParams>;
46
+
47
+ export { type CassetteDataType, CassetteOptions, Indexer, type UseKVStoreResult, VcrConfig, deserialize, isCassetteAvailable, record, serialize, useKVStore, useSink };
@@ -0,0 +1,47 @@
1
+ import { V as VcrConfig, I as Indexer, C as CassetteOptions, a as IndexerContext } from './shared/indexer.26fbe988.js';
2
+ export { c as IndexerConfig, b as IndexerHooks, g as IndexerPlugin, d as IndexerWithStreamConfig, j as VcrReplayResult, f as createIndexer, e as defineIndexer, h as defineIndexerPlugin, l as loadCassette, i as replay, r as run, u as useIndexerContext } from './shared/indexer.26fbe988.js';
3
+ export { D as DefaultSink, b as Sink, a as SinkCursorParams, S as SinkData, d as defaultSink } from './shared/indexer.d227f25c.js';
4
+ import { StreamDataResponse, Client, DataFinality, Cursor } from '@apibara/protocol';
5
+ import { Database } from 'better-sqlite3';
6
+ import 'hookable';
7
+
8
+ declare function deserialize(str: string): any;
9
+ declare function serialize(obj: Record<string, unknown>): string;
10
+ declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
11
+
12
+ type CassetteDataType<TFilter, TBlock> = {
13
+ filter: TFilter;
14
+ messages: StreamDataResponse<TBlock>[];
15
+ };
16
+ declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
17
+
18
+ declare class KVStore {
19
+ private _db;
20
+ private _finality;
21
+ private _endCursor;
22
+ /** Sqlite Queries Prepare Statements */
23
+ private _beginTxnQuery;
24
+ private _commitTxnQuery;
25
+ private _rollbackTxnQuery;
26
+ private _getQuery;
27
+ private _updateToBlockQuery;
28
+ private _insertIntoKvsQuery;
29
+ private _delQuery;
30
+ constructor(_db: Database, _finality: DataFinality, _endCursor: Cursor);
31
+ static initialize(db: Database): void;
32
+ beginTransaction(): void;
33
+ commitTransaction(): void;
34
+ rollbackTransaction(): void;
35
+ get<T>(key: string): T;
36
+ put<T>(key: string, value: T): void;
37
+ del(key: string): void;
38
+ }
39
+
40
+ type UseKVStoreResult = InstanceType<typeof KVStore>;
41
+ declare function useKVStore(): UseKVStoreResult;
42
+
43
+ declare function useSink<TTxnParams>({ context, }: {
44
+ context: IndexerContext<TTxnParams>;
45
+ }): NonNullable<TTxnParams>;
46
+
47
+ export { type CassetteDataType, CassetteOptions, Indexer, type UseKVStoreResult, VcrConfig, deserialize, isCassetteAvailable, record, serialize, useKVStore, useSink };
package/dist/index.mjs ADDED
@@ -0,0 +1,36 @@
1
+ import { u as useIndexerContext } from './shared/indexer.28bd9576.mjs';
2
+ export { c as createIndexer, d as defineIndexer, a as deserialize, i as isCassetteAvailable, l as loadCassette, b as record, e as replay, r as run, s as serialize } from './shared/indexer.28bd9576.mjs';
3
+ export { D as DefaultSink, S as Sink, d as defaultSink } from './shared/indexer.93d6b2eb.mjs';
4
+ import 'node:fs/promises';
5
+ import 'node:path';
6
+ import 'consola';
7
+ import 'klona/full';
8
+ import 'node:assert';
9
+ import 'node:fs';
10
+ import '@apibara/protocol/testing';
11
+ import 'hookable';
12
+ import 'node:async_hooks';
13
+ import 'unctx';
14
+ import '@opentelemetry/api';
15
+
16
+ function defineIndexerPlugin(def) {
17
+ return def;
18
+ }
19
+
20
+ function useKVStore() {
21
+ const ctx = useIndexerContext();
22
+ if (!ctx?.kv)
23
+ throw new Error("KV Plugin is not available in context!");
24
+ return ctx.kv;
25
+ }
26
+
27
+ function useSink({
28
+ context
29
+ }) {
30
+ if (!context.sinkTransaction) {
31
+ throw new Error("Transaction context doesn't exist!");
32
+ }
33
+ return context.sinkTransaction;
34
+ }
35
+
36
+ export { defineIndexerPlugin, useIndexerContext, useKVStore, useSink };
@@ -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.js';
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,279 @@
1
+ import fs$1 from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import consola from 'consola';
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 { createHooks, createDebugger } from 'hookable';
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.93d6b2eb.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) {
49
+ await indexerAsyncContext.callAsync({}, async () => {
50
+ const context = useIndexerContext();
51
+ const sink = indexer.options.sink ?? defaultSink();
52
+ context.sink = sink;
53
+ await indexer.hooks.callHook("run:before");
54
+ const isFactoryMode = indexer.options.factory !== void 0;
55
+ const request = indexer.streamConfig.Request.make({
56
+ filter: isFactoryMode ? [indexer.options.filter, {}] : [indexer.options.filter],
57
+ finality: indexer.options.finality,
58
+ startingCursor: indexer.options.startingCursor
59
+ });
60
+ const options = {};
61
+ await indexer.hooks.callHook("connect:before", { request, options });
62
+ let mainFilter;
63
+ if (isFactoryMode) {
64
+ mainFilter = request.filter[1];
65
+ }
66
+ let stream = client.streamData(request, options)[Symbol.asyncIterator]();
67
+ await indexer.hooks.callHook("connect:after");
68
+ while (true) {
69
+ const { value: message, done } = await stream.next();
70
+ if (done) {
71
+ break;
72
+ }
73
+ await indexer.hooks.callHook("message", { message });
74
+ switch (message._tag) {
75
+ case "data": {
76
+ await tracer.startActiveSpan("message data", async (span) => {
77
+ const blocks = message.data.data;
78
+ const { cursor, endCursor, finality } = message.data;
79
+ await sink.transaction(
80
+ { cursor, endCursor, finality },
81
+ async (txn) => {
82
+ context.sinkTransaction = txn;
83
+ let block;
84
+ if (isFactoryMode) {
85
+ assert(indexer.options.factory !== void 0);
86
+ const [factoryBlock, mainBlock] = blocks;
87
+ block = mainBlock;
88
+ if (factoryBlock !== null) {
89
+ const { filter } = await indexer.options.factory({
90
+ block: factoryBlock,
91
+ context
92
+ });
93
+ if (filter) {
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)[Symbol.asyncIterator]();
108
+ const { value: message2 } = await stream.next();
109
+ assert(message2._tag === "data");
110
+ const [_factoryBlock, _block] = message2.data.data;
111
+ block = _block;
112
+ }
113
+ }
114
+ } else {
115
+ block = blocks[0];
116
+ }
117
+ if (block) {
118
+ await tracer.startActiveSpan("handler", async (span2) => {
119
+ await indexer.hooks.callHook("handler:before", {
120
+ block,
121
+ endCursor,
122
+ finality
123
+ });
124
+ try {
125
+ await indexer.options.transform({
126
+ block,
127
+ cursor,
128
+ endCursor,
129
+ finality,
130
+ context
131
+ });
132
+ await indexer.hooks.callHook("handler:after", {
133
+ block,
134
+ finality,
135
+ endCursor
136
+ });
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
+ }
148
+ );
149
+ await indexer.hooks.callHook("transaction:commit", {
150
+ finality,
151
+ endCursor
152
+ });
153
+ span.end();
154
+ });
155
+ break;
156
+ }
157
+ case "invalidate": {
158
+ await tracer.startActiveSpan("message invalidate", async (span) => {
159
+ await sink.invalidate(message.invalidate.cursor);
160
+ });
161
+ break;
162
+ }
163
+ default: {
164
+ consola.warn("unexpected message", message);
165
+ throw new Error("not implemented");
166
+ }
167
+ }
168
+ await indexer.hooks.callHook("run:after");
169
+ }
170
+ });
171
+ }
172
+
173
+ function deserialize(str) {
174
+ return JSON.parse(
175
+ str,
176
+ (_, value) => typeof value === "string" && value.match(/^\d+n$/) ? BigInt(value.slice(0, -1)) : value
177
+ );
178
+ }
179
+ function serialize(obj) {
180
+ return JSON.stringify(
181
+ obj,
182
+ (_, value) => typeof value === "bigint" ? `${value.toString()}n` : value,
183
+ " "
184
+ );
185
+ }
186
+ function isCassetteAvailable(vcrConfig, cassetteName) {
187
+ const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
188
+ return fs.existsSync(filePath);
189
+ }
190
+
191
+ async function record(vcrConfig, client, indexerArg, cassetteOptions) {
192
+ const indexer = klona(indexerArg);
193
+ const messages = [];
194
+ indexer.hooks.addHooks({
195
+ "connect:before"({ options, request }) {
196
+ request.startingCursor = cassetteOptions.startingCursor;
197
+ options.endingCursor = cassetteOptions.endingCursor;
198
+ },
199
+ message({ message }) {
200
+ messages.push(message);
201
+ },
202
+ async "run:after"() {
203
+ const output = {
204
+ filter: indexer.options.filter,
205
+ messages
206
+ };
207
+ const filePath = path.join(
208
+ vcrConfig.cassetteDir,
209
+ `${cassetteOptions.name}.json`
210
+ );
211
+ await fs$1.writeFile(filePath, serialize(output), { flag: "w" });
212
+ }
213
+ });
214
+ await run(client, indexer);
215
+ }
216
+
217
+ var __defProp = Object.defineProperty;
218
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
219
+ var __publicField = (obj, key, value) => {
220
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
221
+ return value;
222
+ };
223
+ const transactionHelper = (context) => {
224
+ return {
225
+ insert: (data) => {
226
+ context.buffer.push(...data);
227
+ }
228
+ };
229
+ };
230
+ class VcrSink extends Sink {
231
+ constructor() {
232
+ super(...arguments);
233
+ __publicField(this, "result", []);
234
+ }
235
+ write({ data, endCursor }) {
236
+ if (data.length === 0)
237
+ return;
238
+ this.result.push({ data, endCursor });
239
+ }
240
+ async transaction({ cursor, endCursor, finality }, cb) {
241
+ const context = {
242
+ buffer: []
243
+ };
244
+ const writer = transactionHelper(context);
245
+ await cb({ writer });
246
+ this.write({ data: context.buffer, endCursor });
247
+ }
248
+ async invalidate(cursor) {
249
+ throw new Error("Not implemented");
250
+ }
251
+ }
252
+ function vcr() {
253
+ return new VcrSink();
254
+ }
255
+
256
+ async function replay(vcrConfig, indexer, cassetteName) {
257
+ const client = loadCassette(vcrConfig, cassetteName);
258
+ const sink = vcr();
259
+ await run(client, indexer);
260
+ return {
261
+ outputs: sink.result
262
+ };
263
+ }
264
+ function loadCassette(vcrConfig, cassetteName) {
265
+ const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
266
+ const data = fs.readFileSync(filePath, "utf8");
267
+ const cassetteData = deserialize(data);
268
+ const { filter, messages } = cassetteData;
269
+ return new MockClient((request, options) => {
270
+ assert.deepStrictEqual(
271
+ request.filter,
272
+ filter,
273
+ "Request and Cassette filter mismatch"
274
+ );
275
+ return messages;
276
+ });
277
+ }
278
+
279
+ 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 };
@@ -0,0 +1,17 @@
1
+ import consola from 'consola';
2
+
3
+ class Sink {
4
+ }
5
+ class DefaultSink extends Sink {
6
+ async transaction({ cursor, endCursor, finality }, cb) {
7
+ await cb({});
8
+ }
9
+ async invalidate(cursor) {
10
+ consola.info(`Invalidating cursor ${cursor?.orderKey}`);
11
+ }
12
+ }
13
+ function defaultSink() {
14
+ return new DefaultSink();
15
+ }
16
+
17
+ export { DefaultSink as D, Sink as S, defaultSink as d };