@apibara/indexer 2.1.0-beta.3 → 2.1.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.
Files changed (66) hide show
  1. package/dist/index.cjs +71 -26
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.mts +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.mjs +61 -16
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/internal/index.cjs +1 -0
  9. package/dist/internal/index.cjs.map +1 -0
  10. package/dist/internal/index.mjs +1 -0
  11. package/dist/internal/index.mjs.map +1 -0
  12. package/dist/internal/plugins.cjs +4 -4
  13. package/dist/internal/plugins.cjs.map +1 -0
  14. package/dist/internal/plugins.d.cts +1 -1
  15. package/dist/internal/plugins.d.mts +1 -1
  16. package/dist/internal/plugins.d.ts +1 -1
  17. package/dist/internal/plugins.mjs +2 -2
  18. package/dist/internal/plugins.mjs.map +1 -0
  19. package/dist/internal/testing.cjs +25 -10
  20. package/dist/internal/testing.cjs.map +1 -0
  21. package/dist/internal/testing.d.cts +5 -3
  22. package/dist/internal/testing.d.mts +5 -3
  23. package/dist/internal/testing.d.ts +5 -3
  24. package/dist/internal/testing.mjs +23 -8
  25. package/dist/internal/testing.mjs.map +1 -0
  26. package/dist/plugins/index.cjs +4 -4
  27. package/dist/plugins/index.cjs.map +1 -0
  28. package/dist/plugins/index.d.cts +2 -2
  29. package/dist/plugins/index.d.mts +2 -2
  30. package/dist/plugins/index.d.ts +2 -2
  31. package/dist/plugins/index.mjs +4 -4
  32. package/dist/plugins/index.mjs.map +1 -0
  33. package/dist/shared/{indexer.fedcd831.d.cts → indexer.27b29a67.d.cts} +3 -4
  34. package/dist/shared/{indexer.fedcd831.d.mts → indexer.27b29a67.d.mts} +3 -4
  35. package/dist/shared/{indexer.fedcd831.d.ts → indexer.27b29a67.d.ts} +3 -4
  36. package/dist/shared/{indexer.077335f3.cjs → indexer.479ae593.cjs} +6 -0
  37. package/dist/shared/indexer.479ae593.cjs.map +1 -0
  38. package/dist/shared/{indexer.a55ad619.mjs → indexer.75773ef1.mjs} +6 -1
  39. package/dist/shared/indexer.75773ef1.mjs.map +1 -0
  40. package/dist/shared/{indexer.ff25c953.mjs → indexer.98a921a7.mjs} +2 -2
  41. package/dist/shared/indexer.98a921a7.mjs.map +1 -0
  42. package/dist/shared/{indexer.2416906c.cjs → indexer.a09fa402.cjs} +4 -4
  43. package/dist/shared/indexer.a09fa402.cjs.map +1 -0
  44. package/dist/testing/index.cjs +10 -5
  45. package/dist/testing/index.cjs.map +1 -0
  46. package/dist/testing/index.d.cts +4 -3
  47. package/dist/testing/index.d.mts +4 -3
  48. package/dist/testing/index.d.ts +4 -3
  49. package/dist/testing/index.mjs +10 -5
  50. package/dist/testing/index.mjs.map +1 -0
  51. package/dist/vcr/index.cjs +3 -1
  52. package/dist/vcr/index.cjs.map +1 -0
  53. package/dist/vcr/index.d.cts +1 -1
  54. package/dist/vcr/index.d.mts +1 -1
  55. package/dist/vcr/index.d.ts +1 -1
  56. package/dist/vcr/index.mjs +3 -1
  57. package/dist/vcr/index.mjs.map +1 -0
  58. package/package.json +3 -3
  59. package/src/indexer.ts +49 -15
  60. package/src/internal/testing.ts +34 -11
  61. package/src/otel.ts +29 -2
  62. package/src/testing/index.ts +11 -0
  63. package/dist/shared/indexer.601ceab0.cjs +0 -7
  64. package/dist/shared/indexer.9b21ddd2.mjs +0 -5
  65. package/src/compose.test.ts +0 -76
  66. package/src/indexer.test.ts +0 -430
@@ -1,12 +1,13 @@
1
- import { I as IndexerWithStreamConfig } from '../shared/indexer.fedcd831.cjs';
1
+ import { I as IndexerWithStreamConfig } from '../shared/indexer.27b29a67.cjs';
2
2
  import '@apibara/protocol';
3
3
  import 'hookable';
4
4
 
5
+ type VcrResult = Record<string, unknown>;
5
6
  declare function createVcr(): {
6
7
  run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
7
8
  fromBlock: bigint;
8
9
  toBlock: bigint;
9
- }): Promise<void>;
10
+ }): Promise<VcrResult>;
10
11
  };
11
12
 
12
- export { createVcr };
13
+ export { type VcrResult, createVcr };
@@ -1,12 +1,13 @@
1
- import { I as IndexerWithStreamConfig } from '../shared/indexer.fedcd831.mjs';
1
+ import { I as IndexerWithStreamConfig } from '../shared/indexer.27b29a67.mjs';
2
2
  import '@apibara/protocol';
3
3
  import 'hookable';
4
4
 
5
+ type VcrResult = Record<string, unknown>;
5
6
  declare function createVcr(): {
6
7
  run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
7
8
  fromBlock: bigint;
8
9
  toBlock: bigint;
9
- }): Promise<void>;
10
+ }): Promise<VcrResult>;
10
11
  };
11
12
 
12
- export { createVcr };
13
+ export { type VcrResult, createVcr };
@@ -1,12 +1,13 @@
1
- import { I as IndexerWithStreamConfig } from '../shared/indexer.fedcd831.js';
1
+ import { I as IndexerWithStreamConfig } from '../shared/indexer.27b29a67.js';
2
2
  import '@apibara/protocol';
3
3
  import 'hookable';
4
4
 
5
+ type VcrResult = Record<string, unknown>;
5
6
  declare function createVcr(): {
6
7
  run<TFilter, TBlock>(cassetteName: string, indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>, range: {
7
8
  fromBlock: bigint;
8
9
  toBlock: bigint;
9
- }): Promise<void>;
10
+ }): Promise<VcrResult>;
10
11
  };
11
12
 
12
- export { createVcr };
13
+ export { type VcrResult, createVcr };
@@ -1,23 +1,23 @@
1
1
  import { createClient } from '@apibara/protocol';
2
2
  import ci from 'ci-info';
3
+ import { u as useIndexerContext } from '../shared/indexer.75773ef1.mjs';
3
4
  import { createIndexer } from '../index.mjs';
4
5
  import { internalContext } from '../internal/plugins.mjs';
5
- import { l as logger } from '../shared/indexer.ff25c953.mjs';
6
+ import { l as logger } from '../shared/indexer.98a921a7.mjs';
6
7
  import { isCassetteAvailable, record, replay } from '../vcr/index.mjs';
8
+ import 'node:async_hooks';
9
+ import 'unctx';
7
10
  import 'consola';
8
11
  import 'hookable';
9
12
  import 'node:assert';
10
- import '../shared/indexer.a55ad619.mjs';
11
- import 'node:async_hooks';
12
- import 'unctx';
13
13
  import '@opentelemetry/api';
14
- import '../shared/indexer.9b21ddd2.mjs';
15
14
  import 'node:fs/promises';
16
15
  import 'node:path';
17
16
  import 'node:fs';
18
17
  import '@apibara/protocol/testing';
19
18
 
20
19
  function createVcr() {
20
+ let result;
21
21
  return {
22
22
  async run(cassetteName, indexerConfig, range) {
23
23
  const vcrConfig = {
@@ -41,6 +41,9 @@ function createVcr() {
41
41
  ...indexerConfig.plugins ?? []
42
42
  ];
43
43
  const indexer = createIndexer(indexerConfig);
44
+ indexer.hooks.hook("run:after", () => {
45
+ result = useIndexerContext();
46
+ });
44
47
  if (!isCassetteAvailable(vcrConfig, cassetteName)) {
45
48
  if (ci.isCI) {
46
49
  throw new Error("Cannot record cassette in CI");
@@ -53,8 +56,10 @@ function createVcr() {
53
56
  } else {
54
57
  await replay(vcrConfig, indexer, cassetteName);
55
58
  }
59
+ return result;
56
60
  }
57
61
  };
58
62
  }
59
63
 
60
64
  export { createVcr };
65
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/testing/index.ts"],"sourcesContent":["import { createClient } from \"@apibara/protocol\";\nimport ci from \"ci-info\";\nimport { useIndexerContext } from \"../context\";\nimport { type IndexerWithStreamConfig, createIndexer } from \"../indexer\";\nimport { type InternalContext, internalContext } from \"../plugins/context\";\nimport { logger } from \"../plugins/logger\";\nimport type { CassetteOptions, VcrConfig } from \"../vcr/config\";\nimport { isCassetteAvailable } from \"../vcr/helper\";\nimport { record } from \"../vcr/record\";\nimport { replay } from \"../vcr/replay\";\n\nexport type VcrResult = Record<string, unknown>;\n\nexport function createVcr() {\n let result: VcrResult;\n\n return {\n async run<TFilter, TBlock>(\n cassetteName: string,\n indexerConfig: IndexerWithStreamConfig<TFilter, TBlock>,\n range: { fromBlock: bigint; toBlock: bigint },\n ) {\n const vcrConfig: VcrConfig = {\n cassetteDir: \"cassettes\",\n };\n\n const cassetteOptions: CassetteOptions = {\n name: cassetteName,\n startingCursor: {\n orderKey: range.fromBlock,\n },\n endingCursor: {\n orderKey: range.toBlock,\n },\n };\n\n indexerConfig.plugins = [\n internalContext({\n indexerName: cassetteName,\n availableIndexers: [cassetteName],\n } as InternalContext),\n logger(),\n ...(indexerConfig.plugins ?? []),\n ];\n\n const indexer = createIndexer(indexerConfig);\n\n indexer.hooks.hook(\"run:after\", () => {\n result = useIndexerContext();\n });\n\n if (!isCassetteAvailable(vcrConfig, cassetteName)) {\n if (ci.isCI) {\n throw new Error(\"Cannot record cassette in CI\");\n }\n\n const client = createClient(\n indexer.streamConfig,\n indexer.options.streamUrl,\n );\n await record(vcrConfig, client, indexer, cassetteOptions);\n } else {\n await replay(vcrConfig, indexer, cassetteName);\n }\n\n return result;\n },\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAaO,SAAS,SAAY,GAAA;AAC1B,EAAI,IAAA,MAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,MAAM,GAAA,CACJ,YACA,EAAA,aAAA,EACA,KACA,EAAA;AACA,MAAA,MAAM,SAAuB,GAAA;AAAA,QAC3B,WAAa,EAAA,WAAA;AAAA,OACf,CAAA;AAEA,MAAA,MAAM,eAAmC,GAAA;AAAA,QACvC,IAAM,EAAA,YAAA;AAAA,QACN,cAAgB,EAAA;AAAA,UACd,UAAU,KAAM,CAAA,SAAA;AAAA,SAClB;AAAA,QACA,YAAc,EAAA;AAAA,UACZ,UAAU,KAAM,CAAA,OAAA;AAAA,SAClB;AAAA,OACF,CAAA;AAEA,MAAA,aAAA,CAAc,OAAU,GAAA;AAAA,QACtB,eAAgB,CAAA;AAAA,UACd,WAAa,EAAA,YAAA;AAAA,UACb,iBAAA,EAAmB,CAAC,YAAY,CAAA;AAAA,SACd,CAAA;AAAA,QACpB,MAAO,EAAA;AAAA,QACP,GAAI,aAAc,CAAA,OAAA,IAAW,EAAC;AAAA,OAChC,CAAA;AAEA,MAAM,MAAA,OAAA,GAAU,cAAc,aAAa,CAAA,CAAA;AAE3C,MAAQ,OAAA,CAAA,KAAA,CAAM,IAAK,CAAA,WAAA,EAAa,MAAM;AACpC,QAAA,MAAA,GAAS,iBAAkB,EAAA,CAAA;AAAA,OAC5B,CAAA,CAAA;AAED,MAAA,IAAI,CAAC,mBAAA,CAAoB,SAAW,EAAA,YAAY,CAAG,EAAA;AACjD,QAAA,IAAI,GAAG,IAAM,EAAA;AACX,UAAM,MAAA,IAAI,MAAM,8BAA8B,CAAA,CAAA;AAAA,SAChD;AAEA,QAAA,MAAM,MAAS,GAAA,YAAA;AAAA,UACb,OAAQ,CAAA,YAAA;AAAA,UACR,QAAQ,OAAQ,CAAA,SAAA;AAAA,SAClB,CAAA;AACA,QAAA,MAAM,MAAO,CAAA,SAAA,EAAW,MAAQ,EAAA,OAAA,EAAS,eAAe,CAAA,CAAA;AAAA,OACnD,MAAA;AACL,QAAM,MAAA,MAAA,CAAO,SAAW,EAAA,OAAA,EAAS,YAAY,CAAA,CAAA;AAAA,OAC/C;AAEA,MAAO,OAAA,MAAA,CAAA;AAAA,KACT;AAAA,GACF,CAAA;AACF;;;;"}
@@ -9,10 +9,11 @@ const testing = require('@apibara/protocol/testing');
9
9
  require('@apibara/protocol');
10
10
  require('consola');
11
11
  require('hookable');
12
- require('../shared/indexer.077335f3.cjs');
12
+ require('../shared/indexer.479ae593.cjs');
13
13
  require('node:async_hooks');
14
14
  require('unctx');
15
15
  require('@opentelemetry/api');
16
+ require('../internal/plugins.cjs');
16
17
 
17
18
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
18
19
 
@@ -90,3 +91,4 @@ exports.loadCassette = loadCassette;
90
91
  exports.record = record;
91
92
  exports.replay = replay;
92
93
  exports.serialize = serialize;
94
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../src/vcr/helper.ts","../../src/vcr/record.ts","../../src/vcr/replay.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { VcrConfig } from \"./config\";\n\nexport function deserialize(str: string) {\n return JSON.parse(str, (_, value) =>\n typeof value === \"string\" && value.match(/^\\d+n$/)\n ? BigInt(value.slice(0, -1))\n : value,\n );\n}\n\nexport function serialize(obj: Record<string, unknown>): string {\n return JSON.stringify(\n obj,\n (_, value) => (typeof value === \"bigint\" ? `${value.toString()}n` : value),\n \"\\t\",\n );\n}\n\nexport function isCassetteAvailable(\n vcrConfig: VcrConfig,\n cassetteName: string,\n): boolean {\n const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);\n return fs.existsSync(filePath);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Client, StreamDataResponse } from \"@apibara/protocol\";\nimport { type Indexer, run } from \"../indexer\";\nimport type { CassetteOptions, VcrConfig } from \"./config\";\nimport { serialize } from \"./helper\";\n\nexport type CassetteDataType<TFilter, TBlock> = {\n filter: TFilter;\n messages: StreamDataResponse<TBlock>[];\n};\n\nexport async function record<TFilter, TBlock, TTxnParams>(\n vcrConfig: VcrConfig,\n client: Client<TFilter, TBlock>,\n indexer: Indexer<TFilter, TBlock>,\n cassetteOptions: CassetteOptions,\n) {\n const messages: StreamDataResponse<TBlock>[] = [];\n\n indexer.hooks.addHooks({\n \"connect:before\"({ options, request }) {\n request.startingCursor = cassetteOptions.startingCursor;\n options.endingCursor = cassetteOptions.endingCursor;\n },\n message({ message }) {\n messages.push(message);\n },\n async \"run:after\"() {\n const output: CassetteDataType<TFilter, TBlock> = {\n filter: indexer.options.filter,\n messages: messages,\n };\n\n await fs.mkdir(vcrConfig.cassetteDir, { recursive: true });\n\n const filePath = path.join(\n vcrConfig.cassetteDir,\n `${cassetteOptions.name}.json`,\n );\n\n await fs.writeFile(filePath, serialize(output), { flag: \"w\" });\n },\n });\n\n await run(client, indexer);\n}\n","import assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Client } from \"@apibara/protocol\";\nimport { MockClient } from \"@apibara/protocol/testing\";\nimport { type Indexer, run } from \"../indexer\";\nimport { type CassetteDataType, deserialize } from \"../vcr\";\nimport type { VcrConfig } from \"./config\";\n\nexport async function replay<TFilter, TBlock, TTxnParams>(\n vcrConfig: VcrConfig,\n indexer: Indexer<TFilter, TBlock>,\n cassetteName: string,\n) {\n const client = loadCassette<TFilter, TBlock>(vcrConfig, cassetteName);\n await run(client, indexer);\n}\n\nexport function loadCassette<TFilter, TBlock>(\n vcrConfig: VcrConfig,\n cassetteName: string,\n): Client<TFilter, TBlock> {\n const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);\n\n const data = fs.readFileSync(filePath, \"utf8\");\n const cassetteData: CassetteDataType<TFilter, TBlock> = deserialize(data);\n\n const { filter, messages } = cassetteData;\n\n return new MockClient<TFilter, TBlock>((request, options) => {\n // Notice that the request filter is an array of filters,\n // so we need to wrap the indexer filter in an array.\n assert.deepStrictEqual(\n request.filter,\n [filter],\n \"Indexer and cassette filter mismatch. Hint: delete the cassette and run again.\",\n );\n\n return messages;\n });\n}\n"],"names":["path","fs","run","MockClient","assert"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
2
- import { a as Indexer } from '../shared/indexer.fedcd831.cjs';
2
+ import { a as Indexer } from '../shared/indexer.27b29a67.cjs';
3
3
  import 'hookable';
4
4
 
5
5
  type VcrConfig = {
@@ -1,5 +1,5 @@
1
1
  import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
2
- import { a as Indexer } from '../shared/indexer.fedcd831.mjs';
2
+ import { a as Indexer } from '../shared/indexer.27b29a67.mjs';
3
3
  import 'hookable';
4
4
 
5
5
  type VcrConfig = {
@@ -1,5 +1,5 @@
1
1
  import { Cursor, StreamDataResponse, Client } from '@apibara/protocol';
2
- import { a as Indexer } from '../shared/indexer.fedcd831.js';
2
+ import { a as Indexer } from '../shared/indexer.27b29a67.js';
3
3
  import 'hookable';
4
4
 
5
5
  type VcrConfig = {
@@ -7,10 +7,11 @@ import { MockClient } from '@apibara/protocol/testing';
7
7
  import '@apibara/protocol';
8
8
  import 'consola';
9
9
  import 'hookable';
10
- import '../shared/indexer.a55ad619.mjs';
10
+ import '../shared/indexer.75773ef1.mjs';
11
11
  import 'node:async_hooks';
12
12
  import 'unctx';
13
13
  import '@opentelemetry/api';
14
+ import '../internal/plugins.mjs';
14
15
 
15
16
  function deserialize(str) {
16
17
  return JSON.parse(
@@ -76,3 +77,4 @@ function loadCassette(vcrConfig, cassetteName) {
76
77
  }
77
78
 
78
79
  export { deserialize, isCassetteAvailable, loadCassette, record, replay, serialize };
80
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/vcr/helper.ts","../../src/vcr/record.ts","../../src/vcr/replay.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { VcrConfig } from \"./config\";\n\nexport function deserialize(str: string) {\n return JSON.parse(str, (_, value) =>\n typeof value === \"string\" && value.match(/^\\d+n$/)\n ? BigInt(value.slice(0, -1))\n : value,\n );\n}\n\nexport function serialize(obj: Record<string, unknown>): string {\n return JSON.stringify(\n obj,\n (_, value) => (typeof value === \"bigint\" ? `${value.toString()}n` : value),\n \"\\t\",\n );\n}\n\nexport function isCassetteAvailable(\n vcrConfig: VcrConfig,\n cassetteName: string,\n): boolean {\n const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);\n return fs.existsSync(filePath);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Client, StreamDataResponse } from \"@apibara/protocol\";\nimport { type Indexer, run } from \"../indexer\";\nimport type { CassetteOptions, VcrConfig } from \"./config\";\nimport { serialize } from \"./helper\";\n\nexport type CassetteDataType<TFilter, TBlock> = {\n filter: TFilter;\n messages: StreamDataResponse<TBlock>[];\n};\n\nexport async function record<TFilter, TBlock, TTxnParams>(\n vcrConfig: VcrConfig,\n client: Client<TFilter, TBlock>,\n indexer: Indexer<TFilter, TBlock>,\n cassetteOptions: CassetteOptions,\n) {\n const messages: StreamDataResponse<TBlock>[] = [];\n\n indexer.hooks.addHooks({\n \"connect:before\"({ options, request }) {\n request.startingCursor = cassetteOptions.startingCursor;\n options.endingCursor = cassetteOptions.endingCursor;\n },\n message({ message }) {\n messages.push(message);\n },\n async \"run:after\"() {\n const output: CassetteDataType<TFilter, TBlock> = {\n filter: indexer.options.filter,\n messages: messages,\n };\n\n await fs.mkdir(vcrConfig.cassetteDir, { recursive: true });\n\n const filePath = path.join(\n vcrConfig.cassetteDir,\n `${cassetteOptions.name}.json`,\n );\n\n await fs.writeFile(filePath, serialize(output), { flag: \"w\" });\n },\n });\n\n await run(client, indexer);\n}\n","import assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Client } from \"@apibara/protocol\";\nimport { MockClient } from \"@apibara/protocol/testing\";\nimport { type Indexer, run } from \"../indexer\";\nimport { type CassetteDataType, deserialize } from \"../vcr\";\nimport type { VcrConfig } from \"./config\";\n\nexport async function replay<TFilter, TBlock, TTxnParams>(\n vcrConfig: VcrConfig,\n indexer: Indexer<TFilter, TBlock>,\n cassetteName: string,\n) {\n const client = loadCassette<TFilter, TBlock>(vcrConfig, cassetteName);\n await run(client, indexer);\n}\n\nexport function loadCassette<TFilter, TBlock>(\n vcrConfig: VcrConfig,\n cassetteName: string,\n): Client<TFilter, TBlock> {\n const filePath = path.join(vcrConfig.cassetteDir, `${cassetteName}.json`);\n\n const data = fs.readFileSync(filePath, \"utf8\");\n const cassetteData: CassetteDataType<TFilter, TBlock> = deserialize(data);\n\n const { filter, messages } = cassetteData;\n\n return new MockClient<TFilter, TBlock>((request, options) => {\n // Notice that the request filter is an array of filters,\n // so we need to wrap the indexer filter in an array.\n assert.deepStrictEqual(\n request.filter,\n [filter],\n \"Indexer and cassette filter mismatch. Hint: delete the cassette and run again.\",\n );\n\n return messages;\n });\n}\n"],"names":["fs"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apibara/indexer",
3
- "version": "2.1.0-beta.3",
3
+ "version": "2.1.0-beta.31",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -70,10 +70,10 @@
70
70
  "vitest": "^1.6.0"
71
71
  },
72
72
  "dependencies": {
73
- "@apibara/protocol": "2.1.0-beta.3",
73
+ "@apibara/protocol": "2.1.0-beta.31",
74
74
  "@opentelemetry/api": "^1.9.0",
75
75
  "ci-info": "^4.1.0",
76
- "consola": "^3.2.3",
76
+ "consola": "^3.4.2",
77
77
  "hookable": "^5.5.3",
78
78
  "klona": "^2.0.6",
79
79
  "nice-grpc": "^2.1.8",
package/src/indexer.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  type Client,
3
3
  ClientError,
4
+ type CreateClientOptions,
4
5
  type Cursor,
5
6
  type DataFinality,
6
7
  type Finalize,
7
- type Heartbeat,
8
8
  type Invalidate,
9
+ ServerError,
9
10
  Status,
10
11
  type StreamConfig,
11
12
  type StreamDataOptions,
@@ -28,8 +29,9 @@ import {
28
29
  indexerAsyncContext,
29
30
  useIndexerContext,
30
31
  } from "./context";
31
- import { tracer } from "./otel";
32
+ import { createIndexerMetrics, createTracer } from "./otel";
32
33
  import type { IndexerPlugin } from "./plugins";
34
+ import { useInternalContext } from "./plugins/context";
33
35
 
34
36
  export type UseMiddlewareFunction = (
35
37
  fn: MiddlewareFunction<IndexerContext>,
@@ -59,7 +61,7 @@ export interface IndexerHooks<TFilter, TBlock> {
59
61
  message: ({ message }: { message: StreamDataResponse<TBlock> }) => void;
60
62
  "message:invalidate": ({ message }: { message: Invalidate }) => void;
61
63
  "message:finalize": ({ message }: { message: Finalize }) => void;
62
- "message:heartbeat": ({ message }: { message: Heartbeat }) => void;
64
+ "message:heartbeat": () => void;
63
65
  "message:systemMessage": ({ message }: { message: SystemMessage }) => void;
64
66
  }
65
67
 
@@ -81,6 +83,7 @@ export type IndexerConfig<TFilter, TBlock> = {
81
83
  streamUrl: string;
82
84
  filter: TFilter;
83
85
  finality?: DataFinality;
86
+ clientOptions?: CreateClientOptions;
84
87
  factory?: ({
85
88
  block,
86
89
  context,
@@ -179,13 +182,18 @@ export async function runWithReconnect<TFilter, TBlock>(
179
182
 
180
183
  retryCount++;
181
184
 
182
- if (error instanceof ClientError) {
185
+ if (error instanceof ClientError || error instanceof ServerError) {
186
+ const isServerError = error instanceof ServerError;
187
+
183
188
  if (error.code === Status.INTERNAL) {
184
189
  if (retryCount < maxRetries) {
185
190
  consola.error(
186
- "Internal server error, reconnecting...",
187
- error.message,
191
+ `Internal ${isServerError ? "server" : "client"} error: ${
192
+ error.message
193
+ }`,
188
194
  );
195
+ consola.start("Reconnecting...");
196
+ console.log();
189
197
 
190
198
  // Add jitter to the retry delay to avoid all clients retrying at the same time.
191
199
  const delay = Math.random() * (retryDelay * 0.2) + retryDelay;
@@ -216,8 +224,13 @@ export async function run<TFilter, TBlock>(
216
224
  const context = useIndexerContext();
217
225
  const middleware = await registerMiddleware(indexer);
218
226
 
227
+ const indexerMetrics = createIndexerMetrics();
228
+ const tracer = createTracer();
229
+
219
230
  await indexer.hooks.callHook("run:before");
220
231
 
232
+ const { indexerName: indexerId } = useInternalContext();
233
+
221
234
  const isFactoryMode = indexer.options.factory !== undefined;
222
235
 
223
236
  // Give priority to startingCursor over startingBlock.
@@ -235,13 +248,13 @@ export async function run<TFilter, TBlock>(
235
248
  }
236
249
 
237
250
  // if factory mode we add a empty filter at the end of the filter array.
238
- const request = indexer.streamConfig.Request.make({
251
+ const request = {
239
252
  filter: isFactoryMode
240
253
  ? [indexer.options.filter, {} as TFilter]
241
254
  : [indexer.options.filter],
242
255
  finality: indexer.options.finality,
243
256
  startingCursor,
244
- });
257
+ } as StreamDataRequest<TFilter>;
245
258
 
246
259
  const options: StreamDataOptions = {};
247
260
 
@@ -288,11 +301,19 @@ export async function run<TFilter, TBlock>(
288
301
  context.endCursor = endCursor;
289
302
  context.finality = finality;
290
303
 
304
+ // Record current block number being processed
305
+ indexerMetrics.currentBlockGauge.record(
306
+ Number(endCursor?.orderKey),
307
+ {
308
+ indexer_id: indexerId,
309
+ },
310
+ );
311
+
291
312
  await middleware(context, async () => {
292
313
  let block: TBlock | null;
293
314
 
294
315
  // when factory mode
295
- if (isFactoryMode) {
316
+ if (isFactoryMode && finality !== "pending") {
296
317
  assert(indexer.options.factory !== undefined);
297
318
 
298
319
  const [factoryBlock, mainBlock] = blocks;
@@ -315,11 +336,11 @@ export async function run<TFilter, TBlock>(
315
336
  );
316
337
 
317
338
  // create request with new filters
318
- const request = indexer.streamConfig.Request.make({
339
+ const request = {
319
340
  filter: [indexer.options.filter, mainFilter],
320
341
  finality: indexer.options.finality,
321
342
  startingCursor: cursor,
322
- });
343
+ } as StreamDataRequest<TFilter>;
323
344
 
324
345
  await indexer.hooks.callHook("connect:factory", {
325
346
  request,
@@ -364,6 +385,11 @@ export async function run<TFilter, TBlock>(
364
385
  span.end();
365
386
  });
366
387
 
388
+ // Record processed block metric
389
+ indexerMetrics.processedBlockCounter.add(1, {
390
+ indexer_id: indexerId,
391
+ });
392
+
367
393
  context.cursor = undefined;
368
394
  context.endCursor = undefined;
369
395
  context.finality = undefined;
@@ -372,21 +398,29 @@ export async function run<TFilter, TBlock>(
372
398
  }
373
399
  case "invalidate": {
374
400
  await tracer.startActiveSpan("message invalidate", async (span) => {
375
- await indexer.hooks.callHook("message:invalidate", { message });
401
+ // Record reorg metric
402
+ indexerMetrics.reorgCounter.add(1, {
403
+ indexer_id: indexerId,
404
+ });
405
+ await indexer.hooks.callHook("message:invalidate", {
406
+ message: message.invalidate,
407
+ });
376
408
  span.end();
377
409
  });
378
410
  break;
379
411
  }
380
412
  case "finalize": {
381
413
  await tracer.startActiveSpan("message finalize", async (span) => {
382
- await indexer.hooks.callHook("message:finalize", { message });
414
+ await indexer.hooks.callHook("message:finalize", {
415
+ message: message.finalize,
416
+ });
383
417
  span.end();
384
418
  });
385
419
  break;
386
420
  }
387
421
  case "heartbeat": {
388
422
  await tracer.startActiveSpan("message heartbeat", async (span) => {
389
- await indexer.hooks.callHook("message:heartbeat", { message });
423
+ await indexer.hooks.callHook("message:heartbeat");
390
424
  span.end();
391
425
  });
392
426
  break;
@@ -409,7 +443,7 @@ export async function run<TFilter, TBlock>(
409
443
  }
410
444
 
411
445
  await indexer.hooks.callHook("message:systemMessage", {
412
- message,
446
+ message: message.systemMessage,
413
447
  });
414
448
  span.end();
415
449
  },
@@ -5,10 +5,9 @@ import {
5
5
  MockStream,
6
6
  type MockStreamResponse,
7
7
  } from "@apibara/protocol/testing";
8
-
9
8
  import { useIndexerContext } from "../context";
10
9
  import { type IndexerConfig, createIndexer, defineIndexer } from "../indexer";
11
- import { defineIndexerPlugin } from "../plugins";
10
+ import { defineIndexerPlugin, logger } from "../plugins";
12
11
  import { type InternalContext, internalContext } from "./plugins";
13
12
 
14
13
  export type MockMessagesOptions = {
@@ -20,6 +19,8 @@ export type MockMessagesOptions = {
20
19
  finalizeToIndex: number;
21
20
  finalizeTriggerIndex: number;
22
21
  };
22
+ uniqueKey?: boolean;
23
+ baseBlockNumber?: bigint;
23
24
  };
24
25
 
25
26
  export function generateMockMessages(
@@ -30,33 +31,50 @@ export function generateMockMessages(
30
31
  const finalizeAt = options?.finalize;
31
32
  const messages: MockStreamResponse[] = [];
32
33
 
34
+ const baseBlockNumber = options?.baseBlockNumber ?? BigInt(5_000_000);
35
+
33
36
  for (let i = 0; i < count; i++) {
37
+ const currentBlockNumber = baseBlockNumber + BigInt(i);
38
+ const uniqueKey = uniqueKeyFromOrderKey(currentBlockNumber);
34
39
  if (invalidateAt && i === invalidateAt.invalidateTriggerIndex) {
40
+ const invalidateToBlock =
41
+ baseBlockNumber + BigInt(invalidateAt.invalidateFromIndex);
35
42
  messages.push({
36
43
  _tag: "invalidate",
37
44
  invalidate: {
38
45
  cursor: {
39
- orderKey: BigInt(5_000_000 + invalidateAt.invalidateFromIndex),
46
+ orderKey: invalidateToBlock,
47
+ uniqueKey: options?.uniqueKey
48
+ ? uniqueKeyFromOrderKey(invalidateToBlock)
49
+ : undefined,
40
50
  },
41
- },
42
- } as Invalidate);
51
+ } as Invalidate,
52
+ });
43
53
  } else if (finalizeAt && i === finalizeAt.finalizeTriggerIndex) {
54
+ const fianlizedToBlock =
55
+ baseBlockNumber + BigInt(finalizeAt.finalizeToIndex);
44
56
  messages.push({
45
57
  _tag: "finalize",
46
58
  finalize: {
47
59
  cursor: {
48
- orderKey: BigInt(5_000_000 + finalizeAt.finalizeToIndex),
60
+ orderKey: fianlizedToBlock,
61
+ uniqueKey: options?.uniqueKey
62
+ ? uniqueKeyFromOrderKey(fianlizedToBlock)
63
+ : undefined,
49
64
  },
50
- },
51
- } as Finalize);
65
+ } as Finalize,
66
+ });
52
67
  } else {
53
68
  messages.push({
54
69
  _tag: "data",
55
70
  data: {
56
- cursor: { orderKey: BigInt(5_000_000 + i - 1) },
71
+ cursor: { orderKey: currentBlockNumber - 1n },
57
72
  finality: "accepted",
58
- data: [{ data: `${5_000_000 + i}` }],
59
- endCursor: { orderKey: BigInt(5_000_000 + i) },
73
+ data: [{ data: `${baseBlockNumber + BigInt(i)}` }],
74
+ endCursor: {
75
+ orderKey: currentBlockNumber,
76
+ uniqueKey: options?.uniqueKey ? uniqueKey : undefined,
77
+ },
60
78
  production: "backfill",
61
79
  },
62
80
  });
@@ -66,6 +84,10 @@ export function generateMockMessages(
66
84
  return messages;
67
85
  }
68
86
 
87
+ function uniqueKeyFromOrderKey(orderKey: bigint): `0x${string}` {
88
+ return `0xff00${orderKey.toString()}`;
89
+ }
90
+
69
91
  type MockIndexerParams = {
70
92
  internalContext?: InternalContext;
71
93
  override?: Partial<IndexerConfig<MockFilter, MockBlock>>;
@@ -82,6 +104,7 @@ export function getMockIndexer(params?: MockIndexerParams) {
82
104
  filter: {},
83
105
  async transform() {},
84
106
  plugins: [
107
+ logger(),
85
108
  internalContext(
86
109
  contextParams ??
87
110
  ({
package/src/otel.ts CHANGED
@@ -1,3 +1,30 @@
1
- import { trace } from "@opentelemetry/api";
1
+ import { metrics, trace } from "@opentelemetry/api";
2
2
 
3
- export const tracer = trace.getTracer("@apibara/indexer");
3
+ export function createTracer() {
4
+ return trace.getTracer("@apibara/indexer");
5
+ }
6
+
7
+ export function createIndexerMetrics() {
8
+ const meter = metrics.getMeter("@apibara/indexer");
9
+
10
+ const currentBlockGauge = meter.createGauge("current_block", {
11
+ description: "Current block number being processed",
12
+ unit: "{block}",
13
+ });
14
+
15
+ const processedBlockCounter = meter.createCounter("processed_blocks", {
16
+ description: "Number of blocks processed",
17
+ unit: "{blocks}",
18
+ });
19
+
20
+ const reorgCounter = meter.createCounter("reorgs", {
21
+ description: "Number of reorgs (invalidate messages) received",
22
+ unit: "{reorgs}",
23
+ });
24
+
25
+ return {
26
+ currentBlockGauge,
27
+ processedBlockCounter,
28
+ reorgCounter,
29
+ };
30
+ }
@@ -1,5 +1,6 @@
1
1
  import { createClient } from "@apibara/protocol";
2
2
  import ci from "ci-info";
3
+ import { useIndexerContext } from "../context";
3
4
  import { type IndexerWithStreamConfig, createIndexer } from "../indexer";
4
5
  import { type InternalContext, internalContext } from "../plugins/context";
5
6
  import { logger } from "../plugins/logger";
@@ -8,7 +9,11 @@ import { isCassetteAvailable } from "../vcr/helper";
8
9
  import { record } from "../vcr/record";
9
10
  import { replay } from "../vcr/replay";
10
11
 
12
+ export type VcrResult = Record<string, unknown>;
13
+
11
14
  export function createVcr() {
15
+ let result: VcrResult;
16
+
12
17
  return {
13
18
  async run<TFilter, TBlock>(
14
19
  cassetteName: string,
@@ -40,6 +45,10 @@ export function createVcr() {
40
45
 
41
46
  const indexer = createIndexer(indexerConfig);
42
47
 
48
+ indexer.hooks.hook("run:after", () => {
49
+ result = useIndexerContext();
50
+ });
51
+
43
52
  if (!isCassetteAvailable(vcrConfig, cassetteName)) {
44
53
  if (ci.isCI) {
45
54
  throw new Error("Cannot record cassette in CI");
@@ -53,6 +62,8 @@ export function createVcr() {
53
62
  } else {
54
63
  await replay(vcrConfig, indexer, cassetteName);
55
64
  }
65
+
66
+ return result;
56
67
  },
57
68
  };
58
69
  }
@@ -1,7 +0,0 @@
1
- 'use strict';
2
-
3
- function defineIndexerPlugin(def) {
4
- return def;
5
- }
6
-
7
- exports.defineIndexerPlugin = defineIndexerPlugin;
@@ -1,5 +0,0 @@
1
- function defineIndexerPlugin(def) {
2
- return def;
3
- }
4
-
5
- export { defineIndexerPlugin as d };