opnet 1.7.21 → 1.7.22
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/browser/229.index.js +1 -0
- package/browser/_version.d.ts +1 -1
- package/browser/block/Block.d.ts +8 -2
- package/browser/cache/LRUCaching.d.ts +7 -0
- package/browser/cache/P2OPCache.d.ts +2 -0
- package/browser/fetch/fetch-browser.d.ts +1 -0
- package/browser/fetch/fetch.d.ts +2 -2
- package/browser/fetch/fetcher-type.d.ts +4 -0
- package/browser/index.js +1 -1
- package/browser/providers/JSONRpcProvider.d.ts +5 -2
- package/browser/threading/JSONThreader.d.ts +20 -0
- package/browser/threading/SharedThreader.d.ts +36 -0
- package/browser/threading/WorkerCreator.d.ts +3 -0
- package/browser/threading/interfaces/IThread.d.ts +28 -0
- package/browser/threading/worker-scripts/JSONWorker.d.ts +2 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/block/Block.d.ts +8 -2
- package/build/block/Block.js +24 -10
- package/build/cache/LRUCaching.d.ts +7 -0
- package/build/cache/LRUCaching.js +28 -0
- package/build/cache/P2OPCache.d.ts +2 -0
- package/build/cache/P2OPCache.js +19 -0
- package/build/fetch/fetch.d.ts +2 -2
- package/build/fetch/fetch.js +9 -5
- package/build/fetch/fetcher-type.d.ts +4 -0
- package/build/providers/JSONRpcProvider.d.ts +5 -2
- package/build/providers/JSONRpcProvider.js +22 -6
- package/build/threading/JSONThreader.d.ts +20 -0
- package/build/threading/JSONThreader.js +38 -0
- package/build/threading/SharedThreader.d.ts +36 -0
- package/build/threading/SharedThreader.js +184 -0
- package/build/threading/WorkerCreator.d.ts +3 -0
- package/build/threading/WorkerCreator.js +57 -0
- package/build/threading/interfaces/IThread.d.ts +28 -0
- package/build/threading/interfaces/IThread.js +1 -0
- package/build/threading/worker-scripts/JSONWorker.d.ts +2 -0
- package/build/threading/worker-scripts/JSONWorker.js +76 -0
- package/build/transactions/metadata/TransactionReceipt.js +4 -8
- package/package.json +3 -2
- package/src/_version.ts +1 -1
- package/src/block/Block.ts +32 -14
- package/src/cache/LRUCaching.ts +30 -0
- package/src/cache/P2OPCache.ts +22 -0
- package/src/fetch/fetch-browser.js +3 -1
- package/src/fetch/fetch.ts +14 -9
- package/src/fetch/fetcher-type.ts +5 -0
- package/src/providers/JSONRpcProvider.ts +24 -10
- package/src/threading/JSONThreader.ts +62 -0
- package/src/threading/SharedThreader.ts +255 -0
- package/src/threading/WorkerCreator.ts +67 -0
- package/src/threading/interfaces/IThread.ts +38 -0
- package/src/threading/worker-scripts/JSONWorker.ts +78 -0
- package/src/transactions/metadata/TransactionReceipt.ts +5 -14
- package/webpack.config.js +2 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
2
|
+
export async function createWorker(workerScript) {
|
|
3
|
+
if (isNode) {
|
|
4
|
+
const { Worker } = await import('worker_threads');
|
|
5
|
+
const worker = new Worker(workerScript, { eval: true });
|
|
6
|
+
worker.on('error', (err) => {
|
|
7
|
+
console.error('[WorkerCreator] Worker error:', err);
|
|
8
|
+
});
|
|
9
|
+
let messageCallback = null;
|
|
10
|
+
worker.on('message', (msg) => {
|
|
11
|
+
if (messageCallback)
|
|
12
|
+
messageCallback(msg);
|
|
13
|
+
});
|
|
14
|
+
worker.unref();
|
|
15
|
+
return {
|
|
16
|
+
postMessage: (msg, transferables) => {
|
|
17
|
+
if (transferables && transferables.length > 0) {
|
|
18
|
+
worker.postMessage(msg, transferables);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
worker.postMessage(msg);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
onMessage: (callback) => {
|
|
25
|
+
messageCallback = callback;
|
|
26
|
+
},
|
|
27
|
+
terminate: async () => {
|
|
28
|
+
await worker.terminate();
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const blob = new Blob([workerScript], { type: 'application/javascript' });
|
|
34
|
+
const url = URL.createObjectURL(blob);
|
|
35
|
+
const worker = new Worker(url);
|
|
36
|
+
worker.onerror = (err) => {
|
|
37
|
+
console.error('[WorkerCreator] Worker error:', err);
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
postMessage: (msg, transferables) => {
|
|
41
|
+
if (transferables && transferables.length > 0) {
|
|
42
|
+
worker.postMessage(msg, transferables);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
worker.postMessage(msg);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
onMessage: (callback) => {
|
|
49
|
+
worker.onmessage = (e) => callback(e.data);
|
|
50
|
+
},
|
|
51
|
+
terminate: () => {
|
|
52
|
+
worker.terminate();
|
|
53
|
+
URL.revokeObjectURL(url);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface TaskMessage<TOp extends string = string, TData = unknown> {
|
|
2
|
+
id: number;
|
|
3
|
+
op: TOp;
|
|
4
|
+
data: TData;
|
|
5
|
+
}
|
|
6
|
+
export interface ResultMessage<TResult = unknown> {
|
|
7
|
+
id: number;
|
|
8
|
+
result?: TResult;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PendingTask<TResult = unknown> {
|
|
12
|
+
resolve: (value: TResult) => void;
|
|
13
|
+
reject: (error: Error) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface QueuedTask<TOp extends string = string, TData = unknown, TResult = unknown> extends PendingTask<TResult> {
|
|
16
|
+
op: TOp;
|
|
17
|
+
data: TData;
|
|
18
|
+
transferables?: ArrayBuffer[];
|
|
19
|
+
}
|
|
20
|
+
export interface ThreaderOptions {
|
|
21
|
+
poolSize?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface UniversalWorker<TOp extends string = string, TData = unknown, TResult = unknown> {
|
|
24
|
+
postMessage(msg: TaskMessage<TOp, TData>, transferables?: readonly Transferable[]): void;
|
|
25
|
+
onMessage(callback: (msg: ResultMessage<TResult>) => void): void;
|
|
26
|
+
terminate(): void | Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export type WorkerScript = string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const jsonWorkerScript = `
|
|
2
|
+
(async () => {
|
|
3
|
+
let parentPort = null;
|
|
4
|
+
let isBrowser = typeof self !== 'undefined' && typeof self.onmessage !== 'undefined';
|
|
5
|
+
|
|
6
|
+
if (!isBrowser) {
|
|
7
|
+
const wt = await import('node:worker_threads');
|
|
8
|
+
parentPort = wt.parentPort;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const send = (msg) => {
|
|
12
|
+
if (isBrowser) {
|
|
13
|
+
postMessage(msg);
|
|
14
|
+
} else if (parentPort) {
|
|
15
|
+
parentPort.postMessage(msg);
|
|
16
|
+
} else {
|
|
17
|
+
console.error('[JsonWorker] No way to send message:', msg);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const handler = (e) => {
|
|
22
|
+
// Node: e is the message directly
|
|
23
|
+
// Browser: e is MessageEvent, e.data is the message
|
|
24
|
+
const msg = isBrowser ? e.data : e;
|
|
25
|
+
|
|
26
|
+
// Handle raw ArrayBuffer (shouldn't happen but defensive)
|
|
27
|
+
if (msg instanceof ArrayBuffer) {
|
|
28
|
+
console.error('[JsonWorker] Received raw ArrayBuffer instead of message object. Length:', msg.byteLength);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { id, op, data } = msg ?? {};
|
|
33
|
+
|
|
34
|
+
if (typeof id !== 'number') {
|
|
35
|
+
console.error('[JsonWorker] Invalid message, missing id. Got:', msg);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
let result;
|
|
41
|
+
if (op === 'parse') {
|
|
42
|
+
if (data === undefined || data === null) {
|
|
43
|
+
throw new Error('No data provided for parse operation');
|
|
44
|
+
}
|
|
45
|
+
const text = data instanceof ArrayBuffer
|
|
46
|
+
? new TextDecoder().decode(data)
|
|
47
|
+
: data;
|
|
48
|
+
result = JSON.parse(text);
|
|
49
|
+
} else if (op === 'stringify') {
|
|
50
|
+
result = JSON.stringify(data);
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error('Unknown operation: ' + op);
|
|
53
|
+
}
|
|
54
|
+
send({ id, result });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
57
|
+
console.error('[JsonWorker] Error processing task ' + id + ':', error);
|
|
58
|
+
send({ id, error });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (isBrowser) {
|
|
63
|
+
self.onmessage = handler;
|
|
64
|
+
self.onerror = (err) => {
|
|
65
|
+
console.error('[JsonWorker] Uncaught error:', err);
|
|
66
|
+
};
|
|
67
|
+
} else if (parentPort) {
|
|
68
|
+
parentPort.on('message', handler);
|
|
69
|
+
parentPort.on('error', (err) => {
|
|
70
|
+
console.error('[JsonWorker] Uncaught error:', err);
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
console.error('[JsonWorker] No parentPort and not browser - worker cannot receive messages');
|
|
74
|
+
}
|
|
75
|
+
})();
|
|
76
|
+
`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NetEvent } from '@btc-vision/transaction';
|
|
2
|
+
import { getP2op } from '../../cache/P2OPCache.js';
|
|
2
3
|
import { CallResult } from '../../contracts/CallResult.js';
|
|
3
4
|
export class TransactionReceipt {
|
|
4
5
|
receipt;
|
|
@@ -26,8 +27,7 @@ export class TransactionReceipt {
|
|
|
26
27
|
const parsedEvents = {};
|
|
27
28
|
if (!Array.isArray(events)) {
|
|
28
29
|
for (const [key, value] of Object.entries(events)) {
|
|
29
|
-
const
|
|
30
|
-
const caP2op = ca.p2op(network);
|
|
30
|
+
const caP2op = getP2op(key, network);
|
|
31
31
|
const v = value.map((event) => {
|
|
32
32
|
return this.decodeEvent(event);
|
|
33
33
|
});
|
|
@@ -39,11 +39,7 @@ export class TransactionReceipt {
|
|
|
39
39
|
for (const event of events) {
|
|
40
40
|
const parsedEvent = this.decodeEvent(event);
|
|
41
41
|
const contractAddress = event.contractAddress;
|
|
42
|
-
const
|
|
43
|
-
const caP2op = ca.p2op(network);
|
|
44
|
-
if (!parsedEvents[caP2op]) {
|
|
45
|
-
parsedEvents[caP2op] = [];
|
|
46
|
-
}
|
|
42
|
+
const caP2op = getP2op(contractAddress, network);
|
|
47
43
|
if (!parsedEvents[caP2op]) {
|
|
48
44
|
parsedEvents[caP2op] = [];
|
|
49
45
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opnet",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.22",
|
|
5
5
|
"author": "OP_NET",
|
|
6
6
|
"description": "The perfect library for building Bitcoin-based applications.",
|
|
7
7
|
"engines": {
|
|
@@ -100,8 +100,9 @@
|
|
|
100
100
|
"@btc-vision/bip32": "^6.0.3",
|
|
101
101
|
"@btc-vision/bitcoin": "^6.4.11",
|
|
102
102
|
"@btc-vision/bitcoin-rpc": "^1.0.5",
|
|
103
|
+
"@btc-vision/logger": "^1.0.7",
|
|
103
104
|
"@btc-vision/post-quantum": "^0.5.3",
|
|
104
|
-
"@btc-vision/transaction": "^1.7.
|
|
105
|
+
"@btc-vision/transaction": "^1.7.22",
|
|
105
106
|
"@noble/hashes": "^1.8.0",
|
|
106
107
|
"bignumber.js": "^9.3.1",
|
|
107
108
|
"buffer": "^6.0.3",
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.7.
|
|
1
|
+
export const version = '1.7.22';
|
package/src/block/Block.ts
CHANGED
|
@@ -42,13 +42,14 @@ export class Block implements Omit<IBlock, 'gasUsed' | 'ema' | 'baseGas' | 'depl
|
|
|
42
42
|
|
|
43
43
|
public readonly checksumProofs: BlockHeaderChecksumProof;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
private readonly _rawBlock: IBlock;
|
|
46
|
+
private readonly _network: Network;
|
|
47
47
|
|
|
48
48
|
constructor(block: IBlock, network: Network) {
|
|
49
|
-
if (!block)
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (!block) throw new Error('Invalid block.');
|
|
50
|
+
|
|
51
|
+
this._rawBlock = block;
|
|
52
|
+
this._network = network;
|
|
52
53
|
|
|
53
54
|
this.height = BigInt(block.height.toString());
|
|
54
55
|
|
|
@@ -78,16 +79,33 @@ export class Block implements Omit<IBlock, 'gasUsed' | 'ema' | 'baseGas' | 'depl
|
|
|
78
79
|
this.receiptRoot = block.receiptRoot;
|
|
79
80
|
|
|
80
81
|
this.checksumProofs = block.checksumProofs;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private _transactions?: TransactionBase<OPNetTransactionTypes>[];
|
|
85
|
+
|
|
86
|
+
public get transactions(): TransactionBase<OPNetTransactionTypes>[] {
|
|
87
|
+
if (!this._transactions) {
|
|
88
|
+
this._transactions = TransactionParser.parseTransactions(
|
|
89
|
+
this._rawBlock.transactions as ITransaction[],
|
|
90
|
+
this._network,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return this._transactions;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private _deployments?: Address[];
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
public get deployments(): Address[] {
|
|
99
|
+
if (!this._deployments) {
|
|
100
|
+
this._deployments = this._rawBlock.deployments
|
|
101
|
+
? this._rawBlock.deployments.map((address) => Address.fromString(address))
|
|
102
|
+
: [];
|
|
103
|
+
}
|
|
104
|
+
return this._deployments;
|
|
105
|
+
}
|
|
86
106
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
})
|
|
91
|
-
: [];
|
|
107
|
+
// For cases where you need raw without parsing
|
|
108
|
+
public get rawTransactions(): ITransaction[] {
|
|
109
|
+
return this._rawBlock.transactions as ITransaction[];
|
|
92
110
|
}
|
|
93
111
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class LRUCache<K, V> {
|
|
2
|
+
private readonly cache: Map<K, V>;
|
|
3
|
+
private readonly maxSize: number;
|
|
4
|
+
|
|
5
|
+
constructor(maxSize: number) {
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
this.maxSize = maxSize;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public get(key: K): V | undefined {
|
|
11
|
+
const value = this.cache.get(key);
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
this.cache.delete(key);
|
|
14
|
+
this.cache.set(key, value);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public set(key: K, value: V): void {
|
|
20
|
+
if (this.cache.has(key)) {
|
|
21
|
+
this.cache.delete(key);
|
|
22
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
23
|
+
const firstKey = this.cache.keys().next().value;
|
|
24
|
+
if (firstKey !== undefined) {
|
|
25
|
+
this.cache.delete(firstKey);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this.cache.set(key, value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
+
import { Address } from '@btc-vision/transaction';
|
|
3
|
+
import { LRUCache } from './LRUCaching.js';
|
|
4
|
+
|
|
5
|
+
const P2OP_CACHE_MAX_SIZE = 5_000;
|
|
6
|
+
const p2opCache = new LRUCache<string, string>(P2OP_CACHE_MAX_SIZE);
|
|
7
|
+
const addressCache = new LRUCache<string, Address>(P2OP_CACHE_MAX_SIZE);
|
|
8
|
+
|
|
9
|
+
export const getP2op = (rawAddress: string, network: Network): string => {
|
|
10
|
+
const cacheKey = `${network.bip32}:${network.pubKeyHash}:${network.bech32}:${rawAddress}`;
|
|
11
|
+
let cached = p2opCache.get(cacheKey);
|
|
12
|
+
if (cached === undefined) {
|
|
13
|
+
let addr = addressCache.get(rawAddress);
|
|
14
|
+
if (addr === undefined) {
|
|
15
|
+
addr = Address.fromString(rawAddress);
|
|
16
|
+
addressCache.set(rawAddress, addr);
|
|
17
|
+
}
|
|
18
|
+
cached = addr.p2op(network);
|
|
19
|
+
p2opCache.set(cacheKey, cached);
|
|
20
|
+
}
|
|
21
|
+
return cached;
|
|
22
|
+
};
|
package/src/fetch/fetch.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import pLimit from 'p-limit';
|
|
2
|
-
import { Agent, fetch as undiciFetch,
|
|
3
|
-
import {
|
|
2
|
+
import { Agent, fetch as undiciFetch, RequestInit, Response } from 'undici';
|
|
3
|
+
import { FetcherWithCleanup } from './fetcher-type.js';
|
|
4
4
|
|
|
5
|
-
export function getFetcher(configs: Agent.Options):
|
|
5
|
+
export function getFetcher(configs: Agent.Options): FetcherWithCleanup {
|
|
6
6
|
const agent = new Agent(configs);
|
|
7
|
-
|
|
8
|
-
setGlobalDispatcher(agent);
|
|
9
|
-
|
|
10
7
|
const limit = pLimit(500);
|
|
11
8
|
|
|
12
|
-
async function limitedFetch(
|
|
13
|
-
|
|
9
|
+
async function limitedFetch(
|
|
10
|
+
input: Parameters<typeof undiciFetch>[0],
|
|
11
|
+
init?: RequestInit,
|
|
12
|
+
): Promise<Response> {
|
|
13
|
+
return limit(() => undiciFetch(input, { ...init, dispatcher: agent }));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
fetch: limitedFetch,
|
|
18
|
+
close: async () => {
|
|
19
|
+
await agent.close();
|
|
20
|
+
},
|
|
21
|
+
};
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export default getFetcher;
|
|
@@ -3,7 +3,8 @@ import Agent from 'undici/types/agent.js';
|
|
|
3
3
|
import { Response } from 'undici/types/fetch';
|
|
4
4
|
|
|
5
5
|
import getFetcher from '../fetch/fetch.js';
|
|
6
|
-
import { Fetcher } from '../fetch/fetcher-type.js';
|
|
6
|
+
import { Fetcher, FetcherWithCleanup } from '../fetch/fetcher-type.js';
|
|
7
|
+
import { jsonThreader } from '../threading/JSONThreader.js';
|
|
7
8
|
import { AbstractRpcProvider } from './AbstractRpcProvider.js';
|
|
8
9
|
import { JsonRpcPayload } from './interfaces/JSONRpc.js';
|
|
9
10
|
import { JsonRpcCallResult, JsonRpcError, JsonRpcResult } from './interfaces/JSONRpcResult.js';
|
|
@@ -16,6 +17,8 @@ import { JsonRpcCallResult, JsonRpcError, JsonRpcResult } from './interfaces/JSO
|
|
|
16
17
|
export class JSONRpcProvider extends AbstractRpcProvider {
|
|
17
18
|
public readonly url: string;
|
|
18
19
|
|
|
20
|
+
private _fetcherWithCleanup: FetcherWithCleanup | undefined;
|
|
21
|
+
|
|
19
22
|
constructor(
|
|
20
23
|
url: string,
|
|
21
24
|
network: Network,
|
|
@@ -27,20 +30,25 @@ export class JSONRpcProvider extends AbstractRpcProvider {
|
|
|
27
30
|
pipelining: 2, // max pipelining per server
|
|
28
31
|
},
|
|
29
32
|
private useRESTAPI: boolean = true,
|
|
33
|
+
private readonly useThreadedParsing: boolean = true,
|
|
30
34
|
) {
|
|
31
35
|
super(network);
|
|
32
36
|
|
|
33
37
|
this.url = this.providerUrl(url);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
private _fetcher: Fetcher | undefined;
|
|
37
|
-
|
|
38
40
|
private get fetcher(): Fetcher {
|
|
39
|
-
if (!this.
|
|
40
|
-
this.
|
|
41
|
+
if (!this._fetcherWithCleanup) {
|
|
42
|
+
this._fetcherWithCleanup = getFetcher(this.fetcherConfigurations);
|
|
41
43
|
}
|
|
44
|
+
return this._fetcherWithCleanup.fetch;
|
|
45
|
+
}
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
public async close(): Promise<void> {
|
|
48
|
+
if (this._fetcherWithCleanup) {
|
|
49
|
+
await this._fetcherWithCleanup.close();
|
|
50
|
+
this._fetcherWithCleanup = undefined;
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
/**
|
|
@@ -58,11 +66,9 @@ export class JSONRpcProvider extends AbstractRpcProvider {
|
|
|
58
66
|
* @returns {Promise<JsonRpcCallResult>} - The result of the call
|
|
59
67
|
*/
|
|
60
68
|
public async _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcCallResult> {
|
|
61
|
-
// Create an AbortController instance
|
|
62
69
|
const controller = new AbortController();
|
|
63
70
|
const { signal } = controller;
|
|
64
71
|
|
|
65
|
-
// Start a timer that will abort the fetch after the timeout period
|
|
66
72
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
67
73
|
|
|
68
74
|
const params = {
|
|
@@ -70,7 +76,6 @@ export class JSONRpcProvider extends AbstractRpcProvider {
|
|
|
70
76
|
headers: {
|
|
71
77
|
'Content-Type': 'application/json',
|
|
72
78
|
'User-Agent': 'OPNET/1.0',
|
|
73
|
-
//'Accept-Encoding': 'gzip, deflate, br',
|
|
74
79
|
Accept: 'application/json',
|
|
75
80
|
'Accept-Charset': 'utf-8',
|
|
76
81
|
'Accept-Language': 'en-US',
|
|
@@ -87,7 +92,7 @@ export class JSONRpcProvider extends AbstractRpcProvider {
|
|
|
87
92
|
throw new Error(`Failed to fetch: ${resp.statusText}`);
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
const fetchedData =
|
|
95
|
+
const fetchedData = await this.parseResponse<JsonRpcResult | JsonRpcError>(resp);
|
|
91
96
|
if (!fetchedData) {
|
|
92
97
|
throw new Error('No data fetched');
|
|
93
98
|
}
|
|
@@ -118,4 +123,13 @@ export class JSONRpcProvider extends AbstractRpcProvider {
|
|
|
118
123
|
return `${url}/api/v1/json-rpc`;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
126
|
+
|
|
127
|
+
private async parseResponse<T>(resp: Response): Promise<T> {
|
|
128
|
+
if (this.useThreadedParsing) {
|
|
129
|
+
const buffer = await resp.arrayBuffer();
|
|
130
|
+
return jsonThreader.parseBuffer<T>(buffer);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return (await resp.json()) as Promise<T>;
|
|
134
|
+
}
|
|
121
135
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ThreaderOptions, WorkerScript } from './interfaces/IThread.js';
|
|
2
|
+
import { BaseThreader } from './SharedThreader.js';
|
|
3
|
+
import { jsonWorkerScript } from './worker-scripts/JSONWorker.js';
|
|
4
|
+
|
|
5
|
+
export type JsonValue =
|
|
6
|
+
| string
|
|
7
|
+
| number
|
|
8
|
+
| boolean
|
|
9
|
+
| null
|
|
10
|
+
| JsonValue[]
|
|
11
|
+
| { [key: string]: JsonValue };
|
|
12
|
+
|
|
13
|
+
type JsonOp = 'parse' | 'stringify';
|
|
14
|
+
type JsonInput = string | JsonValue | ArrayBuffer;
|
|
15
|
+
type JsonOutput = string | JsonValue;
|
|
16
|
+
|
|
17
|
+
export class JsonThreader extends BaseThreader<JsonOp, JsonInput, JsonOutput> {
|
|
18
|
+
protected readonly workerScript: WorkerScript = jsonWorkerScript;
|
|
19
|
+
private readonly threadingThreshold: number;
|
|
20
|
+
|
|
21
|
+
public constructor(options: ThreaderOptions & { threadingThreshold?: number } = {}) {
|
|
22
|
+
super(options);
|
|
23
|
+
this.threadingThreshold = options.threadingThreshold ?? 16_384; // 16KB default
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async parse<T = JsonValue>(json: string): Promise<T> {
|
|
27
|
+
if (json.length < this.threadingThreshold) {
|
|
28
|
+
return JSON.parse(json) as T;
|
|
29
|
+
}
|
|
30
|
+
const result = await this.run('parse', json);
|
|
31
|
+
return result as T;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async parseBuffer<T = JsonValue>(buffer: ArrayBuffer): Promise<T> {
|
|
35
|
+
if (buffer.byteLength < this.threadingThreshold) {
|
|
36
|
+
const text = new TextDecoder().decode(buffer);
|
|
37
|
+
return JSON.parse(text) as T;
|
|
38
|
+
}
|
|
39
|
+
const result = await this.runWithTransfer('parse', buffer, [buffer]);
|
|
40
|
+
return result as T;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async stringify(data: JsonValue): Promise<string> {
|
|
44
|
+
const result = JSON.stringify(data);
|
|
45
|
+
if (result.length < this.threadingThreshold) {
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
return (await this.run('stringify', data)) as string;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const GLOBAL_KEY = Symbol.for('opnet.jsonThreader');
|
|
53
|
+
const globalObj = (typeof globalThis !== 'undefined' ? globalThis : global) as Record<
|
|
54
|
+
symbol,
|
|
55
|
+
JsonThreader
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
if (!globalObj[GLOBAL_KEY]) {
|
|
59
|
+
globalObj[GLOBAL_KEY] = new JsonThreader();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const jsonThreader: JsonThreader = globalObj[GLOBAL_KEY];
|