@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.
Files changed (127) hide show
  1. package/dist/index.cjs +271 -39
  2. package/dist/index.d.cts +1 -16
  3. package/dist/index.d.mts +1 -16
  4. package/dist/index.d.ts +1 -16
  5. package/dist/index.mjs +262 -25
  6. package/dist/internal/index.cjs +10 -0
  7. package/dist/internal/index.d.cts +3 -0
  8. package/dist/internal/index.d.mts +3 -0
  9. package/dist/internal/index.d.ts +3 -0
  10. package/dist/internal/index.mjs +8 -0
  11. package/dist/internal/plugins.cjs +38 -0
  12. package/dist/internal/plugins.d.cts +13 -0
  13. package/dist/internal/plugins.d.mts +13 -0
  14. package/dist/internal/plugins.d.ts +13 -0
  15. package/dist/internal/plugins.mjs +34 -0
  16. package/dist/internal/testing.cjs +118 -0
  17. package/dist/internal/testing.d.cts +42 -0
  18. package/dist/internal/testing.d.mts +42 -0
  19. package/dist/internal/testing.d.ts +42 -0
  20. package/dist/internal/testing.mjs +113 -0
  21. package/dist/plugins/index.cjs +39 -3
  22. package/dist/plugins/index.d.cts +16 -2
  23. package/dist/plugins/index.d.mts +16 -2
  24. package/dist/plugins/index.d.ts +16 -2
  25. package/dist/plugins/index.mjs +36 -3
  26. package/dist/shared/indexer.077335f3.cjs +15 -0
  27. package/dist/shared/indexer.2416906c.cjs +29 -0
  28. package/dist/shared/indexer.601ceab0.cjs +7 -0
  29. package/dist/shared/indexer.9b21ddd2.mjs +5 -0
  30. package/dist/shared/indexer.a55ad619.mjs +12 -0
  31. package/dist/shared/indexer.fedcd831.d.cts +100 -0
  32. package/dist/shared/indexer.fedcd831.d.mts +100 -0
  33. package/dist/shared/indexer.fedcd831.d.ts +100 -0
  34. package/dist/shared/indexer.ff25c953.mjs +26 -0
  35. package/dist/testing/index.cjs +52 -50
  36. package/dist/testing/index.d.cts +8 -36
  37. package/dist/testing/index.d.mts +8 -36
  38. package/dist/testing/index.d.ts +8 -36
  39. package/dist/testing/index.mjs +47 -47
  40. package/dist/vcr/index.cjs +84 -17
  41. package/dist/vcr/index.d.cts +16 -7
  42. package/dist/vcr/index.d.mts +16 -7
  43. package/dist/vcr/index.d.ts +16 -7
  44. package/dist/vcr/index.mjs +75 -11
  45. package/package.json +22 -42
  46. package/src/compose.test.ts +76 -0
  47. package/src/compose.ts +71 -0
  48. package/src/context.ts +14 -8
  49. package/src/index.ts +0 -5
  50. package/src/indexer.test.ts +125 -186
  51. package/src/indexer.ts +278 -151
  52. package/src/internal/index.ts +6 -0
  53. package/src/internal/plugins.ts +1 -0
  54. package/src/internal/testing.ts +148 -0
  55. package/src/plugins/config.ts +4 -4
  56. package/src/plugins/context.ts +40 -0
  57. package/src/plugins/index.ts +8 -1
  58. package/src/plugins/logger.ts +30 -0
  59. package/src/plugins/persistence.ts +24 -187
  60. package/src/testing/index.ts +58 -3
  61. package/src/vcr/record.ts +5 -3
  62. package/src/vcr/replay.ts +8 -18
  63. package/dist/plugins/kv.cjs +0 -131
  64. package/dist/plugins/kv.d.cts +0 -32
  65. package/dist/plugins/kv.d.mts +0 -32
  66. package/dist/plugins/kv.d.ts +0 -32
  67. package/dist/plugins/kv.mjs +0 -124
  68. package/dist/plugins/persistence.cjs +0 -182
  69. package/dist/plugins/persistence.d.cts +0 -50
  70. package/dist/plugins/persistence.d.mts +0 -50
  71. package/dist/plugins/persistence.d.ts +0 -50
  72. package/dist/plugins/persistence.mjs +0 -179
  73. package/dist/shared/indexer.2c23c9cd.mjs +0 -35
  74. package/dist/shared/indexer.318d3617.cjs +0 -47
  75. package/dist/shared/indexer.36530330.mjs +0 -249
  76. package/dist/shared/indexer.500fd281.d.cts +0 -23
  77. package/dist/shared/indexer.541d43eb.cjs +0 -266
  78. package/dist/shared/indexer.93d6b2eb.mjs +0 -17
  79. package/dist/shared/indexer.a8b7ab1f.cjs +0 -25
  80. package/dist/shared/indexer.b9c8f0d8.d.cts +0 -19
  81. package/dist/shared/indexer.b9c8f0d8.d.mts +0 -19
  82. package/dist/shared/indexer.b9c8f0d8.d.ts +0 -19
  83. package/dist/shared/indexer.c7ed6b83.d.cts +0 -82
  84. package/dist/shared/indexer.e1856641.d.mts +0 -23
  85. package/dist/shared/indexer.e4f2430f.d.ts +0 -23
  86. package/dist/shared/indexer.e8bd138d.d.mts +0 -82
  87. package/dist/shared/indexer.f761abcd.d.ts +0 -82
  88. package/dist/sinks/csv.cjs +0 -85
  89. package/dist/sinks/csv.d.cts +0 -66
  90. package/dist/sinks/csv.d.mts +0 -66
  91. package/dist/sinks/csv.d.ts +0 -66
  92. package/dist/sinks/csv.mjs +0 -78
  93. package/dist/sinks/drizzle/index.cjs +0 -212
  94. package/dist/sinks/drizzle/index.d.cts +0 -153
  95. package/dist/sinks/drizzle/index.d.mts +0 -153
  96. package/dist/sinks/drizzle/index.d.ts +0 -153
  97. package/dist/sinks/drizzle/index.mjs +0 -198
  98. package/dist/sinks/sqlite.cjs +0 -90
  99. package/dist/sinks/sqlite.d.cts +0 -71
  100. package/dist/sinks/sqlite.d.mts +0 -71
  101. package/dist/sinks/sqlite.d.ts +0 -71
  102. package/dist/sinks/sqlite.mjs +0 -87
  103. package/src/hooks/index.ts +0 -2
  104. package/src/hooks/useKVStore.ts +0 -12
  105. package/src/hooks/useSink.ts +0 -13
  106. package/src/plugins/kv.test.ts +0 -120
  107. package/src/plugins/kv.ts +0 -132
  108. package/src/plugins/persistence.test.ts +0 -151
  109. package/src/sink.ts +0 -36
  110. package/src/sinks/csv.test.ts +0 -65
  111. package/src/sinks/csv.ts +0 -159
  112. package/src/sinks/drizzle/Int8Range.ts +0 -52
  113. package/src/sinks/drizzle/delete.ts +0 -42
  114. package/src/sinks/drizzle/drizzle.test.ts +0 -239
  115. package/src/sinks/drizzle/drizzle.ts +0 -115
  116. package/src/sinks/drizzle/index.ts +0 -6
  117. package/src/sinks/drizzle/insert.ts +0 -42
  118. package/src/sinks/drizzle/select.ts +0 -44
  119. package/src/sinks/drizzle/transaction.ts +0 -49
  120. package/src/sinks/drizzle/update.ts +0 -47
  121. package/src/sinks/drizzle/utils.ts +0 -99
  122. package/src/sinks/sqlite.test.ts +0 -99
  123. package/src/sinks/sqlite.ts +0 -170
  124. package/src/testing/helper.ts +0 -13
  125. package/src/testing/indexer.ts +0 -35
  126. package/src/testing/setup.ts +0 -59
  127. package/src/testing/vcr.ts +0 -54
@@ -1,60 +1,60 @@
1
- import { test as test$1 } from 'vitest';
2
- import { i as isCassetteAvailable } from '../shared/indexer.2c23c9cd.mjs';
3
- import { l as loadCassette, a as record, b as replay } from '../shared/indexer.36530330.mjs';
4
- export { V as VcrSink, v as vcr } from '../shared/indexer.36530330.mjs';
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 'node:fs';
9
- import 'node:path';
14
+ import '../shared/indexer.9b21ddd2.mjs';
10
15
  import 'node:fs/promises';
11
- import 'klona/full';
12
- import 'consola';
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
- const test = test$1.extend({
19
- vcr: {
20
- withClient
21
- }
22
- });
23
- async function withClient(cassetteName, range, callback) {
24
- const vcrConfig = {
25
- cassetteDir: "cassettes"
26
- };
27
- const cassetteOptions = {
28
- name: cassetteName,
29
- startingCursor: {
30
- orderKey: range.fromBlock
31
- },
32
- endingCursor: {
33
- orderKey: range.toBlock
34
- }
35
- };
36
- const context = {
37
- async run(indexer) {
38
- const client = loadCassette(vcrConfig, cassetteName);
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 { generateMockMessages, test };
60
+ export { createVcr };
@@ -1,25 +1,92 @@
1
1
  'use strict';
2
2
 
3
- const helper = require('../shared/indexer.318d3617.cjs');
4
- const vcr_index = require('../shared/indexer.541d43eb.cjs');
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 = helper.deserialize;
21
- exports.isCassetteAvailable = helper.isCassetteAvailable;
22
- exports.serialize = helper.serialize;
23
- exports.loadCassette = vcr_index.loadCassette;
24
- exports.record = vcr_index.record;
25
- exports.replay = vcr_index.replay;
87
+ exports.deserialize = deserialize;
88
+ exports.isCassetteAvailable = isCassetteAvailable;
89
+ exports.loadCassette = loadCassette;
90
+ exports.record = record;
91
+ exports.replay = replay;
92
+ exports.serialize = serialize;
@@ -1,10 +1,16 @@
1
- import { V as VcrConfig, C as CassetteOptions } from '../shared/indexer.500fd281.cjs';
2
- export { a as VcrReplayResult, l as loadCassette, r as replay } from '../shared/indexer.500fd281.cjs';
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>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
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 };
@@ -1,10 +1,16 @@
1
- import { V as VcrConfig, C as CassetteOptions } from '../shared/indexer.e1856641.mjs';
2
- export { a as VcrReplayResult, l as loadCassette, r as replay } from '../shared/indexer.e1856641.mjs';
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>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
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 };
@@ -1,10 +1,16 @@
1
- import { V as VcrConfig, C as CassetteOptions } from '../shared/indexer.e4f2430f.js';
2
- export { a as VcrReplayResult, l as loadCassette, r as replay } from '../shared/indexer.e4f2430f.js';
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>, indexerArg: Indexer<TFilter, TBlock, TTxnParams>, cassetteOptions: CassetteOptions): Promise<void>;
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 };
@@ -1,14 +1,78 @@
1
- export { d as deserialize, i as isCassetteAvailable, s as serialize } from '../shared/indexer.2c23c9cd.mjs';
2
- export { l as loadCassette, a as record, b as replay } from '../shared/indexer.36530330.mjs';
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
- import 'node:fs';
7
- import 'node:path';
8
- import 'node:fs/promises';
9
- import 'klona/full';
10
- import 'consola';
11
- import 'hookable';
12
- import 'node:assert';
13
- import '../shared/indexer.93d6b2eb.mjs';
14
- import '@apibara/protocol/testing';
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.0.0-beta.9",
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
- "./plugins/kv": {
56
- "types": "./dist/plugins/kv.d.ts",
57
- "import": "./dist/plugins/kv.mjs",
58
- "require": "./dist/plugins/kv.cjs",
59
- "default": "./dist/plugins/kv.mjs"
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
- "./plugins/persistence": {
62
- "types": "./dist/plugins/persistence.d.ts",
63
- "import": "./dist/plugins/persistence.mjs",
64
- "require": "./dist/plugins/persistence.cjs",
65
- "default": "./dist/plugins/persistence.mjs"
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.0.0-beta.9",
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
+ }