@apibara/indexer 2.0.0-beta.3 → 2.0.0-beta.31
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 +270 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +259 -0
- package/dist/internal/testing.cjs +109 -0
- package/dist/internal/testing.d.cts +40 -0
- package/dist/internal/testing.d.mts +40 -0
- package/dist/internal/testing.d.ts +40 -0
- package/dist/internal/testing.mjs +104 -0
- package/dist/plugins/index.cjs +43 -0
- package/dist/plugins/index.d.cts +18 -0
- package/dist/plugins/index.d.mts +18 -0
- package/dist/plugins/index.d.ts +18 -0
- package/dist/plugins/index.mjs +38 -0
- package/dist/shared/indexer.077335f3.cjs +15 -0
- package/dist/shared/indexer.2416906c.cjs +29 -0
- package/dist/shared/indexer.601ceab0.cjs +7 -0
- package/dist/shared/indexer.8939ecc8.d.cts +91 -0
- package/dist/shared/indexer.8939ecc8.d.mts +91 -0
- package/dist/shared/indexer.8939ecc8.d.ts +91 -0
- package/dist/shared/indexer.9b21ddd2.mjs +5 -0
- package/dist/shared/indexer.a55ad619.mjs +12 -0
- package/dist/shared/indexer.ff25c953.mjs +26 -0
- package/dist/testing/index.cjs +58 -0
- package/dist/testing/index.d.cts +12 -0
- package/dist/testing/index.d.mts +12 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.mjs +52 -0
- package/dist/vcr/index.cjs +92 -0
- package/dist/vcr/index.d.cts +27 -0
- package/dist/vcr/index.d.mts +27 -0
- package/dist/vcr/index.d.ts +27 -0
- package/dist/vcr/index.mjs +78 -0
- package/package.json +31 -41
- package/src/compose.test.ts +76 -0
- package/src/compose.ts +71 -0
- package/src/context.ts +14 -8
- package/src/index.ts +0 -5
- package/src/indexer.test.ts +109 -186
- package/src/indexer.ts +244 -144
- package/src/internal/testing.ts +135 -0
- package/src/plugins/config.ts +4 -4
- package/src/plugins/index.ts +8 -1
- package/src/plugins/logger.ts +30 -0
- package/src/plugins/persistence.ts +24 -187
- package/src/testing/index.ts +50 -3
- package/src/vcr/record.ts +6 -4
- package/src/vcr/replay.ts +8 -18
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useKVStore.ts +0 -12
- package/src/hooks/useSink.ts +0 -13
- package/src/plugins/kv.test.ts +0 -120
- package/src/plugins/kv.ts +0 -132
- package/src/plugins/persistence.test.ts +0 -151
- package/src/sink.ts +0 -36
- package/src/sinks/csv.test.ts +0 -65
- package/src/sinks/csv.ts +0 -159
- package/src/sinks/drizzle/Int8Range.ts +0 -52
- package/src/sinks/drizzle/delete.ts +0 -42
- package/src/sinks/drizzle/drizzle.test.ts +0 -239
- package/src/sinks/drizzle/drizzle.ts +0 -115
- package/src/sinks/drizzle/index.ts +0 -6
- package/src/sinks/drizzle/insert.ts +0 -39
- package/src/sinks/drizzle/select.ts +0 -44
- package/src/sinks/drizzle/transaction.ts +0 -49
- package/src/sinks/drizzle/update.ts +0 -47
- package/src/sinks/drizzle/utils.ts +0 -36
- package/src/sinks/sqlite.test.ts +0 -99
- package/src/sinks/sqlite.ts +0 -170
- package/src/testing/helper.ts +0 -13
- package/src/testing/indexer.ts +0 -35
- package/src/testing/setup.ts +0 -59
- package/src/testing/vcr.ts +0 -54
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
import { u as useIndexerContext } from './indexer.a55ad619.mjs';
|
|
3
|
+
import { d as defineIndexerPlugin } from './indexer.9b21ddd2.mjs';
|
|
4
|
+
|
|
5
|
+
function logger({
|
|
6
|
+
logger: logger2
|
|
7
|
+
} = {}) {
|
|
8
|
+
return defineIndexerPlugin((indexer) => {
|
|
9
|
+
indexer.hooks.hook("run:before", () => {
|
|
10
|
+
const ctx = useIndexerContext();
|
|
11
|
+
if (logger2) {
|
|
12
|
+
ctx.logger = consola.create({ reporters: [logger2] });
|
|
13
|
+
} else {
|
|
14
|
+
ctx.logger = consola.create({});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function useLogger() {
|
|
20
|
+
const ctx = useIndexerContext();
|
|
21
|
+
if (!ctx?.logger)
|
|
22
|
+
throw new Error("Logger plugin is not available in context");
|
|
23
|
+
return ctx.logger;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { logger as l, useLogger as u };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const protocol = require('@apibara/protocol');
|
|
4
|
+
const ci = require('ci-info');
|
|
5
|
+
const index = require('../index.cjs');
|
|
6
|
+
const logger = require('../shared/indexer.2416906c.cjs');
|
|
7
|
+
const vcr_index = require('../vcr/index.cjs');
|
|
8
|
+
require('consola');
|
|
9
|
+
require('hookable');
|
|
10
|
+
require('node:assert');
|
|
11
|
+
require('../shared/indexer.077335f3.cjs');
|
|
12
|
+
require('node:async_hooks');
|
|
13
|
+
require('unctx');
|
|
14
|
+
require('@opentelemetry/api');
|
|
15
|
+
require('../shared/indexer.601ceab0.cjs');
|
|
16
|
+
require('node:fs/promises');
|
|
17
|
+
require('node:path');
|
|
18
|
+
require('node:fs');
|
|
19
|
+
require('@apibara/protocol/testing');
|
|
20
|
+
|
|
21
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
22
|
+
|
|
23
|
+
const ci__default = /*#__PURE__*/_interopDefaultCompat(ci);
|
|
24
|
+
|
|
25
|
+
function createVcr() {
|
|
26
|
+
return {
|
|
27
|
+
async run(cassetteName, indexerConfig, range) {
|
|
28
|
+
const vcrConfig = {
|
|
29
|
+
cassetteDir: "cassettes"
|
|
30
|
+
};
|
|
31
|
+
const cassetteOptions = {
|
|
32
|
+
name: cassetteName,
|
|
33
|
+
startingCursor: {
|
|
34
|
+
orderKey: range.fromBlock
|
|
35
|
+
},
|
|
36
|
+
endingCursor: {
|
|
37
|
+
orderKey: range.toBlock
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
indexerConfig.plugins = [logger.logger(), ...indexerConfig.plugins ?? []];
|
|
41
|
+
const indexer = index.createIndexer(indexerConfig);
|
|
42
|
+
if (!vcr_index.isCassetteAvailable(vcrConfig, cassetteName)) {
|
|
43
|
+
if (ci__default.isCI) {
|
|
44
|
+
throw new Error("Cannot record cassette in CI");
|
|
45
|
+
}
|
|
46
|
+
const client = protocol.createClient(
|
|
47
|
+
indexer.streamConfig,
|
|
48
|
+
indexer.options.streamUrl
|
|
49
|
+
);
|
|
50
|
+
await vcr_index.record(vcrConfig, client, indexer, cassetteOptions);
|
|
51
|
+
} else {
|
|
52
|
+
await vcr_index.replay(vcrConfig, indexer, cassetteName);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
exports.createVcr = createVcr;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { I as IndexerWithStreamConfig } from '../shared/indexer.8939ecc8.cjs';
|
|
2
|
+
import '@apibara/protocol';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
declare function createVcr(): {
|
|
6
|
+
run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
|
|
7
|
+
fromBlock: bigint;
|
|
8
|
+
toBlock: bigint;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { createVcr };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { I as IndexerWithStreamConfig } from '../shared/indexer.8939ecc8.mjs';
|
|
2
|
+
import '@apibara/protocol';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
declare function createVcr(): {
|
|
6
|
+
run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
|
|
7
|
+
fromBlock: bigint;
|
|
8
|
+
toBlock: bigint;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { createVcr };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { I as IndexerWithStreamConfig } from '../shared/indexer.8939ecc8.js';
|
|
2
|
+
import '@apibara/protocol';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
declare function createVcr(): {
|
|
6
|
+
run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
|
|
7
|
+
fromBlock: bigint;
|
|
8
|
+
toBlock: bigint;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { createVcr };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createClient } from '@apibara/protocol';
|
|
2
|
+
import ci from 'ci-info';
|
|
3
|
+
import { createIndexer } from '../index.mjs';
|
|
4
|
+
import { l as logger } from '../shared/indexer.ff25c953.mjs';
|
|
5
|
+
import { isCassetteAvailable, record, replay } from '../vcr/index.mjs';
|
|
6
|
+
import 'consola';
|
|
7
|
+
import 'hookable';
|
|
8
|
+
import 'node:assert';
|
|
9
|
+
import '../shared/indexer.a55ad619.mjs';
|
|
10
|
+
import 'node:async_hooks';
|
|
11
|
+
import 'unctx';
|
|
12
|
+
import '@opentelemetry/api';
|
|
13
|
+
import '../shared/indexer.9b21ddd2.mjs';
|
|
14
|
+
import 'node:fs/promises';
|
|
15
|
+
import 'node:path';
|
|
16
|
+
import 'node:fs';
|
|
17
|
+
import '@apibara/protocol/testing';
|
|
18
|
+
|
|
19
|
+
function createVcr() {
|
|
20
|
+
return {
|
|
21
|
+
async run(cassetteName, indexerConfig, range) {
|
|
22
|
+
const vcrConfig = {
|
|
23
|
+
cassetteDir: "cassettes"
|
|
24
|
+
};
|
|
25
|
+
const cassetteOptions = {
|
|
26
|
+
name: cassetteName,
|
|
27
|
+
startingCursor: {
|
|
28
|
+
orderKey: range.fromBlock
|
|
29
|
+
},
|
|
30
|
+
endingCursor: {
|
|
31
|
+
orderKey: range.toBlock
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
indexerConfig.plugins = [logger(), ...indexerConfig.plugins ?? []];
|
|
35
|
+
const indexer = createIndexer(indexerConfig);
|
|
36
|
+
if (!isCassetteAvailable(vcrConfig, cassetteName)) {
|
|
37
|
+
if (ci.isCI) {
|
|
38
|
+
throw new Error("Cannot record cassette in CI");
|
|
39
|
+
}
|
|
40
|
+
const client = createClient(
|
|
41
|
+
indexer.streamConfig,
|
|
42
|
+
indexer.options.streamUrl
|
|
43
|
+
);
|
|
44
|
+
await record(vcrConfig, client, indexer, cassetteOptions);
|
|
45
|
+
} else {
|
|
46
|
+
await replay(vcrConfig, indexer, cassetteName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { createVcr };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs$1 = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const index = require('../index.cjs');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const testing = require('@apibara/protocol/testing');
|
|
9
|
+
require('@apibara/protocol');
|
|
10
|
+
require('consola');
|
|
11
|
+
require('hookable');
|
|
12
|
+
require('../shared/indexer.077335f3.cjs');
|
|
13
|
+
require('node:async_hooks');
|
|
14
|
+
require('unctx');
|
|
15
|
+
require('@opentelemetry/api');
|
|
16
|
+
|
|
17
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
18
|
+
|
|
19
|
+
const fs__default$1 = /*#__PURE__*/_interopDefaultCompat(fs$1);
|
|
20
|
+
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
21
|
+
const assert__default = /*#__PURE__*/_interopDefaultCompat(assert);
|
|
22
|
+
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
23
|
+
|
|
24
|
+
function deserialize(str) {
|
|
25
|
+
return JSON.parse(
|
|
26
|
+
str,
|
|
27
|
+
(_, value) => typeof value === "string" && value.match(/^\d+n$/) ? BigInt(value.slice(0, -1)) : value
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function serialize(obj) {
|
|
31
|
+
return JSON.stringify(
|
|
32
|
+
obj,
|
|
33
|
+
(_, value) => typeof value === "bigint" ? `${value.toString()}n` : value,
|
|
34
|
+
" "
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function isCassetteAvailable(vcrConfig, cassetteName) {
|
|
38
|
+
const filePath = path__default.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
|
|
39
|
+
return fs__default.existsSync(filePath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function record(vcrConfig, client, indexer, cassetteOptions) {
|
|
43
|
+
const messages = [];
|
|
44
|
+
indexer.hooks.addHooks({
|
|
45
|
+
"connect:before"({ options, request }) {
|
|
46
|
+
request.startingCursor = cassetteOptions.startingCursor;
|
|
47
|
+
options.endingCursor = cassetteOptions.endingCursor;
|
|
48
|
+
},
|
|
49
|
+
message({ message }) {
|
|
50
|
+
messages.push(message);
|
|
51
|
+
},
|
|
52
|
+
async "run:after"() {
|
|
53
|
+
const output = {
|
|
54
|
+
filter: indexer.options.filter,
|
|
55
|
+
messages
|
|
56
|
+
};
|
|
57
|
+
await fs__default$1.mkdir(vcrConfig.cassetteDir, { recursive: true });
|
|
58
|
+
const filePath = path__default.join(
|
|
59
|
+
vcrConfig.cassetteDir,
|
|
60
|
+
`${cassetteOptions.name}.json`
|
|
61
|
+
);
|
|
62
|
+
await fs__default$1.writeFile(filePath, serialize(output), { flag: "w" });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
await index.run(client, indexer);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function replay(vcrConfig, indexer, cassetteName) {
|
|
69
|
+
const client = loadCassette(vcrConfig, cassetteName);
|
|
70
|
+
await index.run(client, indexer);
|
|
71
|
+
}
|
|
72
|
+
function loadCassette(vcrConfig, cassetteName) {
|
|
73
|
+
const filePath = path__default.join(vcrConfig.cassetteDir, `${cassetteName}.json`);
|
|
74
|
+
const data = fs__default.readFileSync(filePath, "utf8");
|
|
75
|
+
const cassetteData = deserialize(data);
|
|
76
|
+
const { filter, messages } = cassetteData;
|
|
77
|
+
return new testing.MockClient((request, options) => {
|
|
78
|
+
assert__default.deepStrictEqual(
|
|
79
|
+
request.filter,
|
|
80
|
+
[filter],
|
|
81
|
+
"Indexer and cassette filter mismatch. Hint: delete the cassette and run again."
|
|
82
|
+
);
|
|
83
|
+
return messages;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
exports.deserialize = deserialize;
|
|
88
|
+
exports.isCassetteAvailable = isCassetteAvailable;
|
|
89
|
+
exports.loadCassette = loadCassette;
|
|
90
|
+
exports.record = record;
|
|
91
|
+
exports.replay = replay;
|
|
92
|
+
exports.serialize = serialize;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { c as Indexer } from '../shared/indexer.8939ecc8.cjs';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare function deserialize(str: string): any;
|
|
15
|
+
declare function serialize(obj: Record<string, unknown>): string;
|
|
16
|
+
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
17
|
+
|
|
18
|
+
type CassetteDataType<TFilter, TBlock> = {
|
|
19
|
+
filter: TFilter;
|
|
20
|
+
messages: StreamDataResponse<TBlock>[];
|
|
21
|
+
};
|
|
22
|
+
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock>, cassetteOptions: CassetteOptions): Promise<void>;
|
|
23
|
+
|
|
24
|
+
declare function replay<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock>, cassetteName: string): Promise<void>;
|
|
25
|
+
declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
|
|
26
|
+
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { c as Indexer } from '../shared/indexer.8939ecc8.mjs';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare function deserialize(str: string): any;
|
|
15
|
+
declare function serialize(obj: Record<string, unknown>): string;
|
|
16
|
+
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
17
|
+
|
|
18
|
+
type CassetteDataType<TFilter, TBlock> = {
|
|
19
|
+
filter: TFilter;
|
|
20
|
+
messages: StreamDataResponse<TBlock>[];
|
|
21
|
+
};
|
|
22
|
+
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock>, cassetteOptions: CassetteOptions): Promise<void>;
|
|
23
|
+
|
|
24
|
+
declare function replay<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock>, cassetteName: string): Promise<void>;
|
|
25
|
+
declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
|
|
26
|
+
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { c as Indexer } from '../shared/indexer.8939ecc8.js';
|
|
3
|
+
import 'hookable';
|
|
4
|
+
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare function deserialize(str: string): any;
|
|
15
|
+
declare function serialize(obj: Record<string, unknown>): string;
|
|
16
|
+
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
17
|
+
|
|
18
|
+
type CassetteDataType<TFilter, TBlock> = {
|
|
19
|
+
filter: TFilter;
|
|
20
|
+
messages: StreamDataResponse<TBlock>[];
|
|
21
|
+
};
|
|
22
|
+
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>, indexer: Indexer<TFilter, TBlock>, cassetteOptions: CassetteOptions): Promise<void>;
|
|
23
|
+
|
|
24
|
+
declare function replay<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, indexer: Indexer<TFilter, TBlock>, cassetteName: string): Promise<void>;
|
|
25
|
+
declare function loadCassette<TFilter, TBlock>(vcrConfig: VcrConfig, cassetteName: string): Client<TFilter, TBlock>;
|
|
26
|
+
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
|
@@ -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.
|
|
3
|
+
"version": "2.0.0-beta.31",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
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,51 @@
|
|
|
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/testing.d.ts",
|
|
39
|
+
"import": "./dist/internal/testing.mjs",
|
|
40
|
+
"require": "./dist/internal/testing.cjs",
|
|
41
|
+
"default": "./dist/internal/testing.mjs"
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
|
-
"publishConfig": {},
|
|
40
44
|
"scripts": {
|
|
41
45
|
"build": "unbuild",
|
|
42
|
-
"lint": "biome check .",
|
|
43
46
|
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "biome check .",
|
|
44
48
|
"lint:fix": "pnpm lint --write",
|
|
45
49
|
"test": "vitest",
|
|
46
|
-
"test:ci": "vitest run"
|
|
47
|
-
"format": "biome format . --write"
|
|
50
|
+
"test:ci": "vitest run"
|
|
48
51
|
},
|
|
49
52
|
"devDependencies": {
|
|
53
|
+
"@electric-sql/pglite": "^0.2.14",
|
|
50
54
|
"@types/better-sqlite3": "^7.6.11",
|
|
51
55
|
"@types/node": "^20.14.0",
|
|
52
56
|
"@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
57
|
"unbuild": "^2.0.0",
|
|
59
58
|
"vitest": "^1.6.0"
|
|
60
59
|
},
|
|
61
60
|
"dependencies": {
|
|
62
|
-
"@apibara/protocol": "2.0.0-beta.
|
|
61
|
+
"@apibara/protocol": "2.0.0-beta.31",
|
|
63
62
|
"@opentelemetry/api": "^1.9.0",
|
|
63
|
+
"ci-info": "^4.1.0",
|
|
64
64
|
"consola": "^3.2.3",
|
|
65
65
|
"hookable": "^5.5.3",
|
|
66
66
|
"klona": "^2.0.6",
|
|
@@ -68,16 +68,6 @@
|
|
|
68
68
|
"unctx": "^2.3.1"
|
|
69
69
|
},
|
|
70
70
|
"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
71
|
"vitest": "^1.6.0"
|
|
76
|
-
}
|
|
77
|
-
"files": [
|
|
78
|
-
"dist",
|
|
79
|
-
"src",
|
|
80
|
-
"README.md"
|
|
81
|
-
],
|
|
82
|
-
"types": "./dist/index.d.ts"
|
|
72
|
+
}
|
|
83
73
|
}
|
|
@@ -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
|
+
});
|