@apibara/indexer 2.0.0-beta.9 → 2.1.0-beta.2
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 +271 -39
- package/dist/index.d.cts +1 -16
- package/dist/index.d.mts +1 -16
- package/dist/index.d.ts +1 -16
- package/dist/index.mjs +262 -25
- package/dist/internal/index.cjs +10 -0
- package/dist/internal/index.d.cts +3 -0
- package/dist/internal/index.d.mts +3 -0
- package/dist/internal/index.d.ts +3 -0
- package/dist/internal/index.mjs +8 -0
- package/dist/internal/plugins.cjs +38 -0
- package/dist/internal/plugins.d.cts +13 -0
- package/dist/internal/plugins.d.mts +13 -0
- package/dist/internal/plugins.d.ts +13 -0
- package/dist/internal/plugins.mjs +34 -0
- package/dist/internal/testing.cjs +118 -0
- package/dist/internal/testing.d.cts +42 -0
- package/dist/internal/testing.d.mts +42 -0
- package/dist/internal/testing.d.ts +42 -0
- package/dist/internal/testing.mjs +113 -0
- package/dist/plugins/index.cjs +39 -3
- package/dist/plugins/index.d.cts +16 -2
- package/dist/plugins/index.d.mts +16 -2
- package/dist/plugins/index.d.ts +16 -2
- package/dist/plugins/index.mjs +36 -3
- 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.9b21ddd2.mjs +5 -0
- package/dist/shared/indexer.a55ad619.mjs +12 -0
- package/dist/shared/indexer.fedcd831.d.cts +100 -0
- package/dist/shared/indexer.fedcd831.d.mts +100 -0
- package/dist/shared/indexer.fedcd831.d.ts +100 -0
- package/dist/shared/indexer.ff25c953.mjs +26 -0
- package/dist/testing/index.cjs +52 -50
- package/dist/testing/index.d.cts +8 -36
- package/dist/testing/index.d.mts +8 -36
- package/dist/testing/index.d.ts +8 -36
- package/dist/testing/index.mjs +47 -47
- package/dist/vcr/index.cjs +84 -17
- package/dist/vcr/index.d.cts +16 -7
- package/dist/vcr/index.d.mts +16 -7
- package/dist/vcr/index.d.ts +16 -7
- package/dist/vcr/index.mjs +75 -11
- package/package.json +22 -42
- 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 +125 -186
- package/src/indexer.ts +278 -151
- package/src/internal/index.ts +6 -0
- package/src/internal/plugins.ts +1 -0
- package/src/internal/testing.ts +148 -0
- package/src/plugins/config.ts +4 -4
- package/src/plugins/context.ts +40 -0
- 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 +58 -3
- package/src/vcr/record.ts +5 -3
- package/src/vcr/replay.ts +8 -18
- package/dist/plugins/kv.cjs +0 -131
- package/dist/plugins/kv.d.cts +0 -32
- package/dist/plugins/kv.d.mts +0 -32
- package/dist/plugins/kv.d.ts +0 -32
- package/dist/plugins/kv.mjs +0 -124
- package/dist/plugins/persistence.cjs +0 -182
- package/dist/plugins/persistence.d.cts +0 -50
- package/dist/plugins/persistence.d.mts +0 -50
- package/dist/plugins/persistence.d.ts +0 -50
- package/dist/plugins/persistence.mjs +0 -179
- package/dist/shared/indexer.2c23c9cd.mjs +0 -35
- package/dist/shared/indexer.318d3617.cjs +0 -47
- package/dist/shared/indexer.36530330.mjs +0 -249
- package/dist/shared/indexer.500fd281.d.cts +0 -23
- package/dist/shared/indexer.541d43eb.cjs +0 -266
- package/dist/shared/indexer.93d6b2eb.mjs +0 -17
- package/dist/shared/indexer.a8b7ab1f.cjs +0 -25
- package/dist/shared/indexer.b9c8f0d8.d.cts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.mts +0 -19
- package/dist/shared/indexer.b9c8f0d8.d.ts +0 -19
- package/dist/shared/indexer.c7ed6b83.d.cts +0 -82
- package/dist/shared/indexer.e1856641.d.mts +0 -23
- package/dist/shared/indexer.e4f2430f.d.ts +0 -23
- package/dist/shared/indexer.e8bd138d.d.mts +0 -82
- package/dist/shared/indexer.f761abcd.d.ts +0 -82
- package/dist/sinks/csv.cjs +0 -85
- package/dist/sinks/csv.d.cts +0 -66
- package/dist/sinks/csv.d.mts +0 -66
- package/dist/sinks/csv.d.ts +0 -66
- package/dist/sinks/csv.mjs +0 -78
- package/dist/sinks/drizzle/index.cjs +0 -212
- package/dist/sinks/drizzle/index.d.cts +0 -153
- package/dist/sinks/drizzle/index.d.mts +0 -153
- package/dist/sinks/drizzle/index.d.ts +0 -153
- package/dist/sinks/drizzle/index.mjs +0 -198
- package/dist/sinks/sqlite.cjs +0 -90
- package/dist/sinks/sqlite.d.cts +0 -71
- package/dist/sinks/sqlite.d.mts +0 -71
- package/dist/sinks/sqlite.d.ts +0 -71
- package/dist/sinks/sqlite.mjs +0 -87
- 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 -42
- 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 -99
- 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
package/dist/testing/index.mjs
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { createClient } from '@apibara/protocol';
|
|
2
|
+
import ci from 'ci-info';
|
|
3
|
+
import { createIndexer } from '../index.mjs';
|
|
4
|
+
import { internalContext } from '../internal/plugins.mjs';
|
|
5
|
+
import { l as logger } from '../shared/indexer.ff25c953.mjs';
|
|
6
|
+
import { isCassetteAvailable, record, replay } from '../vcr/index.mjs';
|
|
7
|
+
import 'consola';
|
|
8
|
+
import 'hookable';
|
|
9
|
+
import 'node:assert';
|
|
10
|
+
import '../shared/indexer.a55ad619.mjs';
|
|
5
11
|
import 'node:async_hooks';
|
|
6
12
|
import 'unctx';
|
|
7
13
|
import '@opentelemetry/api';
|
|
8
|
-
import '
|
|
9
|
-
import 'node:path';
|
|
14
|
+
import '../shared/indexer.9b21ddd2.mjs';
|
|
10
15
|
import 'node:fs/promises';
|
|
11
|
-
import '
|
|
12
|
-
import '
|
|
13
|
-
import 'hookable';
|
|
14
|
-
import 'node:assert';
|
|
15
|
-
import '../shared/indexer.93d6b2eb.mjs';
|
|
16
|
+
import 'node:path';
|
|
17
|
+
import 'node:fs';
|
|
16
18
|
import '@apibara/protocol/testing';
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
function createVcr() {
|
|
21
|
+
return {
|
|
22
|
+
async run(cassetteName, indexerConfig, range) {
|
|
23
|
+
const vcrConfig = {
|
|
24
|
+
cassetteDir: "cassettes"
|
|
25
|
+
};
|
|
26
|
+
const cassetteOptions = {
|
|
27
|
+
name: cassetteName,
|
|
28
|
+
startingCursor: {
|
|
29
|
+
orderKey: range.fromBlock
|
|
30
|
+
},
|
|
31
|
+
endingCursor: {
|
|
32
|
+
orderKey: range.toBlock
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
indexerConfig.plugins = [
|
|
36
|
+
internalContext({
|
|
37
|
+
indexerName: cassetteName,
|
|
38
|
+
availableIndexers: [cassetteName]
|
|
39
|
+
}),
|
|
40
|
+
logger(),
|
|
41
|
+
...indexerConfig.plugins ?? []
|
|
42
|
+
];
|
|
43
|
+
const indexer = createIndexer(indexerConfig);
|
|
39
44
|
if (!isCassetteAvailable(vcrConfig, cassetteName)) {
|
|
45
|
+
if (ci.isCI) {
|
|
46
|
+
throw new Error("Cannot record cassette in CI");
|
|
47
|
+
}
|
|
48
|
+
const client = createClient(
|
|
49
|
+
indexer.streamConfig,
|
|
50
|
+
indexer.options.streamUrl
|
|
51
|
+
);
|
|
40
52
|
await record(vcrConfig, client, indexer, cassetteOptions);
|
|
53
|
+
} else {
|
|
54
|
+
await replay(vcrConfig, indexer, cassetteName);
|
|
41
55
|
}
|
|
42
|
-
return await replay(vcrConfig, indexer, cassetteName);
|
|
43
56
|
}
|
|
44
57
|
};
|
|
45
|
-
await callback(context);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function generateMockMessages(count = 10) {
|
|
49
|
-
return [...Array(count)].map((_, i) => ({
|
|
50
|
-
_tag: "data",
|
|
51
|
-
data: {
|
|
52
|
-
cursor: { orderKey: BigInt(5e6 - 1) },
|
|
53
|
-
finality: "accepted",
|
|
54
|
-
data: [{ data: `${5e6 + i}` }],
|
|
55
|
-
endCursor: { orderKey: BigInt(5e6 + i) }
|
|
56
|
-
}
|
|
57
|
-
}));
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export {
|
|
60
|
+
export { createVcr };
|
package/dist/vcr/index.cjs
CHANGED
|
@@ -1,25 +1,92 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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');
|
|
5
13
|
require('node:async_hooks');
|
|
6
14
|
require('unctx');
|
|
7
15
|
require('@opentelemetry/api');
|
|
8
|
-
require('node:fs');
|
|
9
|
-
require('node:path');
|
|
10
|
-
require('node:fs/promises');
|
|
11
|
-
require('klona/full');
|
|
12
|
-
require('consola');
|
|
13
|
-
require('hookable');
|
|
14
|
-
require('node:assert');
|
|
15
|
-
require('../shared/indexer.a8b7ab1f.cjs');
|
|
16
|
-
require('@apibara/protocol/testing');
|
|
17
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
|
+
}
|
|
18
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
|
+
}
|
|
19
86
|
|
|
20
|
-
exports.deserialize =
|
|
21
|
-
exports.isCassetteAvailable =
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
87
|
+
exports.deserialize = deserialize;
|
|
88
|
+
exports.isCassetteAvailable = isCassetteAvailable;
|
|
89
|
+
exports.loadCassette = loadCassette;
|
|
90
|
+
exports.record = record;
|
|
91
|
+
exports.replay = replay;
|
|
92
|
+
exports.serialize = serialize;
|
package/dist/vcr/index.d.cts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { StreamDataResponse, Client } from '@apibara/protocol';
|
|
4
|
-
import { f as Indexer } from '../shared/indexer.c7ed6b83.cjs';
|
|
5
|
-
import '../shared/indexer.b9c8f0d8.cjs';
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { a as Indexer } from '../shared/indexer.fedcd831.cjs';
|
|
6
3
|
import 'hookable';
|
|
7
4
|
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
8
14
|
declare function deserialize(str: string): any;
|
|
9
15
|
declare function serialize(obj: Record<string, unknown>): string;
|
|
10
16
|
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
@@ -13,6 +19,9 @@ type CassetteDataType<TFilter, TBlock> = {
|
|
|
13
19
|
filter: TFilter;
|
|
14
20
|
messages: StreamDataResponse<TBlock>[];
|
|
15
21
|
};
|
|
16
|
-
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>,
|
|
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>;
|
|
17
26
|
|
|
18
|
-
export { type CassetteDataType, CassetteOptions, VcrConfig, deserialize, isCassetteAvailable, record, serialize };
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
package/dist/vcr/index.d.mts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { StreamDataResponse, Client } from '@apibara/protocol';
|
|
4
|
-
import { f as Indexer } from '../shared/indexer.e8bd138d.mjs';
|
|
5
|
-
import '../shared/indexer.b9c8f0d8.mjs';
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { a as Indexer } from '../shared/indexer.fedcd831.mjs';
|
|
6
3
|
import 'hookable';
|
|
7
4
|
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
8
14
|
declare function deserialize(str: string): any;
|
|
9
15
|
declare function serialize(obj: Record<string, unknown>): string;
|
|
10
16
|
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
@@ -13,6 +19,9 @@ type CassetteDataType<TFilter, TBlock> = {
|
|
|
13
19
|
filter: TFilter;
|
|
14
20
|
messages: StreamDataResponse<TBlock>[];
|
|
15
21
|
};
|
|
16
|
-
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>,
|
|
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>;
|
|
17
26
|
|
|
18
|
-
export { type CassetteDataType, CassetteOptions, VcrConfig, deserialize, isCassetteAvailable, record, serialize };
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
package/dist/vcr/index.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { StreamDataResponse, Client } from '@apibara/protocol';
|
|
4
|
-
import { f as Indexer } from '../shared/indexer.f761abcd.js';
|
|
5
|
-
import '../shared/indexer.b9c8f0d8.js';
|
|
1
|
+
import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
|
|
2
|
+
import { a as Indexer } from '../shared/indexer.fedcd831.js';
|
|
6
3
|
import 'hookable';
|
|
7
4
|
|
|
5
|
+
type VcrConfig = {
|
|
6
|
+
cassetteDir: string;
|
|
7
|
+
};
|
|
8
|
+
type CassetteOptions = {
|
|
9
|
+
name: string;
|
|
10
|
+
startingCursor: Cursor;
|
|
11
|
+
endingCursor: Cursor;
|
|
12
|
+
};
|
|
13
|
+
|
|
8
14
|
declare function deserialize(str: string): any;
|
|
9
15
|
declare function serialize(obj: Record<string, unknown>): string;
|
|
10
16
|
declare function isCassetteAvailable(vcrConfig: VcrConfig, cassetteName: string): boolean;
|
|
@@ -13,6 +19,9 @@ type CassetteDataType<TFilter, TBlock> = {
|
|
|
13
19
|
filter: TFilter;
|
|
14
20
|
messages: StreamDataResponse<TBlock>[];
|
|
15
21
|
};
|
|
16
|
-
declare function record<TFilter, TBlock, TTxnParams>(vcrConfig: VcrConfig, client: Client<TFilter, TBlock>,
|
|
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>;
|
|
17
26
|
|
|
18
|
-
export { type CassetteDataType, CassetteOptions, VcrConfig, deserialize, isCassetteAvailable, record, serialize };
|
|
27
|
+
export { type CassetteDataType, type CassetteOptions, type VcrConfig, deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
|
package/dist/vcr/index.mjs
CHANGED
|
@@ -1,14 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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';
|
|
3
11
|
import 'node:async_hooks';
|
|
4
12
|
import 'unctx';
|
|
5
13
|
import '@opentelemetry/api';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apibara/indexer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -16,24 +16,6 @@
|
|
|
16
16
|
"require": "./dist/index.cjs",
|
|
17
17
|
"default": "./dist/index.mjs"
|
|
18
18
|
},
|
|
19
|
-
"./sinks/sqlite": {
|
|
20
|
-
"types": "./dist/sinks/sqlite.d.ts",
|
|
21
|
-
"import": "./dist/sinks/sqlite.mjs",
|
|
22
|
-
"require": "./dist/sinks/sqlite.cjs",
|
|
23
|
-
"default": "./dist/sinks/sqlite.mjs"
|
|
24
|
-
},
|
|
25
|
-
"./sinks/csv": {
|
|
26
|
-
"types": "./dist/sinks/csv.d.ts",
|
|
27
|
-
"import": "./dist/sinks/csv.mjs",
|
|
28
|
-
"require": "./dist/sinks/csv.cjs",
|
|
29
|
-
"default": "./dist/sinks/csv.mjs"
|
|
30
|
-
},
|
|
31
|
-
"./sinks/drizzle": {
|
|
32
|
-
"types": "./dist/sinks/drizzle/index.d.ts",
|
|
33
|
-
"import": "./dist/sinks/drizzle/index.mjs",
|
|
34
|
-
"require": "./dist/sinks/drizzle/index.cjs",
|
|
35
|
-
"default": "./dist/sinks/drizzle/index.mjs"
|
|
36
|
-
},
|
|
37
19
|
"./testing": {
|
|
38
20
|
"types": "./dist/testing/index.d.ts",
|
|
39
21
|
"import": "./dist/testing/index.mjs",
|
|
@@ -52,43 +34,45 @@
|
|
|
52
34
|
"require": "./dist/plugins/index.cjs",
|
|
53
35
|
"default": "./dist/plugins/index.mjs"
|
|
54
36
|
},
|
|
55
|
-
"./
|
|
56
|
-
"types": "./dist/
|
|
57
|
-
"import": "./dist/
|
|
58
|
-
"require": "./dist/
|
|
59
|
-
"default": "./dist/
|
|
37
|
+
"./internal": {
|
|
38
|
+
"types": "./dist/internal/index.d.ts",
|
|
39
|
+
"import": "./dist/internal/index.mjs",
|
|
40
|
+
"require": "./dist/internal/index.cjs",
|
|
41
|
+
"default": "./dist/internal/index.mjs"
|
|
60
42
|
},
|
|
61
|
-
"./
|
|
62
|
-
"types": "./dist/
|
|
63
|
-
"import": "./dist/
|
|
64
|
-
"require": "./dist/
|
|
65
|
-
"default": "./dist/
|
|
43
|
+
"./internal/testing": {
|
|
44
|
+
"types": "./dist/internal/testing.d.ts",
|
|
45
|
+
"import": "./dist/internal/testing.mjs",
|
|
46
|
+
"require": "./dist/internal/testing.cjs",
|
|
47
|
+
"default": "./dist/internal/testing.mjs"
|
|
48
|
+
},
|
|
49
|
+
"./internal/plugins": {
|
|
50
|
+
"types": "./dist/internal/plugins.d.ts",
|
|
51
|
+
"import": "./dist/internal/plugins.mjs",
|
|
52
|
+
"require": "./dist/internal/plugins.cjs",
|
|
53
|
+
"default": "./dist/internal/plugins.mjs"
|
|
66
54
|
}
|
|
67
55
|
},
|
|
68
56
|
"scripts": {
|
|
69
57
|
"build": "unbuild",
|
|
70
|
-
"lint": "biome check .",
|
|
71
58
|
"typecheck": "tsc --noEmit",
|
|
59
|
+
"lint": "biome check .",
|
|
72
60
|
"lint:fix": "pnpm lint --write",
|
|
73
61
|
"test": "vitest",
|
|
74
|
-
"test:ci": "vitest run"
|
|
75
|
-
"format": "biome format . --write"
|
|
62
|
+
"test:ci": "vitest run"
|
|
76
63
|
},
|
|
77
64
|
"devDependencies": {
|
|
65
|
+
"@electric-sql/pglite": "^0.2.17",
|
|
78
66
|
"@types/better-sqlite3": "^7.6.11",
|
|
79
67
|
"@types/node": "^20.14.0",
|
|
80
68
|
"@types/pg": "^8.11.10",
|
|
81
|
-
"better-sqlite3": "^11.1.2",
|
|
82
|
-
"csv-stringify": "^6.5.0",
|
|
83
|
-
"drizzle-orm": "^0.35.2",
|
|
84
|
-
"pg": "^8.12.0",
|
|
85
|
-
"postgres-range": "^1.1.4",
|
|
86
69
|
"unbuild": "^2.0.0",
|
|
87
70
|
"vitest": "^1.6.0"
|
|
88
71
|
},
|
|
89
72
|
"dependencies": {
|
|
90
|
-
"@apibara/protocol": "2.
|
|
73
|
+
"@apibara/protocol": "2.1.0-beta.2",
|
|
91
74
|
"@opentelemetry/api": "^1.9.0",
|
|
75
|
+
"ci-info": "^4.1.0",
|
|
92
76
|
"consola": "^3.2.3",
|
|
93
77
|
"hookable": "^5.5.3",
|
|
94
78
|
"klona": "^2.0.6",
|
|
@@ -96,10 +80,6 @@
|
|
|
96
80
|
"unctx": "^2.3.1"
|
|
97
81
|
},
|
|
98
82
|
"peerDependencies": {
|
|
99
|
-
"better-sqlite3": "^11.1.2",
|
|
100
|
-
"csv-stringify": "^6.5.0",
|
|
101
|
-
"drizzle-orm": "^0.35.2",
|
|
102
|
-
"postgres-range": "^1.1.4",
|
|
103
83
|
"vitest": "^1.6.0"
|
|
104
84
|
}
|
|
105
85
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { type MiddlewareFunction, type NextFunction, compose } from "./compose";
|
|
3
|
+
|
|
4
|
+
type C = {
|
|
5
|
+
bag: Record<string, unknown>;
|
|
6
|
+
finalized: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type MiddlewareTuple = MiddlewareFunction<C>;
|
|
10
|
+
|
|
11
|
+
describe("compose", () => {
|
|
12
|
+
async function a(context: C, next: NextFunction) {
|
|
13
|
+
context.bag.log = "log";
|
|
14
|
+
await next();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function b(context: C, next: NextFunction) {
|
|
18
|
+
await next();
|
|
19
|
+
context.bag.headers = "custom-header";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function c(context: C, next: NextFunction) {
|
|
23
|
+
context.bag.xxx = "yyy";
|
|
24
|
+
await next();
|
|
25
|
+
context.bag.zzz = context.bag.xxx;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function handler(context: C, next: NextFunction) {
|
|
29
|
+
context.bag.log = `${context.bag.log} message`;
|
|
30
|
+
await next();
|
|
31
|
+
context.bag.message = "new response";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const middleware: MiddlewareTuple[] = [];
|
|
35
|
+
|
|
36
|
+
middleware.push(a);
|
|
37
|
+
middleware.push(b);
|
|
38
|
+
middleware.push(c);
|
|
39
|
+
middleware.push(handler);
|
|
40
|
+
|
|
41
|
+
it("composes", async () => {
|
|
42
|
+
const context: C = {
|
|
43
|
+
bag: {},
|
|
44
|
+
finalized: false,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const composed = compose<C>(middleware);
|
|
48
|
+
await composed(context);
|
|
49
|
+
|
|
50
|
+
expect(context.bag.log).toBeDefined();
|
|
51
|
+
expect(context.bag.log).toBe("log message");
|
|
52
|
+
expect(context.bag.headers).toBe("custom-header");
|
|
53
|
+
expect(context.bag.xxx).toBe("yyy");
|
|
54
|
+
expect(context.bag.zzz).toBe("yyy");
|
|
55
|
+
expect(context.finalized).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("accepts a next function", async () => {
|
|
59
|
+
const context: C = {
|
|
60
|
+
bag: {},
|
|
61
|
+
finalized: false,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const composed = compose<C>(middleware);
|
|
65
|
+
await composed(context, async () => {
|
|
66
|
+
context.finalized = true;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(context.bag.log).toBeDefined();
|
|
70
|
+
expect(context.bag.log).toBe("log message");
|
|
71
|
+
expect(context.bag.headers).toBe("custom-header");
|
|
72
|
+
expect(context.bag.xxx).toBe("yyy");
|
|
73
|
+
expect(context.bag.zzz).toBe("yyy");
|
|
74
|
+
expect(context.finalized).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
package/src/compose.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2021 - present, Yusuke Wada and Hono contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { IndexerContext } from "./context";
|
|
26
|
+
|
|
27
|
+
export type NextFunction = () => Promise<void>;
|
|
28
|
+
export type MiddlewareFunction<C> = (
|
|
29
|
+
context: C,
|
|
30
|
+
next: NextFunction,
|
|
31
|
+
) => Promise<void>;
|
|
32
|
+
|
|
33
|
+
export function compose<C extends IndexerContext>(
|
|
34
|
+
middleware: MiddlewareFunction<C>[],
|
|
35
|
+
): (context: C, next?: NextFunction) => Promise<void> {
|
|
36
|
+
return (context, next) => {
|
|
37
|
+
let index = -1;
|
|
38
|
+
|
|
39
|
+
return dispatch(0);
|
|
40
|
+
|
|
41
|
+
/// Dispatch the middleware functions.
|
|
42
|
+
async function dispatch(i: number): Promise<void> {
|
|
43
|
+
if (i <= index) {
|
|
44
|
+
throw new Error("next() called multiple times");
|
|
45
|
+
}
|
|
46
|
+
index = i;
|
|
47
|
+
|
|
48
|
+
let handler: MiddlewareFunction<C> | undefined;
|
|
49
|
+
|
|
50
|
+
if (i >= middleware.length) {
|
|
51
|
+
if (next) {
|
|
52
|
+
await next();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (middleware[i]) {
|
|
59
|
+
handler = middleware[i];
|
|
60
|
+
} else {
|
|
61
|
+
handler = i === middleware.length ? next : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!handler) {
|
|
65
|
+
throw new Error("Handler not found");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await handler(context, () => dispatch(i + 1));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|