cashscript 0.13.0-next.7 → 0.13.0-next.9
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/Contract.js +3 -3
- package/dist/Errors.d.ts +6 -0
- package/dist/Errors.js +14 -1
- package/dist/TransactionBuilder.d.ts +20 -2
- package/dist/TransactionBuilder.js +56 -6
- package/dist/interfaces.d.ts +4 -0
- package/dist/libauth-template/LibauthTemplate.js +2 -3
- package/dist/libauth-template/utils.js +4 -1
- package/dist/network/MockNetworkProvider.d.ts +3 -0
- package/dist/network/MockNetworkProvider.js +5 -0
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +13 -3
- package/package.json +3 -3
package/dist/Contract.js
CHANGED
|
@@ -92,10 +92,10 @@ class ContractInternal extends ContractBase {
|
|
|
92
92
|
if (abiFunction.inputs.length !== args.length) {
|
|
93
93
|
throw new Error(`Incorrect number of arguments passed to function ${abiFunction.name}. Expected ${abiFunction.inputs.length} arguments (${abiFunction.inputs.map((input) => input.type)}) but got ${args.length}`);
|
|
94
94
|
}
|
|
95
|
-
const bytecode = hexToBin(this.bytecode);
|
|
96
95
|
const encodedArgs = args
|
|
97
96
|
.map((arg, i) => encodeFunctionArgument(arg, abiFunction.inputs[i].type));
|
|
98
97
|
const generateUnlockingBytecode = ({ transaction, sourceOutputs, inputIndex }) => {
|
|
98
|
+
const bytecode = hexToBin(this.bytecode);
|
|
99
99
|
const completeArgs = encodedArgs.map((arg) => {
|
|
100
100
|
if (!(arg instanceof SignatureTemplate))
|
|
101
101
|
return arg;
|
|
@@ -104,10 +104,10 @@ class ContractInternal extends ContractBase {
|
|
|
104
104
|
const sighash = hash256(preimage);
|
|
105
105
|
return arg.generateSignature(sighash);
|
|
106
106
|
});
|
|
107
|
-
const unlockingBytecode = createUnlockingBytecode(this.contractType,
|
|
107
|
+
const unlockingBytecode = createUnlockingBytecode(this.contractType, bytecode, completeArgs, selector);
|
|
108
108
|
return unlockingBytecode;
|
|
109
109
|
};
|
|
110
|
-
const generateLockingBytecode = () =>
|
|
110
|
+
const generateLockingBytecode = () => hexToBin(this.lockingBytecode);
|
|
111
111
|
return { generateUnlockingBytecode, generateLockingBytecode, contract: this, params: args, abiFunction };
|
|
112
112
|
};
|
|
113
113
|
}
|
package/dist/Errors.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export declare class OutputTokenCategoryInvalidError extends Error {
|
|
|
17
17
|
export declare class OutputTokenCommitmentInvalidError extends Error {
|
|
18
18
|
constructor(commitment: string);
|
|
19
19
|
}
|
|
20
|
+
export declare class OutputBchChangeLockedError extends Error {
|
|
21
|
+
constructor();
|
|
22
|
+
}
|
|
23
|
+
export declare class OutputTokenChangeLockedError extends Error {
|
|
24
|
+
constructor(category: string);
|
|
25
|
+
}
|
|
20
26
|
export declare class TokensToNonTokenAddressError extends Error {
|
|
21
27
|
constructor(address: string);
|
|
22
28
|
}
|
package/dist/Errors.js
CHANGED
|
@@ -29,6 +29,16 @@ export class OutputTokenCommitmentInvalidError extends Error {
|
|
|
29
29
|
super(`Provided token commitment ${commitment} is not a hex string`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
export class OutputBchChangeLockedError extends Error {
|
|
33
|
+
constructor() {
|
|
34
|
+
super('Tried to add a BCH input or output after a BCH change output was already added');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class OutputTokenChangeLockedError extends Error {
|
|
38
|
+
constructor(category) {
|
|
39
|
+
super(`Tried to add a token input or output with category ${category} after a change output with the same category was already added`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
32
42
|
export class TokensToNonTokenAddressError extends Error {
|
|
33
43
|
constructor(address) {
|
|
34
44
|
super(`Tried to send tokens to an address without token support, ${address}.`);
|
|
@@ -72,7 +82,10 @@ export class FailedRequireError extends FailedTransactionError {
|
|
|
72
82
|
const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer);
|
|
73
83
|
const baseMessage = `${artifact.contractName}.cash:${lineNumber} Require statement failed at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}`;
|
|
74
84
|
const baseMessageWithRequireMessage = `${baseMessage} with the following message: ${requireStatement.message}`;
|
|
75
|
-
const
|
|
85
|
+
const headline = `${requireStatement.message ? baseMessageWithRequireMessage : baseMessage}.`;
|
|
86
|
+
// Compiler-injected guards (e.g. the tx.locktime guard) have no user-written source, so the
|
|
87
|
+
// extracted statement is empty — the require message fully describes the failure on its own.
|
|
88
|
+
const fullMessage = statement.trim() ? `${headline}\nFailing statement: ${statement}` : headline;
|
|
76
89
|
super(fullMessage, bitauthUri);
|
|
77
90
|
this.artifact = artifact;
|
|
78
91
|
this.failingInstructionPointer = failingInstructionPointer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WalletTemplate } from '@bitauth/libauth';
|
|
2
|
-
import { Unlocker, Output, TransactionDetails, UnlockableUtxo, Utxo, InputOptions, VmResourceUsage, BchChangeOutputOptions } from './interfaces.js';
|
|
2
|
+
import { Unlocker, Output, TransactionDetails, UnlockableUtxo, Utxo, InputOptions, VmResourceUsage, BchChangeOutputOptions, TokenChangeOutputOptions } from './interfaces.js';
|
|
3
3
|
import { NetworkProvider } from './network/index.js';
|
|
4
4
|
import { DebugResults } from './debugging.js';
|
|
5
5
|
import { WcTransactionOptions } from './walletconnect-utils.js';
|
|
@@ -30,6 +30,7 @@ export declare class TransactionBuilder {
|
|
|
30
30
|
outputs: Output[];
|
|
31
31
|
locktime: number;
|
|
32
32
|
options: TransactionBuilderOptions;
|
|
33
|
+
private changeLocks;
|
|
33
34
|
/**
|
|
34
35
|
* Create a new TransactionBuilder.
|
|
35
36
|
*
|
|
@@ -95,11 +96,28 @@ export declare class TransactionBuilder {
|
|
|
95
96
|
* fee is computed from the transaction size at the configured fee rate; dust-sized change is
|
|
96
97
|
* simply absorbed into the fee.
|
|
97
98
|
*
|
|
99
|
+
* Should be called *after* all explicit inputs and outputs are added.
|
|
100
|
+
*
|
|
98
101
|
* @param changeOutputOptions - The destination address and the fee rate (in sats/byte) to use.
|
|
99
102
|
* @returns This builder for chaining.
|
|
100
|
-
* @throws If the available surplus is insufficient to cover the fee for the configured rate
|
|
103
|
+
* @throws If the available surplus is insufficient to cover the fee for the configured rate or
|
|
104
|
+
* if a BCH change output was already added.
|
|
101
105
|
*/
|
|
102
106
|
addBchChangeOutputIfNeeded(changeOutputOptions: BchChangeOutputOptions): this;
|
|
107
|
+
/**
|
|
108
|
+
* Add a fungible token change output for the configured category if the transaction's inputs
|
|
109
|
+
* contain more tokens of that category than its outputs. The change output is sent to the
|
|
110
|
+
* provided token address and carries the dust-minimum BCH amount.
|
|
111
|
+
*
|
|
112
|
+
* Should be called *after* all explicit token outputs for the category are added and *before*
|
|
113
|
+
* `addBchChangeOutputIfNeeded`.
|
|
114
|
+
*
|
|
115
|
+
* @param changeOutputOptions - The token category to handle and the destination token address.
|
|
116
|
+
* @returns This builder for chaining.
|
|
117
|
+
* @throws If the destination is not a token-supporting address, or if a corresponding change output
|
|
118
|
+
* or BCH change output was already added.
|
|
119
|
+
*/
|
|
120
|
+
addTokenChangeOutputIfNeeded(changeOutputOptions: TokenChangeOutputOptions): this;
|
|
103
121
|
/**
|
|
104
122
|
* Build the transaction (skipping fee and burn checks) and return its encoded byte length.
|
|
105
123
|
*
|
|
@@ -23,6 +23,7 @@ export class TransactionBuilder {
|
|
|
23
23
|
this.inputs = [];
|
|
24
24
|
this.outputs = [];
|
|
25
25
|
this.locktime = 0;
|
|
26
|
+
this.changeLocks = {};
|
|
26
27
|
this.provider = options.provider;
|
|
27
28
|
this.options = {
|
|
28
29
|
allowImplicitFungibleTokenBurn: options.allowImplicitFungibleTokenBurn ?? false,
|
|
@@ -43,7 +44,7 @@ export class TransactionBuilder {
|
|
|
43
44
|
return this.addInputs([utxo], unlocker, options);
|
|
44
45
|
}
|
|
45
46
|
addInputs(utxos, unlocker, options) {
|
|
46
|
-
utxos.forEach(validateInput);
|
|
47
|
+
utxos.forEach((utxo) => validateInput(utxo, this.changeLocks));
|
|
47
48
|
if ((!unlocker && utxos.some((utxo) => !isUnlockableUtxo(utxo)))
|
|
48
49
|
|| (unlocker && utxos.some((utxo) => isUnlockableUtxo(utxo)))) {
|
|
49
50
|
throw new Error('Either all UTXOs must have an individual unlocker specified, or no UTXOs must have an individual unlocker specified and a shared unlocker must be provided');
|
|
@@ -74,7 +75,7 @@ export class TransactionBuilder {
|
|
|
74
75
|
* @throws If any output is invalid.
|
|
75
76
|
*/
|
|
76
77
|
addOutputs(outputs) {
|
|
77
|
-
outputs.forEach((output) => validateOutput(output, this.provider.network));
|
|
78
|
+
outputs.forEach((output) => validateOutput(output, this.provider.network, this.changeLocks));
|
|
78
79
|
this.outputs = this.outputs.concat(outputs);
|
|
79
80
|
return this;
|
|
80
81
|
}
|
|
@@ -87,7 +88,7 @@ export class TransactionBuilder {
|
|
|
87
88
|
* @returns This builder for chaining.
|
|
88
89
|
*/
|
|
89
90
|
addOpReturnOutput(chunks) {
|
|
90
|
-
this.
|
|
91
|
+
this.addOutput(createOpReturnOutput(chunks));
|
|
91
92
|
return this;
|
|
92
93
|
}
|
|
93
94
|
/**
|
|
@@ -95,9 +96,12 @@ export class TransactionBuilder {
|
|
|
95
96
|
* fee is computed from the transaction size at the configured fee rate; dust-sized change is
|
|
96
97
|
* simply absorbed into the fee.
|
|
97
98
|
*
|
|
99
|
+
* Should be called *after* all explicit inputs and outputs are added.
|
|
100
|
+
*
|
|
98
101
|
* @param changeOutputOptions - The destination address and the fee rate (in sats/byte) to use.
|
|
99
102
|
* @returns This builder for chaining.
|
|
100
|
-
* @throws If the available surplus is insufficient to cover the fee for the configured rate
|
|
103
|
+
* @throws If the available surplus is insufficient to cover the fee for the configured rate or
|
|
104
|
+
* if a BCH change output was already added.
|
|
101
105
|
*/
|
|
102
106
|
addBchChangeOutputIfNeeded(changeOutputOptions) {
|
|
103
107
|
const totalBchInputAmount = this.inputs.reduce((total, input) => total + input.satoshis, 0n);
|
|
@@ -119,9 +123,47 @@ export class TransactionBuilder {
|
|
|
119
123
|
const changeOutput = { to: changeOutputOptions.to, amount: changeAmount };
|
|
120
124
|
const changeOutputDust = calculateDust(changeOutput);
|
|
121
125
|
if (changeAmount < changeOutputDust) {
|
|
126
|
+
this.changeLocks.BCH = true;
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
this.addOutput(changeOutput);
|
|
130
|
+
this.changeLocks.BCH = true;
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Add a fungible token change output for the configured category if the transaction's inputs
|
|
135
|
+
* contain more tokens of that category than its outputs. The change output is sent to the
|
|
136
|
+
* provided token address and carries the dust-minimum BCH amount.
|
|
137
|
+
*
|
|
138
|
+
* Should be called *after* all explicit token outputs for the category are added and *before*
|
|
139
|
+
* `addBchChangeOutputIfNeeded`.
|
|
140
|
+
*
|
|
141
|
+
* @param changeOutputOptions - The token category to handle and the destination token address.
|
|
142
|
+
* @returns This builder for chaining.
|
|
143
|
+
* @throws If the destination is not a token-supporting address, or if a corresponding change output
|
|
144
|
+
* or BCH change output was already added.
|
|
145
|
+
*/
|
|
146
|
+
addTokenChangeOutputIfNeeded(changeOutputOptions) {
|
|
147
|
+
const { category, to } = changeOutputOptions;
|
|
148
|
+
const inputAmount = this.inputs
|
|
149
|
+
.filter((input) => input.token?.category === category)
|
|
150
|
+
.reduce((total, input) => total + input.token.amount, 0n);
|
|
151
|
+
const outputAmount = this.outputs
|
|
152
|
+
.filter((output) => output.token?.category === category)
|
|
153
|
+
.reduce((total, output) => total + output.token.amount, 0n);
|
|
154
|
+
const changeAmount = inputAmount - outputAmount;
|
|
155
|
+
if (changeAmount <= 0n) {
|
|
156
|
+
this.changeLocks[category] = true;
|
|
122
157
|
return this;
|
|
123
158
|
}
|
|
124
|
-
|
|
159
|
+
const changeOutput = {
|
|
160
|
+
to,
|
|
161
|
+
amount: 0n,
|
|
162
|
+
token: { amount: changeAmount, category },
|
|
163
|
+
};
|
|
164
|
+
changeOutput.amount = BigInt(calculateDust(changeOutput));
|
|
165
|
+
this.addOutput(changeOutput);
|
|
166
|
+
this.changeLocks[category] = true;
|
|
125
167
|
return this;
|
|
126
168
|
}
|
|
127
169
|
/**
|
|
@@ -318,7 +360,15 @@ export class TransactionBuilder {
|
|
|
318
360
|
}
|
|
319
361
|
catch (e) {
|
|
320
362
|
const reason = e.error ?? e.message;
|
|
321
|
-
|
|
363
|
+
const getBitauthUriWithFallback = () => {
|
|
364
|
+
try {
|
|
365
|
+
return getBitauthUri(this.getLibauthTemplate());
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return 'Bitauth URI generation failed';
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
throw new FailedTransactionError(reason, getBitauthUriWithFallback());
|
|
322
372
|
}
|
|
323
373
|
}
|
|
324
374
|
async getTxDetails(txid, raw) {
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -62,6 +62,10 @@ export interface BchChangeOutputOptions {
|
|
|
62
62
|
to: string | Uint8Array;
|
|
63
63
|
feeRate: number;
|
|
64
64
|
}
|
|
65
|
+
export interface TokenChangeOutputOptions {
|
|
66
|
+
category: string;
|
|
67
|
+
to: string | Uint8Array;
|
|
68
|
+
}
|
|
65
69
|
export interface TokenDetails {
|
|
66
70
|
amount: bigint;
|
|
67
71
|
category: string;
|
|
@@ -73,8 +73,7 @@ const generateLockingBytecodeParamsMapping = (transactionBuilder) => {
|
|
|
73
73
|
if (isContractUnlocker(input.unlocker)) {
|
|
74
74
|
const lockScriptName = getLockScriptName(input.unlocker.contract);
|
|
75
75
|
const lockingScriptParams = generateLockingScriptParams(input.unlocker.contract, input, lockScriptName);
|
|
76
|
-
|
|
77
|
-
mapping[lockingBytecode] = lockingScriptParams;
|
|
76
|
+
mapping[input.unlocker.contract.lockingBytecode] = lockingScriptParams;
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
return mapping;
|
|
@@ -394,7 +393,7 @@ const generateTemplateScenarioTransactionOutputLockingBytecode = (csOutput, liba
|
|
|
394
393
|
return lockingBytecodeParams;
|
|
395
394
|
if (csOutput.to instanceof Uint8Array)
|
|
396
395
|
return binToHex(csOutput.to);
|
|
397
|
-
if (contract && [contract.address, contract.tokenAddress].includes(csOutput.to))
|
|
396
|
+
if (contract && contract.contractType !== 'p2s' && [contract.address, contract.tokenAddress].includes(csOutput.to))
|
|
398
397
|
return {};
|
|
399
398
|
return binToHex(addressToLockScript(csOutput.to));
|
|
400
399
|
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { bytecodeToScript, formatBitAuthScript } from '@cashscript/utils';
|
|
1
|
+
import { bytecodeToScript, formatBitAuthScript, sha256 } from '@cashscript/utils';
|
|
2
2
|
import { HashType, SignatureAlgorithm, VmTarget } from '../interfaces.js';
|
|
3
3
|
import { hexToBin, binToHex, isHex, decodeCashAddress, assertSuccess, decodeAuthenticationInstructions } from '@bitauth/libauth';
|
|
4
4
|
import { zip } from '../utils.js';
|
|
5
5
|
import SignatureTemplate from '../SignatureTemplate.js';
|
|
6
6
|
export const DEFAULT_VM_TARGET = VmTarget.BCH_2026_05;
|
|
7
7
|
export const getLockScriptName = (contract) => {
|
|
8
|
+
if (contract.contractType === 'p2s') {
|
|
9
|
+
return `${contract.artifact.contractName}_${binToHex(sha256(hexToBin(contract.lockingBytecode)))}_lock`;
|
|
10
|
+
}
|
|
8
11
|
const result = decodeCashAddress(contract.address);
|
|
9
12
|
if (typeof result === 'string')
|
|
10
13
|
throw new Error(result);
|
|
@@ -98,4 +98,9 @@ export default class MockNetworkProvider {
|
|
|
98
98
|
this.transactionMap = {};
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
export class FailingMockNetworkProvider extends MockNetworkProvider {
|
|
102
|
+
async sendRawTransaction(_txHex) {
|
|
103
|
+
throw new Error('broadcast failed');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
101
106
|
//# sourceMappingURL=MockNetworkProvider.js.map
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Transaction } from '@bitauth/libauth';
|
|
2
2
|
import { Script } from '@cashscript/utils';
|
|
3
3
|
import { Utxo, Output, Network, LibauthOutput, TokenDetails, AddressType, UnlockableUtxo, LibauthTokenDetails, ContractType } from './interfaces.js';
|
|
4
|
-
export declare function validateInput(utxo: Utxo): void;
|
|
5
|
-
export declare function validateOutput(output: Output, network: Network): void;
|
|
4
|
+
export declare function validateInput(utxo: Utxo, changeLocks: Record<string, boolean>): void;
|
|
5
|
+
export declare function validateOutput(output: Output, network: Network, changeLocks: Record<string, boolean>): void;
|
|
6
6
|
export declare function isOpReturnOutput(output: Output): boolean;
|
|
7
7
|
export declare function calculateDust(output: Output): number;
|
|
8
8
|
export declare function getOutputSize(output: Output): number;
|
package/dist/utils.js
CHANGED
|
@@ -2,14 +2,16 @@ import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLocki
|
|
|
2
2
|
import { encodeInt, hash160, hash256, sha256, Op, scriptToBytecode, encodeNullDataScript, } from '@cashscript/utils';
|
|
3
3
|
import { Network, } from './interfaces.js';
|
|
4
4
|
import { VERSION_SIZE, LOCKTIME_SIZE } from './constants.js';
|
|
5
|
-
import { OutputSatoshisTooSmallError, OutputTokenAmountTooSmallError, TokensToNonTokenAddressError, UndefinedInputError, OutputAddressNetworkMismatchError, OutputTokenCategoryInvalidError, OutputTokenCommitmentInvalidError, } from './Errors.js';
|
|
5
|
+
import { OutputSatoshisTooSmallError, OutputTokenAmountTooSmallError, TokensToNonTokenAddressError, UndefinedInputError, OutputAddressNetworkMismatchError, OutputTokenCategoryInvalidError, OutputTokenCommitmentInvalidError, OutputBchChangeLockedError, OutputTokenChangeLockedError, } from './Errors.js';
|
|
6
6
|
// ////////// PARAMETER VALIDATION ////////////////////////////////////////////
|
|
7
|
-
export function validateInput(utxo) {
|
|
7
|
+
export function validateInput(utxo, changeLocks) {
|
|
8
8
|
if (!utxo) {
|
|
9
9
|
throw new UndefinedInputError();
|
|
10
10
|
}
|
|
11
|
+
validateChangeLocks(changeLocks, utxo.token?.category);
|
|
11
12
|
}
|
|
12
|
-
export function validateOutput(output, network) {
|
|
13
|
+
export function validateOutput(output, network, changeLocks) {
|
|
14
|
+
validateChangeLocks(changeLocks, output.token?.category);
|
|
13
15
|
if (isOpReturnOutput(output))
|
|
14
16
|
return;
|
|
15
17
|
const minimumAmount = calculateDust(output);
|
|
@@ -39,6 +41,14 @@ export function validateOutput(output, network) {
|
|
|
39
41
|
throw new OutputAddressNetworkMismatchError(output.to, networkPrefix);
|
|
40
42
|
}
|
|
41
43
|
}
|
|
44
|
+
function validateChangeLocks(changeLocks, category) {
|
|
45
|
+
if (changeLocks.BCH) {
|
|
46
|
+
throw new OutputBchChangeLockedError();
|
|
47
|
+
}
|
|
48
|
+
if (category && changeLocks[category]) {
|
|
49
|
+
throw new OutputTokenChangeLockedError(category);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
42
52
|
export function isOpReturnOutput(output) {
|
|
43
53
|
return typeof output.to !== 'string' && output.to[0] === Op.OP_RETURN;
|
|
44
54
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.9",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@bitauth/libauth": "^3.1.0-next.8",
|
|
45
|
-
"@cashscript/utils": "^0.13.0-next.
|
|
45
|
+
"@cashscript/utils": "^0.13.0-next.9",
|
|
46
46
|
"@electrum-cash/network": "^4.1.3",
|
|
47
47
|
"fflate": "^0.8.2",
|
|
48
48
|
"semver": "^7.7.2"
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"typescript": "^5.9.2",
|
|
57
57
|
"vitest": "^4.0.15"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "1bbda54a000f6744bdb67a6379c7bfb0b784dceb"
|
|
60
60
|
}
|