univo 0.2.8 → 0.2.10

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/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import { config } from 'dotenv';
5
5
  import { WebSocket } from 'partysocket';
6
6
  import { defineCommand, runMain } from 'citty';
7
7
  import { http } from './transport.mjs';
8
- import { c as createLogger, v as version, g as name } from './shared/univo.Br975GsZ.mjs';
8
+ import { c as createLogger, v as version, f as name } from './shared/univo.DfCC9G2B.mjs';
9
9
  import './shared/univo.C0flyVee.mjs';
10
10
 
11
11
  const log = createLogger({ quiet: false });
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as createLogger, d as decompress, a as decoder, h as hexToNumber, i as isHexEqual, r as retry, v as version, n as nonNullable, b as normalizeHex } from './shared/univo.Br975GsZ.mjs';
1
+ import { c as createLogger, d as decompress, a as decoder, h as hexToNumber, i as isHexEqual, r as retry, v as version, n as normalizeHex } from './shared/univo.DfCC9G2B.mjs';
2
2
  import { c as createException, g as getException, a as catchException } from './shared/univo.C0flyVee.mjs';
3
3
 
4
4
  function matchFilter(block, filter) {
@@ -129,6 +129,7 @@ function indexer(opts) {
129
129
  }
130
130
  return block;
131
131
  }
132
+ const GetBlockError = createException("Failed to load block from the provided `getBlock` function");
132
133
  const public_getFinalizedHeight = async (chain) => {
133
134
  const [[stored], finalizedBlock] = await Promise.all([
134
135
  metadata.blocks.list({ chain }),
@@ -147,26 +148,43 @@ function indexer(opts) {
147
148
  return Math.min(indexerHeight, finalizedHeight);
148
149
  };
149
150
  const public_writeUnfinalizedHeads = async (heads) => {
151
+ if (heads.length === 0) {
152
+ return log.debug("No heads received, returning...");
153
+ }
150
154
  let chain = void 0;
151
155
  for (const head of heads) {
152
156
  if (chain === void 0) {
153
157
  chain = head.chain;
154
158
  }
155
159
  if (!isHexEqual(chain, head.chain)) {
156
- throw new Error(MultipleChainsError);
160
+ throw new Error("Received heads from separate chains");
157
161
  }
158
162
  }
159
163
  if (chain === void 0) {
160
- return;
164
+ throw new Error("Internal error. Expected chain to be defined after checking heads length");
161
165
  }
162
166
  log.debug(`Received ${heads.length} unfinalized heads...`);
163
167
  const blocks_start = Date.now();
164
- const blocks_nullable = await Promise.all(
165
- heads.map(async (head) => {
168
+ const [finalizedBlock, ...blocks_nullable] = await Promise.all([
169
+ getBlock({ chain, number: "finalized" }),
170
+ ...heads.map(async (head) => {
166
171
  return await getBlock({ chain, number: head.number, hash: head.hash });
167
172
  })
168
- );
169
- const blocks = blocks_nullable.filter(nonNullable);
173
+ ]);
174
+ if (finalizedBlock === null) {
175
+ log.debug("Failed to determine finalized height when processing unfinalized heads");
176
+ throw new Error(GetBlockError);
177
+ }
178
+ const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
179
+ const blocks = blocks_nullable.flatMap((block) => {
180
+ if (block === null) {
181
+ return [];
182
+ }
183
+ if (hexToNumber(block.eth_getBlockByNumber.number) <= finalizedHeight) {
184
+ return [];
185
+ }
186
+ return block;
187
+ });
170
188
  if (blocks.length === 0) {
171
189
  return log.debug("All blocks failed to load, aborting early...");
172
190
  }
@@ -259,7 +277,7 @@ function indexer(opts) {
259
277
  chain = head.chain;
260
278
  }
261
279
  if (!isHexEqual(chain, head.chain)) {
262
- throw new Error(MultipleChainsError);
280
+ throw new Error("Received heads from separate chains");
263
281
  }
264
282
  }
265
283
  if (chain === void 0) {
@@ -272,17 +290,15 @@ function indexer(opts) {
272
290
  let previousHead = firstHead;
273
291
  for (const head of remainingHeads) {
274
292
  if (hexToNumber(head.number) !== hexToNumber(previousHead.number) + 1) {
275
- log.debug("Found invalid block number in received heads, aborting...");
276
- throw new Error(InvalidHeadsError);
293
+ throw new Error("Found invalid block number in received heads, aborting...");
277
294
  }
278
295
  if (!isHexEqual(head.parentHash, previousHead.hash)) {
279
- log.debug("Found invalid block hash in received heads, aborting...");
280
- throw new Error(InvalidHeadsError);
296
+ throw new Error("Found invalid block hash in received heads, aborting...");
281
297
  }
282
298
  previousHead = head;
283
299
  }
284
- const [indexerHeight, finalizedBlock] = await Promise.all([
285
- public_getFinalizedHeight(chain),
300
+ const [[unfinalizedMetadataBlock], finalizedBlock] = await Promise.all([
301
+ metadata.blocks.list({ chain }),
286
302
  //
287
303
  getBlock({ chain, number: "finalized" })
288
304
  ]);
@@ -293,16 +309,27 @@ function indexer(opts) {
293
309
  const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
294
310
  for (const head of heads) {
295
311
  if (hexToNumber(head.number) > finalizedHeight) {
296
- throw new Error(InvalidHeadsError);
312
+ throw new Error("Received head greater than chain finalized height");
297
313
  }
298
314
  }
315
+ if (unfinalizedMetadataBlock === void 0) {
316
+ return log.debug("No heads to finalize, returning...");
317
+ }
318
+ const indexerHeight = hexToNumber(unfinalizedMetadataBlock.number) - 1;
299
319
  log.debug(`Indexer height ${indexerHeight}, finalized height ${finalizedHeight}`);
320
+ if (indexerHeight === finalizedHeight) {
321
+ return log.debug("No heads to finalize, returning...");
322
+ }
300
323
  const newHeads = heads.filter((head) => {
301
324
  return hexToNumber(head.number) > indexerHeight;
302
325
  });
303
- if (newHeads.length === 0) {
326
+ const [firstNewHead] = newHeads;
327
+ if (firstNewHead === void 0) {
304
328
  return log.debug("No new heads received, returning...");
305
329
  }
330
+ if (hexToNumber(firstNewHead.number) !== indexerHeight + 1) {
331
+ throw new Error("Client advanced too far. Heads received are greater than the indexer height");
332
+ }
306
333
  for (const head of newHeads) {
307
334
  const processed = await metadata.blocks.get({ chain: head.chain, number: head.number });
308
335
  if (processed.length === 0) {
@@ -328,7 +355,7 @@ function indexer(opts) {
328
355
  block = await getBlock({ chain: head.chain, number: head.number, hash: head.hash });
329
356
  }
330
357
  if (block === null) {
331
- throw new Error(InvalidHeadsError);
358
+ throw new Error("Received unknown head that wasn't canonical");
332
359
  }
333
360
  const promises = events_grouped_by_storage_map.entries().map(async ([storage, grouped_events]) => {
334
361
  const batch = [];
@@ -453,6 +480,7 @@ function indexer(opts) {
453
480
  };
454
481
  return new Proxy(value, createProxyHandler());
455
482
  };
483
+ const IncompleteBlockError = createException("Received block with missing required property");
456
484
  const private_writeEvents = async (params) => {
457
485
  if (all_events.length === 0) return { failures: [] };
458
486
  const relevant_events = all_events.filter((event2) => params.events.includes(event2.id));
@@ -644,6 +672,7 @@ function indexer(opts) {
644
672
  events_grouped_by_storage_map.set(event2.storage, group);
645
673
  return event2;
646
674
  };
675
+ const UnknownMethodError = createException("The requested method does not exist");
647
676
  const handler = async (req) => {
648
677
  let body_buffer;
649
678
  try {
@@ -710,6 +739,7 @@ function indexer(opts) {
710
739
  } catch (error) {
711
740
  const message = getException(error);
712
741
  if (message) {
742
+ log.error(message);
713
743
  return Response.json(
714
744
  { jsonrpc: "2.0", id: json.id, error: { code: 0, message } },
715
745
  //
@@ -728,10 +758,5 @@ function indexer(opts) {
728
758
  };
729
759
  return { fetch: handler, event };
730
760
  }
731
- const InvalidHeadsError = createException("Received invalid finalized heads");
732
- const UnknownMethodError = createException("The requested method does not exist");
733
- const MultipleChainsError = createException("Received heads from separate chains");
734
- const IncompleteBlockError = createException("Received block with missing required property");
735
- const GetBlockError = createException("Failed to load block from the provided `getBlock` function");
736
761
 
737
762
  export { indexer, matchFilter };
package/dist/realtime.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as createLogger, e as iife, h as hexToNumber, f as numberToHex, m as mutex, r as retry, i as isHexEqual } from './shared/univo.Br975GsZ.mjs';
1
+ import { c as createLogger, b as iife, h as hexToNumber, e as numberToHex, m as mutex, r as retry, i as isHexEqual } from './shared/univo.DfCC9G2B.mjs';
2
2
 
3
3
  const MAX_LENGTH = 1e4;
4
4
  function defineBlockchain(opts) {
@@ -160,6 +160,7 @@ function realtime(opts) {
160
160
  let indexerHeight = initialIndexerHeight;
161
161
  const poll = async () => {
162
162
  try {
163
+ log.debug("Polling for finalized height...");
163
164
  const finalizedBlock = await opts.node.request({
164
165
  method: "eth_getBlockByNumber",
165
166
  params: ["finalized", false]
@@ -1,5 +1,5 @@
1
1
  const name = "univo";
2
- const version = "0.2.8";
2
+ const version = "0.2.10";
3
3
 
4
4
  function createLogger(opts) {
5
5
  let prefix = `[${name}@${version}]`;
@@ -73,9 +73,6 @@ function raise(...args) {
73
73
  if (args[0] instanceof Error) throw args[0];
74
74
  throw new Error(...args);
75
75
  }
76
- function nonNullable(value) {
77
- return value !== null && value !== void 0;
78
- }
79
76
  async function compress(input) {
80
77
  const compressed = new Blob([input]).stream().pipeThrough(new CompressionStream("gzip"));
81
78
  return new Response(compressed).arrayBuffer();
@@ -92,4 +89,4 @@ const normalizeHex = (hex, length) => {
92
89
  return `0x${hex.slice(2).toLowerCase().padStart(length, "0")}`;
93
90
  };
94
91
 
95
- export { decoder as a, normalizeHex as b, createLogger as c, decompress as d, iife as e, numberToHex as f, name as g, hexToNumber as h, isHexEqual as i, compress as j, raise as k, mutex as m, nonNullable as n, retry as r, version as v };
92
+ export { decoder as a, iife as b, createLogger as c, decompress as d, numberToHex as e, name as f, compress as g, hexToNumber as h, isHexEqual as i, raise as j, mutex as m, normalizeHex as n, retry as r, version as v };
@@ -2,8 +2,8 @@ import { Rpc } from './rpc.mjs';
2
2
  import './index.mjs';
3
3
  import 'unstorage';
4
4
 
5
- type Protcol = "http" | "wss";
6
- type Transport<R extends Rpc, P extends Protcol = Protcol> = {
5
+ type Protocol = "http" | "wss";
6
+ type Transport<R extends Rpc, P extends Protocol = Protocol> = {
7
7
  /**
8
8
  * The underlying transport protocol.
9
9
  */
@@ -2,8 +2,8 @@ import { Rpc } from './rpc.js';
2
2
  import './index.js';
3
3
  import 'unstorage';
4
4
 
5
- type Protcol = "http" | "wss";
6
- type Transport<R extends Rpc, P extends Protcol = Protcol> = {
5
+ type Protocol = "http" | "wss";
6
+ type Transport<R extends Rpc, P extends Protocol = Protocol> = {
7
7
  /**
8
8
  * The underlying transport protocol.
9
9
  */
@@ -1,7 +1,7 @@
1
1
  import ws from 'ws';
2
2
  import { WebSocket } from 'partysocket';
3
3
  import { c as createException } from './shared/univo.C0flyVee.mjs';
4
- import { j as compress, k as raise, c as createLogger } from './shared/univo.Br975GsZ.mjs';
4
+ import { g as compress, j as raise, c as createLogger } from './shared/univo.DfCC9G2B.mjs';
5
5
 
6
6
  function createSocket(url, opts) {
7
7
  const socket = new WebSocket(url, [], { WebSocket: ws });
@@ -26,7 +26,9 @@ function wss(url, opts = {}) {
26
26
  logger.error(new Error("Socket error", { cause }));
27
27
  },
28
28
  async onOpen() {
29
- if (subscriptions.size === 0) return;
29
+ if (subscriptions.size === 0) {
30
+ return;
31
+ }
30
32
  const params2 = [];
31
33
  for (const [id2, subscription] of subscriptions) {
32
34
  params2.push(subscription.param);
@@ -74,13 +76,24 @@ function wss(url, opts = {}) {
74
76
  }
75
77
  });
76
78
  const request = async (opts2) => {
77
- return await new Promise((resolve) => {
79
+ return await new Promise((resolve, reject) => {
78
80
  const body = Object.assign({ id: id++ }, opts2);
81
+ const timeout = setTimeout(() => {
82
+ requests.delete(body.id);
83
+ reject(new Error("wss request timed out after 30 seconds"));
84
+ }, 30 * 1e3);
79
85
  requests.set(body.id, (data) => {
86
+ clearTimeout(timeout);
80
87
  requests.delete(body.id);
81
88
  resolve(data);
82
89
  });
83
- socket.send(JSON.stringify(body));
90
+ try {
91
+ socket.send(JSON.stringify(body));
92
+ } catch (cause) {
93
+ clearTimeout(timeout);
94
+ requests.delete(body.id);
95
+ reject(new Error(ClientConnectionError, { cause }));
96
+ }
84
97
  });
85
98
  };
86
99
  const subscribe = async (param, handler) => {
@@ -148,7 +161,7 @@ function http(url, opts = {}) {
148
161
  headers.set("Content-Encoding", "gzip");
149
162
  headers.set("Authorization", `Bearer ${opts.signingKey}`);
150
163
  }
151
- const res = await fetch(url, { headers, body, method: "POST" }).catch((cause) => {
164
+ const res = await fetch(url, { headers, body, method: "POST", signal: AbortSignal.timeout(30 * 1e3) }).catch((cause) => {
152
165
  throw new Error(ClientConnectionError, { cause });
153
166
  });
154
167
  const json = await res.json().catch((cause) => {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "univo",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "0.2.8",
5
+ "version": "0.2.10",
6
6
  "sideEffects": false,
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",