univo 0.1.17 → 0.1.19
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 +1 -1
- package/dist/client.mjs +4 -13
- package/dist/index.d.mts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.mjs +20 -18
- package/dist/realtime.d.mts +7 -1
- package/dist/realtime.d.ts +7 -1
- package/dist/realtime.mjs +96 -44
- package/dist/shared/{univo.2F7MFHxq.mjs → univo.DSp9mW7b.mjs} +3 -15
- package/package.json +1 -1
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 './client.mjs';
|
|
8
|
-
import { c as createLogger,
|
|
8
|
+
import { c as createLogger, v as version, l as name } from './shared/univo.DSp9mW7b.mjs';
|
|
9
9
|
|
|
10
10
|
const log = createLogger({ quiet: false });
|
|
11
11
|
const dev = defineCommand({
|
package/dist/client.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as createException, o as compress, k as raise, g as getException } from './shared/univo.DSp9mW7b.mjs';
|
|
2
2
|
|
|
3
|
-
const MIN_GZIP_SIZE = 1024;
|
|
4
3
|
function http(url, opts = {}) {
|
|
5
4
|
return {
|
|
6
5
|
async request({ jsonrpc, id, method, params }) {
|
|
@@ -10,16 +9,9 @@ function http(url, opts = {}) {
|
|
|
10
9
|
headers.set("Content-Type", "application/json");
|
|
11
10
|
if (method.startsWith("private_")) {
|
|
12
11
|
if (opts.signingKey === void 0) throw new Error(ClientUnauthorizedError);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
headers.set("Content-Encoding", "gzip");
|
|
18
|
-
}
|
|
19
|
-
const signature = await getSignature({ key: opts.signingKey, body }).catch((cause) => {
|
|
20
|
-
throw new Error(ClientSignatureError, { cause });
|
|
21
|
-
});
|
|
22
|
-
headers.set("X-Univo-Signature", signature);
|
|
12
|
+
headers.set("Authorization", `Bearer ${opts.signingKey}`);
|
|
13
|
+
body = await compress(body).catch((cause) => raise(ClientCompressionError, { cause }));
|
|
14
|
+
headers.set("Content-Encoding", "gzip");
|
|
23
15
|
}
|
|
24
16
|
const res = await fetch(url, { headers, body, method: "POST" }).catch((cause) => {
|
|
25
17
|
throw new Error(ClientConnectionError, { cause });
|
|
@@ -37,7 +29,6 @@ function http(url, opts = {}) {
|
|
|
37
29
|
const ClientCompressionError = createException("An error occurred when compressing the request");
|
|
38
30
|
const ClientConnectionError = createException("An errored occurred when connecting to the server");
|
|
39
31
|
const ClientResponseError = createException("An error occurred when reading the servers response");
|
|
40
|
-
const ClientSignatureError = createException("An error occurred with the outgoing request signature");
|
|
41
32
|
const ClientUnauthorizedError = createException("Attempted to execute a private method without providing a request signing key");
|
|
42
33
|
|
|
43
34
|
export { http };
|
package/dist/index.d.mts
CHANGED
|
@@ -49,7 +49,6 @@ type Head = {
|
|
|
49
49
|
type Metadata = {
|
|
50
50
|
version: string;
|
|
51
51
|
language: string;
|
|
52
|
-
max_concurrency: number | undefined;
|
|
53
52
|
};
|
|
54
53
|
type Result = {
|
|
55
54
|
status: string;
|
|
@@ -69,10 +68,6 @@ type IndexerOptions<TBlock> = {
|
|
|
69
68
|
* Request signing key
|
|
70
69
|
*/
|
|
71
70
|
signingKey: string;
|
|
72
|
-
/**
|
|
73
|
-
* Maximum number of concurrent connections to open when backfilling blocks
|
|
74
|
-
*/
|
|
75
|
-
maxConcurrency?: number;
|
|
76
71
|
/**
|
|
77
72
|
* This function is to load each block when indexing blocks in realtime. This ensures that all block
|
|
78
73
|
* data processed originates from a trusted RPC source and can be safely relied on.
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,6 @@ type Head = {
|
|
|
49
49
|
type Metadata = {
|
|
50
50
|
version: string;
|
|
51
51
|
language: string;
|
|
52
|
-
max_concurrency: number | undefined;
|
|
53
52
|
};
|
|
54
53
|
type Result = {
|
|
55
54
|
status: string;
|
|
@@ -69,10 +68,6 @@ type IndexerOptions<TBlock> = {
|
|
|
69
68
|
* Request signing key
|
|
70
69
|
*/
|
|
71
70
|
signingKey: string;
|
|
72
|
-
/**
|
|
73
|
-
* Maximum number of concurrent connections to open when backfilling blocks
|
|
74
|
-
*/
|
|
75
|
-
maxConcurrency?: number;
|
|
76
71
|
/**
|
|
77
72
|
* This function is to load each block when indexing blocks in realtime. This ensures that all block
|
|
78
73
|
* data processed originates from a trusted RPC source and can be safely relied on.
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as createLogger,
|
|
1
|
+
import { c as createLogger, d as decompress, a as decoder, b as createException, g as getException, h as hexToNumber, i as isAddressEqual, e as catchException, r as retry, v as version, n as nonNullable, f as getSignature } from './shared/univo.DSp9mW7b.mjs';
|
|
2
2
|
|
|
3
3
|
function matchFilter(block, filter) {
|
|
4
4
|
if (!chainValid(block, filter)) return false;
|
|
@@ -46,8 +46,7 @@ function indexer(opts) {
|
|
|
46
46
|
const private_getMetadata = async () => {
|
|
47
47
|
return {
|
|
48
48
|
version,
|
|
49
|
-
language: "javascript"
|
|
50
|
-
max_concurrency: opts.maxConcurrency
|
|
49
|
+
language: "javascript"
|
|
51
50
|
};
|
|
52
51
|
};
|
|
53
52
|
const private_getEvents = async () => {
|
|
@@ -448,24 +447,27 @@ function indexer(opts) {
|
|
|
448
447
|
{ status: 400 }
|
|
449
448
|
);
|
|
450
449
|
}
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
} catch {
|
|
450
|
+
const authorization = req.headers.get("Authorization");
|
|
451
|
+
const authenticated = authorization === `Bearer ${opts.signingKey}`;
|
|
452
|
+
let body_string;
|
|
453
|
+
if (req.headers.get("Content-Encoding") === "gzip") {
|
|
454
|
+
if (!authorization) {
|
|
457
455
|
return Response.json(
|
|
458
|
-
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "
|
|
456
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "No bearer token provided" } },
|
|
457
|
+
//
|
|
458
|
+
{ status: 400 }
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
if (!authenticated) {
|
|
462
|
+
return Response.json(
|
|
463
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid bearer token" } },
|
|
459
464
|
//
|
|
460
465
|
{ status: 400 }
|
|
461
466
|
);
|
|
462
467
|
}
|
|
463
|
-
}
|
|
464
|
-
let body_string;
|
|
465
|
-
if (req.headers.get("Content-Encoding") === "gzip") {
|
|
466
468
|
body_string = await decompress(body_buffer);
|
|
467
469
|
} else {
|
|
468
|
-
body_string =
|
|
470
|
+
body_string = decoder.decode(body_buffer);
|
|
469
471
|
}
|
|
470
472
|
let json;
|
|
471
473
|
try {
|
|
@@ -478,16 +480,16 @@ function indexer(opts) {
|
|
|
478
480
|
);
|
|
479
481
|
}
|
|
480
482
|
if (json.method.startsWith("private_")) {
|
|
481
|
-
if (!
|
|
483
|
+
if (!authorization) {
|
|
482
484
|
return Response.json(
|
|
483
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "No
|
|
485
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "No bearer token provided" } },
|
|
484
486
|
//
|
|
485
487
|
{ status: 400 }
|
|
486
488
|
);
|
|
487
489
|
}
|
|
488
|
-
if (
|
|
490
|
+
if (authorization !== `Bearer ${opts.signingKey}`) {
|
|
489
491
|
return Response.json(
|
|
490
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Invalid
|
|
492
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Invalid bearer token" } },
|
|
491
493
|
//
|
|
492
494
|
{ status: 400 }
|
|
493
495
|
);
|
package/dist/realtime.d.mts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Transport -----------------------------------------------------------------------------------------------------------------------------------
|
|
3
3
|
*/
|
|
4
4
|
type Transport = {
|
|
5
|
+
/**
|
|
6
|
+
* Performs as JSON RPC request and returns the result
|
|
7
|
+
*/
|
|
5
8
|
request(opts: {
|
|
6
9
|
method: string;
|
|
7
10
|
params: any[];
|
|
8
11
|
}): Promise<any>;
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Subscribes to specific event types and returns a function to unsubscribe.
|
|
14
|
+
*/
|
|
15
|
+
subscribe(param: "newHeads", handler: (message: any) => void): Promise<{
|
|
10
16
|
unsubscribe(): Promise<void>;
|
|
11
17
|
}>;
|
|
12
18
|
};
|
package/dist/realtime.d.ts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Transport -----------------------------------------------------------------------------------------------------------------------------------
|
|
3
3
|
*/
|
|
4
4
|
type Transport = {
|
|
5
|
+
/**
|
|
6
|
+
* Performs as JSON RPC request and returns the result
|
|
7
|
+
*/
|
|
5
8
|
request(opts: {
|
|
6
9
|
method: string;
|
|
7
10
|
params: any[];
|
|
8
11
|
}): Promise<any>;
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Subscribes to specific event types and returns a function to unsubscribe.
|
|
14
|
+
*/
|
|
15
|
+
subscribe(param: "newHeads", handler: (message: any) => void): Promise<{
|
|
10
16
|
unsubscribe(): Promise<void>;
|
|
11
17
|
}>;
|
|
12
18
|
};
|
package/dist/realtime.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import WS from 'ws';
|
|
2
2
|
import { WebSocket } from 'partysocket';
|
|
3
3
|
import { http } from './client.mjs';
|
|
4
|
-
import { c as createLogger, r as retry, h as hexToNumber,
|
|
4
|
+
import { j as assert, c as createLogger, r as retry, h as hexToNumber, k as raise, m as mutex } from './shared/univo.DSp9mW7b.mjs';
|
|
5
5
|
|
|
6
6
|
function createSocket(url, opts) {
|
|
7
7
|
const ws = new WebSocket(url, [], { WebSocket: WS });
|
|
@@ -13,67 +13,119 @@ function createSocket(url, opts) {
|
|
|
13
13
|
return ws;
|
|
14
14
|
}
|
|
15
15
|
function defineTransport(url) {
|
|
16
|
+
assert(url.startsWith("wss://"), "Websocket connections must start with `wss://`");
|
|
16
17
|
let id = 0;
|
|
17
18
|
const requests = /* @__PURE__ */ new Map();
|
|
19
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
20
|
+
const params = /* @__PURE__ */ new Map();
|
|
21
|
+
const socket = createSocket(url, {
|
|
22
|
+
async onError(cause) {
|
|
23
|
+
console.error(new Error("Socket error", { cause }));
|
|
24
|
+
},
|
|
25
|
+
async onOpen() {
|
|
26
|
+
if (subscriptions.size === 0) return;
|
|
27
|
+
const params2 = [];
|
|
28
|
+
for (const [id2, subscription] of subscriptions) {
|
|
29
|
+
params2.push(subscription.param);
|
|
30
|
+
request({ method: "eth_unsubscribe", params: [id2] }).catch(() => {
|
|
31
|
+
console.warn("Failed to cancel old susbcription");
|
|
32
|
+
});
|
|
33
|
+
subscriptions.delete(id2);
|
|
34
|
+
}
|
|
35
|
+
for (const param of params2) {
|
|
36
|
+
request({ method: "eth_subscribe", params: [param] }).then((id2) => {
|
|
37
|
+
subscriptions.set(id2, { param, latest: Date.now() });
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async onMessage(event) {
|
|
42
|
+
const data = JSON.parse(event.data);
|
|
43
|
+
if (data.method === "eth_subscription") {
|
|
44
|
+
const subscription = subscriptions.get(data.params.subscription);
|
|
45
|
+
if (subscription === void 0) {
|
|
46
|
+
return await request({ method: "eth_unsubscribe", params: [data.params.subscription] }).catch(() => {
|
|
47
|
+
console.warn("Failed to unsubscribe stale subscription");
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const handlers = params.get(subscription.param);
|
|
51
|
+
if (handlers === void 0) {
|
|
52
|
+
return await request({ method: "eth_unsubscribe", params: [data.params.subscription] }).catch(() => {
|
|
53
|
+
console.warn("Failed to unsubscribe stale subscription");
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
for (const handler2 of handlers) {
|
|
57
|
+
try {
|
|
58
|
+
handler2(data.params.result);
|
|
59
|
+
} catch (cause) {
|
|
60
|
+
console.error(new Error("Handler error", { cause }));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
subscription.latest = Date.now();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const handler = requests.get(data.id);
|
|
67
|
+
if (handler === void 0) {
|
|
68
|
+
return console.warn(`Received unknown response for request id ${data.id}...`);
|
|
69
|
+
}
|
|
70
|
+
return handler(data.result);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
18
73
|
async function request(opts) {
|
|
19
74
|
return await new Promise((resolve) => {
|
|
20
75
|
const body = Object.assign({ id: id++ }, opts);
|
|
21
|
-
requests.set(body.id, {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
resolve(data);
|
|
25
|
-
}
|
|
76
|
+
requests.set(body.id, (data) => {
|
|
77
|
+
requests.delete(body.id);
|
|
78
|
+
resolve(data);
|
|
26
79
|
});
|
|
27
80
|
socket.send(JSON.stringify(body));
|
|
28
81
|
});
|
|
29
82
|
}
|
|
30
|
-
const subscriptions = /* @__PURE__ */ new Map();
|
|
31
83
|
async function subscribe(param, handler) {
|
|
32
|
-
const
|
|
33
|
-
|
|
84
|
+
const handlers = params.get(param);
|
|
85
|
+
if (handlers === void 0) {
|
|
86
|
+
const handlers2 = (/* @__PURE__ */ new Set()).add(handler);
|
|
87
|
+
params.set(param, handlers2);
|
|
88
|
+
const id2 = await request({ method: "eth_subscribe", params: [param] }).catch((cause) => {
|
|
89
|
+
throw new Error(`Failed to initialise subscription for param ${param}`, { cause });
|
|
90
|
+
});
|
|
91
|
+
subscriptions.set(id2, { param, latest: Date.now() });
|
|
92
|
+
} else {
|
|
93
|
+
handlers.add(handler);
|
|
94
|
+
}
|
|
34
95
|
return {
|
|
35
96
|
async unsubscribe() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
const socket = createSocket(url, {
|
|
43
|
-
onError(cause) {
|
|
44
|
-
console.error(new Error("Socket error", { cause }));
|
|
45
|
-
},
|
|
46
|
-
onOpen() {
|
|
47
|
-
if (subscriptions.size) {
|
|
48
|
-
const handlers = [];
|
|
49
|
-
for (const [id2, subscription] of subscriptions) {
|
|
50
|
-
handlers.push(subscription);
|
|
51
|
-
request({ method: "eth_unsubscribe", params: [id2] }).catch(() => {
|
|
52
|
-
});
|
|
53
|
-
subscriptions.delete(id2);
|
|
97
|
+
const handlers2 = params.get(param);
|
|
98
|
+
if (handlers2 === void 0) {
|
|
99
|
+
return console.warn("Param already unsubscribed...");
|
|
54
100
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
handlers2.delete(handler);
|
|
102
|
+
if (handlers2.size === 0) {
|
|
103
|
+
await request({ method: "eth_unsubscribe", params: [id] }).catch(() => {
|
|
104
|
+
console.warn("Failed to unsubscribe unused subscription");
|
|
58
105
|
});
|
|
59
106
|
}
|
|
60
107
|
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function healthcheck() {
|
|
111
|
+
const params2 = [];
|
|
112
|
+
for (const [id2, subscription] of subscriptions) {
|
|
113
|
+
if (Date.now() - subscription.latest > 60 * 1e3) {
|
|
114
|
+
params2.push(subscription.param);
|
|
115
|
+
request({ method: "eth_unsubscribe", params: [id2] }).catch(() => {
|
|
116
|
+
console.warn("Failed to cancel old susbcription");
|
|
117
|
+
});
|
|
118
|
+
subscriptions.delete(id2);
|
|
119
|
+
console.warn(`Subscription ${id2} failed health check, reinitialising..`);
|
|
72
120
|
}
|
|
73
|
-
const _request = requests.get(data.id);
|
|
74
|
-
if (_request) _request.handler(data.result);
|
|
75
121
|
}
|
|
76
|
-
|
|
122
|
+
for (const param of params2) {
|
|
123
|
+
request({ method: "eth_subscribe", params: [param] }).then((id2) => {
|
|
124
|
+
subscriptions.set(id2, { param, latest: Date.now() });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
setInterval(healthcheck, 60 * 1e3);
|
|
77
129
|
return { request, subscribe };
|
|
78
130
|
}
|
|
79
131
|
function defineBlockchain(opts) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const name = "univo";
|
|
2
|
-
const version = "0.1.
|
|
2
|
+
const version = "0.1.19";
|
|
3
3
|
|
|
4
4
|
function createException(message) {
|
|
5
5
|
return `univo:${message}`;
|
|
@@ -63,19 +63,6 @@ async function getSignature(opts) {
|
|
|
63
63
|
const hmac = await crypto.subtle.sign("HMAC", key, opts.body);
|
|
64
64
|
return [...new Uint8Array(hmac)].map((binary) => binary.toString(16).padStart(2, "0")).join("");
|
|
65
65
|
}
|
|
66
|
-
async function verifySignature(opts) {
|
|
67
|
-
const keyData = encoder.encode(opts.key);
|
|
68
|
-
const key = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
|
|
69
|
-
const pairs = opts.signature.match(/[\da-f]{2}/gi);
|
|
70
|
-
if (!pairs) return false;
|
|
71
|
-
if (typeof opts.body === "string") {
|
|
72
|
-
const dataToAuthenticate = encoder.encode(opts.body);
|
|
73
|
-
const hmac2 = new Uint8Array(pairs.map((byte) => Number.parseInt(byte, 16))).buffer;
|
|
74
|
-
return await crypto.subtle.verify("HMAC", key, hmac2, dataToAuthenticate);
|
|
75
|
-
}
|
|
76
|
-
const hmac = new Uint8Array(pairs.map((byte) => Number.parseInt(byte, 16))).buffer;
|
|
77
|
-
return await crypto.subtle.verify("HMAC", key, hmac, opts.body);
|
|
78
|
-
}
|
|
79
66
|
function hexToNumber(hex) {
|
|
80
67
|
return Number.parseInt(hex, 16);
|
|
81
68
|
}
|
|
@@ -115,5 +102,6 @@ async function decompress(input) {
|
|
|
115
102
|
const decompressed = new Blob([input]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
116
103
|
return new Response(decompressed).text();
|
|
117
104
|
}
|
|
105
|
+
const decoder = new TextDecoder();
|
|
118
106
|
|
|
119
|
-
export {
|
|
107
|
+
export { decoder as a, createException as b, createLogger as c, decompress as d, catchException as e, getSignature as f, getException as g, hexToNumber as h, isAddressEqual as i, assert as j, raise as k, name as l, mutex as m, nonNullable as n, compress as o, retry as r, version as v };
|