@argonprotocol/bitcoin 1.3.1 → 1.3.3
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/{lib/bitcoin_bindings_bg-T4SJEBFE.wasm → browser/bitcoin_bindings_bg-WMK2AQRE.wasm} +0 -0
- package/browser/index.d.ts +78 -0
- package/browser/index.js +746 -0
- package/browser/index.js.map +1 -0
- package/lib/bitcoin_bindings_bg-WMK2AQRE.wasm +0 -0
- package/lib/index.d.ts +45 -19
- package/lib/index.js +161 -100
- package/package.json +22 -11
package/lib/index.js
CHANGED
|
@@ -4,15 +4,23 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// ts/index.ts
|
|
8
|
+
import { Transaction as Transaction2, Address as Address2, p2wsh as p2wsh2, p2wpkh as p2wpkh2, p2sh as p2sh2, p2pk, p2pkh as p2pkh2 } from "@scure/btc-signer";
|
|
9
|
+
|
|
7
10
|
// ts/CosignScript.ts
|
|
8
|
-
import {
|
|
11
|
+
import { p2pkh, p2sh, p2wpkh, p2wsh, Transaction } from "@scure/btc-signer";
|
|
12
|
+
import {
|
|
13
|
+
hexToU8a as hexToU8a2,
|
|
14
|
+
u8aEq,
|
|
15
|
+
u8aToHex as u8aToHex2
|
|
16
|
+
} from "@argonprotocol/mainchain";
|
|
9
17
|
|
|
10
18
|
// ts/wasm/bitcoin_bindings_bg.wasm
|
|
11
19
|
var bitcoin_bindings_bg_exports = {};
|
|
12
20
|
__export(bitcoin_bindings_bg_exports, {
|
|
13
21
|
default: () => bitcoin_bindings_bg_default
|
|
14
22
|
});
|
|
15
|
-
var bitcoin_bindings_bg_default = "./bitcoin_bindings_bg-
|
|
23
|
+
var bitcoin_bindings_bg_default = "./bitcoin_bindings_bg-WMK2AQRE.wasm";
|
|
16
24
|
|
|
17
25
|
// ts/wasm/bitcoin_bindings_bg.js
|
|
18
26
|
var wasm;
|
|
@@ -223,74 +231,108 @@ var BitcoinNetwork = Object.freeze({
|
|
|
223
231
|
|
|
224
232
|
// ts/wasm/bitcoin_bindings.js
|
|
225
233
|
__wbg_set_wasm(bitcoin_bindings_bg_exports);
|
|
226
|
-
(void 0)();
|
|
227
234
|
|
|
228
235
|
// ts/KeysHelper.ts
|
|
229
|
-
import {
|
|
230
|
-
import
|
|
231
|
-
import
|
|
232
|
-
import
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
+
import { Address, NETWORK, OutScript, TEST_NETWORK } from "@scure/btc-signer";
|
|
237
|
+
import { bech32 } from "@scure/base";
|
|
238
|
+
import { HDKey } from "@scure/bip32";
|
|
239
|
+
import { hexToU8a, u8aToHex } from "@argonprotocol/mainchain";
|
|
240
|
+
import * as secp256k1 from "@noble/secp256k1";
|
|
241
|
+
function getBip32Version(network) {
|
|
242
|
+
if (!network) {
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
245
|
+
if (network === BitcoinNetwork.Testnet || network === BitcoinNetwork.Signet) {
|
|
246
|
+
return {
|
|
247
|
+
private: 70615956,
|
|
248
|
+
// tprv
|
|
249
|
+
public: 70617039
|
|
250
|
+
// tpub
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (network === BitcoinNetwork.Regtest) {
|
|
254
|
+
return {
|
|
255
|
+
private: 70615956,
|
|
256
|
+
// rprv
|
|
257
|
+
public: 70617039
|
|
258
|
+
// rpub
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return void 0;
|
|
236
262
|
}
|
|
237
|
-
function
|
|
238
|
-
|
|
263
|
+
function getChildXpriv(bip39Seed, hdPath, network) {
|
|
264
|
+
const root = HDKey.fromMasterSeed(bip39Seed, getBip32Version(network));
|
|
265
|
+
return root.derive(hdPath);
|
|
239
266
|
}
|
|
240
267
|
function getXpubFromXpriv(xpriv) {
|
|
241
|
-
return xpriv.
|
|
268
|
+
return xpriv.publicExtendedKey;
|
|
242
269
|
}
|
|
243
270
|
function getCompressedPubkey(pubkey) {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
return
|
|
271
|
+
const pubkeyUint8Array = keyToU8a(pubkey);
|
|
272
|
+
if (pubkeyUint8Array.length === 33) {
|
|
273
|
+
return pubkeyUint8Array;
|
|
247
274
|
}
|
|
248
|
-
|
|
275
|
+
const point = secp256k1.Point.fromHex(pubkeyUint8Array);
|
|
276
|
+
return point.toRawBytes(true);
|
|
277
|
+
}
|
|
278
|
+
function getBech32Prefix(network) {
|
|
279
|
+
return getScureNetwork(network).bech32;
|
|
249
280
|
}
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
|
|
281
|
+
function p2wshScriptHexToAddress(scriptPubKeyHex, network) {
|
|
282
|
+
const script = hexToU8a(scriptPubKeyHex);
|
|
283
|
+
if (Buffer.byteLength(script) !== 34 || script[0] !== 0 || script[1] !== 32) {
|
|
284
|
+
throw new Error("Invalid P2WSH scriptPubKey");
|
|
253
285
|
}
|
|
254
|
-
|
|
286
|
+
const witnessProgram = script.slice(2);
|
|
287
|
+
const version = 0;
|
|
288
|
+
const prefix = getBech32Prefix(network);
|
|
289
|
+
return bech32.encode(prefix, [version, ...bech32.toWords(witnessProgram)]);
|
|
255
290
|
}
|
|
256
|
-
function addressBytesHex(
|
|
257
|
-
if (
|
|
258
|
-
return
|
|
291
|
+
function addressBytesHex(address, network) {
|
|
292
|
+
if (address.startsWith("0x")) {
|
|
293
|
+
return address;
|
|
259
294
|
}
|
|
260
|
-
|
|
261
|
-
if (/^[0-9a-fA-F]+$/.test(
|
|
262
|
-
return `0x${
|
|
295
|
+
const bech32Prefix = getBech32Prefix(network);
|
|
296
|
+
if (/^[0-9a-fA-F]+$/.test(address) && !address.startsWith("bc") && !address.startsWith(bech32Prefix)) {
|
|
297
|
+
return `0x${address}`;
|
|
263
298
|
}
|
|
264
|
-
const
|
|
265
|
-
|
|
299
|
+
const btcNetwork = getScureNetwork(network);
|
|
300
|
+
const decoded = Address(btcNetwork).decode(address);
|
|
301
|
+
const out = OutScript.encode(decoded);
|
|
302
|
+
return u8aToHex(out);
|
|
303
|
+
}
|
|
304
|
+
function keyToU8a(pubkey) {
|
|
305
|
+
return typeof pubkey === "string" ? hexToU8a(pubkey) : pubkey;
|
|
266
306
|
}
|
|
267
|
-
function
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
307
|
+
function getScureNetwork(network) {
|
|
308
|
+
if (network === BitcoinNetwork.Bitcoin) {
|
|
309
|
+
return NETWORK;
|
|
310
|
+
} else if (network === BitcoinNetwork.Testnet || network === BitcoinNetwork.Signet) {
|
|
311
|
+
return TEST_NETWORK;
|
|
312
|
+
} else {
|
|
313
|
+
return {
|
|
314
|
+
bech32: "bcrt",
|
|
315
|
+
pubKeyHash: 111,
|
|
316
|
+
scriptHash: 196,
|
|
317
|
+
wif: 239
|
|
318
|
+
};
|
|
271
319
|
}
|
|
272
|
-
return Buffer.from(key);
|
|
273
320
|
}
|
|
274
321
|
|
|
275
322
|
// ts/CosignScript.ts
|
|
276
|
-
var CosignScript = class
|
|
323
|
+
var CosignScript = class {
|
|
277
324
|
constructor(lock, network) {
|
|
278
325
|
this.lock = lock;
|
|
279
|
-
|
|
280
|
-
this.network = network;
|
|
281
|
-
} else {
|
|
282
|
-
this.network = _CosignScript.getBitcoinJsNetwork(
|
|
283
|
-
network
|
|
284
|
-
);
|
|
285
|
-
}
|
|
326
|
+
this.network = network;
|
|
286
327
|
}
|
|
287
|
-
network;
|
|
288
328
|
getFundingPsbt() {
|
|
289
329
|
const { lock, network } = this;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
330
|
+
const tx = new Transaction();
|
|
331
|
+
tx.addOutput({
|
|
332
|
+
script: keyToU8a(lock.p2wshScriptHashHex),
|
|
333
|
+
amount: lock.satoshis
|
|
293
334
|
});
|
|
335
|
+
return tx.toPSBT(0);
|
|
294
336
|
}
|
|
295
337
|
calculateFee(feeRatePerSatVb, toScriptPubkey) {
|
|
296
338
|
toScriptPubkey = addressBytesHex(toScriptPubkey, this.network);
|
|
@@ -302,7 +344,7 @@ var CosignScript = class _CosignScript {
|
|
|
302
344
|
BigInt(lock.vaultClaimHeight),
|
|
303
345
|
BigInt(lock.openClaimHeight),
|
|
304
346
|
BigInt(lock.createdAtHeight),
|
|
305
|
-
|
|
347
|
+
network,
|
|
306
348
|
feeRatePerSatVb,
|
|
307
349
|
toScriptPubkey
|
|
308
350
|
);
|
|
@@ -316,7 +358,7 @@ var CosignScript = class _CosignScript {
|
|
|
316
358
|
BigInt(lock.vaultClaimHeight),
|
|
317
359
|
BigInt(lock.openClaimHeight),
|
|
318
360
|
BigInt(lock.createdAtHeight),
|
|
319
|
-
|
|
361
|
+
network
|
|
320
362
|
);
|
|
321
363
|
}
|
|
322
364
|
getCosignPsbt(args) {
|
|
@@ -333,21 +375,22 @@ var CosignScript = class _CosignScript {
|
|
|
333
375
|
BigInt(lock.vaultClaimHeight),
|
|
334
376
|
BigInt(lock.openClaimHeight),
|
|
335
377
|
BigInt(lock.createdAtHeight),
|
|
336
|
-
|
|
378
|
+
network,
|
|
337
379
|
releaseRequest.toScriptPubkey,
|
|
338
380
|
releaseRequest.bitcoinNetworkFee
|
|
339
381
|
);
|
|
340
382
|
return this.psbtFromHex(psbtStr);
|
|
341
383
|
}
|
|
342
384
|
psbtFromHex(psbtHex) {
|
|
343
|
-
const
|
|
344
|
-
|
|
385
|
+
const psbtBytes = hexToU8a2(psbtHex);
|
|
386
|
+
const tx = Transaction.fromPSBT(psbtBytes);
|
|
387
|
+
if (tx.inputsLength === 0) {
|
|
345
388
|
throw new Error("PSBT has no inputs");
|
|
346
389
|
}
|
|
347
|
-
if (
|
|
390
|
+
if (tx.outputsLength === 0) {
|
|
348
391
|
throw new Error("PSBT has no outputs");
|
|
349
392
|
}
|
|
350
|
-
return
|
|
393
|
+
return tx;
|
|
351
394
|
}
|
|
352
395
|
/**
|
|
353
396
|
* Cosigns the PSBT with the vault xpub.
|
|
@@ -356,21 +399,33 @@ var CosignScript = class _CosignScript {
|
|
|
356
399
|
* @param vaultXpriv - The vault's extended private key of which the xpub was used to create the vault.
|
|
357
400
|
*/
|
|
358
401
|
vaultCosignPsbt(psbt, lock, vaultXpriv) {
|
|
359
|
-
const parentFingerprint =
|
|
360
|
-
|
|
402
|
+
const parentFingerprint = lock.vaultXpubSources.parentFingerprint;
|
|
403
|
+
const vaultFingerprint = vaultXpriv.identifier?.slice(0, 4);
|
|
404
|
+
if (!vaultFingerprint) {
|
|
405
|
+
throw new Error("Could not get vault fingerprint from HDKey");
|
|
406
|
+
}
|
|
407
|
+
if (!u8aEq(parentFingerprint, vaultFingerprint)) {
|
|
361
408
|
throw new Error(
|
|
362
|
-
`Vault xpub fingerprint ${parentFingerprint
|
|
409
|
+
`Vault xpub fingerprint ${u8aToHex2(parentFingerprint)} does not match the vault xpriv fingerprint ${u8aToHex2(vaultFingerprint)}`
|
|
363
410
|
);
|
|
364
411
|
}
|
|
365
412
|
const childPath = `${lock.vaultXpubSources.cosignHdIndex}`;
|
|
366
|
-
const pubkey = vaultXpriv.
|
|
367
|
-
|
|
368
|
-
|
|
413
|
+
const pubkey = vaultXpriv.deriveChild(lock.vaultXpubSources.cosignHdIndex).publicKey;
|
|
414
|
+
if (!pubkey) {
|
|
415
|
+
throw new Error(`Failed to derive public key for path ${childPath}`);
|
|
416
|
+
}
|
|
417
|
+
const vaultPubkey = keyToU8a(lock.vaultPubkey);
|
|
418
|
+
if (!u8aEq(vaultPubkey, pubkey)) {
|
|
369
419
|
throw new Error(
|
|
370
|
-
`Vault pubkey ${vaultPubkey
|
|
420
|
+
`Vault pubkey ${u8aToHex2(vaultPubkey)} does not match the derived pubkey ${u8aToHex2(pubkey)} using path ${childPath}`
|
|
371
421
|
);
|
|
372
422
|
}
|
|
373
|
-
const signedPsbt = signPsbtDerived(
|
|
423
|
+
const signedPsbt = signPsbtDerived(
|
|
424
|
+
u8aToHex2(psbt.toPSBT()),
|
|
425
|
+
vaultXpriv.privateExtendedKey,
|
|
426
|
+
childPath,
|
|
427
|
+
false
|
|
428
|
+
);
|
|
374
429
|
psbt = this.psbtFromHex(signedPsbt);
|
|
375
430
|
return psbt;
|
|
376
431
|
}
|
|
@@ -382,74 +437,80 @@ var CosignScript = class _CosignScript {
|
|
|
382
437
|
const psbt = this.getCosignPsbt(args);
|
|
383
438
|
const { addTx, vaultCosignature, ownerXpriv, ownerXprivChildHdPath } = args;
|
|
384
439
|
psbt.updateInput(0, {
|
|
385
|
-
partialSig: [
|
|
386
|
-
{
|
|
387
|
-
pubkey: keyToBuffer(lock.vaultPubkey),
|
|
388
|
-
signature: Buffer.from(vaultCosignature)
|
|
389
|
-
}
|
|
390
|
-
]
|
|
440
|
+
partialSig: [[keyToU8a(lock.vaultPubkey), vaultCosignature]]
|
|
391
441
|
});
|
|
392
442
|
const derivePubkey = ownerXpriv.publicKey;
|
|
393
|
-
|
|
394
|
-
|
|
443
|
+
if (!derivePubkey) {
|
|
444
|
+
throw new Error("Failed to derive owner public key");
|
|
445
|
+
}
|
|
446
|
+
const ownerPubkey = keyToU8a(lock.ownerPubkey);
|
|
447
|
+
if (!u8aEq(ownerPubkey, derivePubkey)) {
|
|
395
448
|
throw new Error(
|
|
396
|
-
`Owner pubkey ${ownerPubkey
|
|
449
|
+
`Owner pubkey ${u8aToHex2(ownerPubkey)} does not match the derived pubkey ${u8aToHex2(derivePubkey)}`
|
|
397
450
|
);
|
|
398
451
|
}
|
|
399
452
|
if (addTx) {
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
453
|
+
const addTxBytes = hexToU8a2(addTx);
|
|
454
|
+
const tx = Transaction.fromPSBT(addTxBytes);
|
|
455
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
456
|
+
const output = tx.getOutput(i);
|
|
457
|
+
const network = getScureNetwork(this.network);
|
|
403
458
|
const scripts = [
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
459
|
+
p2wpkh(ownerPubkey, network).script,
|
|
460
|
+
p2wsh(p2wpkh(ownerPubkey, network), network).script,
|
|
461
|
+
p2sh(p2pkh(ownerPubkey, network), network).script,
|
|
462
|
+
p2pkh(ownerPubkey, network).script
|
|
407
463
|
];
|
|
408
|
-
if (scripts.some((x) => x && output.script.
|
|
464
|
+
if (scripts.some((x) => x && output.script && u8aEq(output.script, x))) {
|
|
409
465
|
psbt.addInput({
|
|
410
|
-
|
|
466
|
+
txid: tx.id,
|
|
411
467
|
index: i,
|
|
412
468
|
witnessUtxo: {
|
|
413
469
|
script: output.script,
|
|
414
|
-
|
|
470
|
+
amount: output.amount
|
|
415
471
|
}
|
|
416
472
|
});
|
|
417
473
|
}
|
|
418
474
|
}
|
|
419
475
|
}
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
ownerXpriv.privateKey.toString("hex"),
|
|
424
|
-
true
|
|
425
|
-
);
|
|
426
|
-
const finalPsbt = this.psbtFromHex(signedPsbt);
|
|
427
|
-
return finalPsbt.extractTransaction();
|
|
428
|
-
}
|
|
429
|
-
static getBitcoinJsNetwork(network) {
|
|
430
|
-
if (network.isBitcoin) return networks2.bitcoin;
|
|
431
|
-
if (network.isTestnet || network.isSignet) return networks2.testnet;
|
|
432
|
-
if (network.isRegtest) return networks2.regtest;
|
|
433
|
-
throw new Error("Unsupported network: " + network);
|
|
476
|
+
const psbtBytes = u8aToHex2(psbt.toPSBT());
|
|
477
|
+
const signedPsbt = ownerXprivChildHdPath ? signPsbtDerived(psbtBytes, ownerXpriv.privateExtendedKey, ownerXprivChildHdPath, true) : signPsbt(psbtBytes, this.network, u8aToHex2(ownerXpriv.privateKey, void 0, false), true);
|
|
478
|
+
return this.psbtFromHex(signedPsbt);
|
|
434
479
|
}
|
|
435
480
|
};
|
|
436
|
-
function
|
|
437
|
-
if (network
|
|
481
|
+
function getBitcoinNetworkFromApi(network) {
|
|
482
|
+
if (network.isBitcoin) {
|
|
438
483
|
return BitcoinNetwork.Bitcoin;
|
|
439
|
-
} else if (network
|
|
484
|
+
} else if (network.isTestnet) {
|
|
440
485
|
return BitcoinNetwork.Testnet;
|
|
441
|
-
} else if (network
|
|
486
|
+
} else if (network.isRegtest) {
|
|
442
487
|
return BitcoinNetwork.Regtest;
|
|
443
488
|
}
|
|
444
489
|
throw new Error("Unsupported network: " + network);
|
|
445
490
|
}
|
|
491
|
+
|
|
492
|
+
// ts/index.ts
|
|
493
|
+
import * as bip39 from "@scure/bip39";
|
|
446
494
|
export {
|
|
495
|
+
Address2 as Address,
|
|
496
|
+
BitcoinNetwork,
|
|
447
497
|
CosignScript,
|
|
498
|
+
HDKey,
|
|
499
|
+
Transaction2 as Transaction,
|
|
448
500
|
addressBytesHex,
|
|
449
|
-
|
|
501
|
+
bip39,
|
|
502
|
+
getBech32Prefix,
|
|
503
|
+
getBip32Version,
|
|
504
|
+
getBitcoinNetworkFromApi,
|
|
450
505
|
getChildXpriv,
|
|
451
506
|
getCompressedPubkey,
|
|
507
|
+
getScureNetwork,
|
|
452
508
|
getXpubFromXpriv,
|
|
453
|
-
|
|
454
|
-
|
|
509
|
+
keyToU8a,
|
|
510
|
+
p2pk,
|
|
511
|
+
p2pkh2 as p2pkh,
|
|
512
|
+
p2sh2 as p2sh,
|
|
513
|
+
p2wpkh2 as p2wpkh,
|
|
514
|
+
p2wsh2 as p2wsh,
|
|
515
|
+
p2wshScriptHexToAddress
|
|
455
516
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@argonprotocol/bitcoin",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "A client for interop with bitcoin in nodejs.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://github.com/argonprotocol/mainchain#readme",
|
|
15
15
|
"scripts": {
|
|
16
|
-
"wasm-pack": "wasm-pack build --target bundler --release --out-dir ts/wasm --out-name bitcoin_bindings --no-pack --no-opt",
|
|
16
|
+
"wasm-pack": "wasm-pack build --target bundler --release --out-dir ts/wasm --out-name bitcoin_bindings --no-pack --no-opt && node clean-wasm.js",
|
|
17
17
|
"prebuild": "yarn workspace @argonprotocol/mainchain run build",
|
|
18
18
|
"build": "yarn wasm-pack && yarn tsc",
|
|
19
19
|
"pretsc": "yarn workspace @argonprotocol/mainchain run tsc",
|
|
@@ -23,22 +23,33 @@
|
|
|
23
23
|
"tsup": "yarn tsc"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
-
"lib/"
|
|
26
|
+
"lib/",
|
|
27
|
+
"browser/"
|
|
27
28
|
],
|
|
28
29
|
"type": "module",
|
|
29
|
-
"bin": "./lib/cli.js",
|
|
30
30
|
"module": "./lib/index.js",
|
|
31
31
|
"types": "./lib/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./lib/index.d.ts",
|
|
35
|
+
"import": "./lib/index.js",
|
|
36
|
+
"browser": {
|
|
37
|
+
"types": "./browser/index.d.ts",
|
|
38
|
+
"default": "./browser/index.js"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
32
42
|
"dependencies": {
|
|
33
|
-
"@argonprotocol/mainchain": "1.3.
|
|
34
|
-
"
|
|
35
|
-
"bip32": "^
|
|
36
|
-
"bip39": "^
|
|
37
|
-
"
|
|
38
|
-
"
|
|
43
|
+
"@argonprotocol/mainchain": "1.3.3",
|
|
44
|
+
"@noble/secp256k1": "^2.3.0",
|
|
45
|
+
"@scure/bip32": "^1.7.0",
|
|
46
|
+
"@scure/bip39": "^1.6.0",
|
|
47
|
+
"@scure/btc-signer": "^1.8.1",
|
|
48
|
+
"bignumber.js": "^9.1.2"
|
|
39
49
|
},
|
|
40
50
|
"devDependencies": {
|
|
41
|
-
"@argonprotocol/testing": "1.3.
|
|
51
|
+
"@argonprotocol/testing": "1.3.3",
|
|
52
|
+
"@types/node": "22.16.3",
|
|
42
53
|
"tsup": "^8.4.0",
|
|
43
54
|
"tsx": "^4.19.2",
|
|
44
55
|
"typescript": "^5.8.3",
|