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.
Files changed (55) hide show
  1. package/browser/229.index.js +1 -0
  2. package/browser/_version.d.ts +1 -1
  3. package/browser/block/Block.d.ts +8 -2
  4. package/browser/cache/LRUCaching.d.ts +7 -0
  5. package/browser/cache/P2OPCache.d.ts +2 -0
  6. package/browser/fetch/fetch-browser.d.ts +1 -0
  7. package/browser/fetch/fetch.d.ts +2 -2
  8. package/browser/fetch/fetcher-type.d.ts +4 -0
  9. package/browser/index.js +1 -1
  10. package/browser/providers/JSONRpcProvider.d.ts +5 -2
  11. package/browser/threading/JSONThreader.d.ts +20 -0
  12. package/browser/threading/SharedThreader.d.ts +36 -0
  13. package/browser/threading/WorkerCreator.d.ts +3 -0
  14. package/browser/threading/interfaces/IThread.d.ts +28 -0
  15. package/browser/threading/worker-scripts/JSONWorker.d.ts +2 -0
  16. package/build/_version.d.ts +1 -1
  17. package/build/_version.js +1 -1
  18. package/build/block/Block.d.ts +8 -2
  19. package/build/block/Block.js +24 -10
  20. package/build/cache/LRUCaching.d.ts +7 -0
  21. package/build/cache/LRUCaching.js +28 -0
  22. package/build/cache/P2OPCache.d.ts +2 -0
  23. package/build/cache/P2OPCache.js +19 -0
  24. package/build/fetch/fetch.d.ts +2 -2
  25. package/build/fetch/fetch.js +9 -5
  26. package/build/fetch/fetcher-type.d.ts +4 -0
  27. package/build/providers/JSONRpcProvider.d.ts +5 -2
  28. package/build/providers/JSONRpcProvider.js +22 -6
  29. package/build/threading/JSONThreader.d.ts +20 -0
  30. package/build/threading/JSONThreader.js +38 -0
  31. package/build/threading/SharedThreader.d.ts +36 -0
  32. package/build/threading/SharedThreader.js +184 -0
  33. package/build/threading/WorkerCreator.d.ts +3 -0
  34. package/build/threading/WorkerCreator.js +57 -0
  35. package/build/threading/interfaces/IThread.d.ts +28 -0
  36. package/build/threading/interfaces/IThread.js +1 -0
  37. package/build/threading/worker-scripts/JSONWorker.d.ts +2 -0
  38. package/build/threading/worker-scripts/JSONWorker.js +76 -0
  39. package/build/transactions/metadata/TransactionReceipt.js +4 -8
  40. package/package.json +3 -2
  41. package/src/_version.ts +1 -1
  42. package/src/block/Block.ts +32 -14
  43. package/src/cache/LRUCaching.ts +30 -0
  44. package/src/cache/P2OPCache.ts +22 -0
  45. package/src/fetch/fetch-browser.js +3 -1
  46. package/src/fetch/fetch.ts +14 -9
  47. package/src/fetch/fetcher-type.ts +5 -0
  48. package/src/providers/JSONRpcProvider.ts +24 -10
  49. package/src/threading/JSONThreader.ts +62 -0
  50. package/src/threading/SharedThreader.ts +255 -0
  51. package/src/threading/WorkerCreator.ts +67 -0
  52. package/src/threading/interfaces/IThread.ts +38 -0
  53. package/src/threading/worker-scripts/JSONWorker.ts +78 -0
  54. package/src/transactions/metadata/TransactionReceipt.ts +5 -14
  55. 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,2 @@
1
+ import { WorkerScript } from '../interfaces/IThread.js';
2
+ export declare const jsonWorkerScript: WorkerScript;
@@ -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 { Address, NetEvent } from '@btc-vision/transaction';
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 ca = Address.fromString(key);
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 ca = Address.fromString(contractAddress);
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.21",
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.19",
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.21';
1
+ export const version = '1.7.22';
@@ -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
- public readonly transactions: TransactionBase<OPNetTransactionTypes>[] = [];
46
- public readonly deployments: Address[] = [];
45
+ private readonly _rawBlock: IBlock;
46
+ private readonly _network: Network;
47
47
 
48
48
  constructor(block: IBlock, network: Network) {
49
- if (!block) {
50
- throw new Error('Invalid block.');
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
- this.transactions = TransactionParser.parseTransactions(
83
- block.transactions as ITransaction[],
84
- network,
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
- this.deployments = block.deployments
88
- ? block.deployments.map((address) => {
89
- return Address.fromString(address);
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
+ };
@@ -11,7 +11,9 @@ if (!originalFetch) {
11
11
  throw new Error('Fetch API is not available.');
12
12
  }
13
13
 
14
- export class Agent {}
14
+ export class Agent {
15
+ async close() {}
16
+ }
15
17
 
16
18
  const def = {
17
19
  fetch(input, init) {
@@ -1,19 +1,24 @@
1
1
  import pLimit from 'p-limit';
2
- import { Agent, fetch as undiciFetch, Response, setGlobalDispatcher } from 'undici';
3
- import { Fetcher } from './fetcher-type.js';
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): Fetcher {
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(...args: Parameters<typeof undiciFetch>): Promise<Response> {
13
- return limit(() => undiciFetch(...args));
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 limitedFetch;
16
+ return {
17
+ fetch: limitedFetch,
18
+ close: async () => {
19
+ await agent.close();
20
+ },
21
+ };
17
22
  }
18
23
 
19
24
  export default getFetcher;
@@ -1,3 +1,8 @@
1
1
  import { RequestInfo, RequestInit, Response } from 'undici';
2
2
 
3
3
  export type Fetcher = (input: RequestInfo, init?: RequestInit) => Promise<Response>;
4
+
5
+ export interface FetcherWithCleanup {
6
+ fetch: Fetcher;
7
+ close: () => Promise<void>;
8
+ }
@@ -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._fetcher) {
40
- this._fetcher = getFetcher(this.fetcherConfigurations);
41
+ if (!this._fetcherWithCleanup) {
42
+ this._fetcherWithCleanup = getFetcher(this.fetcherConfigurations);
41
43
  }
44
+ return this._fetcherWithCleanup.fetch;
45
+ }
42
46
 
43
- return this._fetcher;
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 = (await resp.json()) as JsonRpcResult | JsonRpcError;
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];