@distrohelena/canton-typescript-sdk 0.1.49 → 0.1.50
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.
|
@@ -9,14 +9,16 @@ class GrpcTransportError extends transport_error_js_1.TransportError {
|
|
|
9
9
|
methodName;
|
|
10
10
|
metadata;
|
|
11
11
|
status;
|
|
12
|
+
errorInfo;
|
|
12
13
|
cause;
|
|
13
|
-
constructor(message, rawError, metadata, status) {
|
|
14
|
+
constructor(message, rawError, metadata, status, errorInfo) {
|
|
14
15
|
super(message);
|
|
15
16
|
this.grpcCode = rawError.code;
|
|
16
17
|
this.serviceName = rawError.serviceName;
|
|
17
18
|
this.methodName = rawError.methodName;
|
|
18
19
|
this.metadata = metadata;
|
|
19
20
|
this.status = status;
|
|
21
|
+
this.errorInfo = errorInfo;
|
|
20
22
|
this.cause = rawError;
|
|
21
23
|
}
|
|
22
24
|
static fromUnknown(error) {
|
|
@@ -25,7 +27,8 @@ class GrpcTransportError extends transport_error_js_1.TransportError {
|
|
|
25
27
|
}
|
|
26
28
|
const metadata = copyMetadata(error.meta);
|
|
27
29
|
const status = decodeStatusDetails(error.meta);
|
|
28
|
-
|
|
30
|
+
const errorInfo = decodeErrorInfo(status);
|
|
31
|
+
return new GrpcTransportError(formatMessage(error, status, errorInfo), error, metadata, status, errorInfo);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
exports.GrpcTransportError = GrpcTransportError;
|
|
@@ -71,10 +74,140 @@ function toBinary(value) {
|
|
|
71
74
|
}
|
|
72
75
|
return value instanceof Uint8Array ? value : undefined;
|
|
73
76
|
}
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Decodes the google.rpc.ErrorInfo entry from a status' packed details. The message type is small and
|
|
79
|
+
* stable (reason/domain/metadata), so it is read directly off the wire instead of adding the whole
|
|
80
|
+
* google.rpc.error_details surface to the generated bindings. Anything malformed yields undefined rather
|
|
81
|
+
* than masking the original RPC failure.
|
|
82
|
+
*/
|
|
83
|
+
function decodeErrorInfo(status) {
|
|
84
|
+
for (const detail of status?.details ?? []) {
|
|
85
|
+
if (typeof detail.typeUrl !== "string" || !detail.typeUrl.endsWith("/google.rpc.ErrorInfo") || !(detail.value instanceof Uint8Array)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const decoded = readErrorInfo(detail.value);
|
|
89
|
+
if (decoded !== undefined) {
|
|
90
|
+
return decoded;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
function readErrorInfo(bytes) {
|
|
96
|
+
try {
|
|
97
|
+
let reason = "";
|
|
98
|
+
let domain = "";
|
|
99
|
+
const metadata = {};
|
|
100
|
+
const reader = wireReader(bytes);
|
|
101
|
+
while (reader.remaining()) {
|
|
102
|
+
const tag = reader.varint();
|
|
103
|
+
const field = Math.floor(tag / 8);
|
|
104
|
+
const wireType = tag % 8;
|
|
105
|
+
if (field === 1 && wireType === 2) {
|
|
106
|
+
reason = reader.string();
|
|
107
|
+
}
|
|
108
|
+
else if (field === 2 && wireType === 2) {
|
|
109
|
+
domain = reader.string();
|
|
110
|
+
}
|
|
111
|
+
else if (field === 3 && wireType === 2) {
|
|
112
|
+
const entry = wireReader(reader.bytes());
|
|
113
|
+
let key = "";
|
|
114
|
+
let value = "";
|
|
115
|
+
while (entry.remaining()) {
|
|
116
|
+
const entryTag = entry.varint();
|
|
117
|
+
if (Math.floor(entryTag / 8) === 1 && entryTag % 8 === 2) {
|
|
118
|
+
key = entry.string();
|
|
119
|
+
}
|
|
120
|
+
else if (Math.floor(entryTag / 8) === 2 && entryTag % 8 === 2) {
|
|
121
|
+
value = entry.string();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
entry.skip(entryTag % 8);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (key.length > 0) {
|
|
128
|
+
metadata[key] = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
reader.skip(wireType);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return reason.length === 0 ? undefined : Object.freeze({ reason, domain, metadata: Object.freeze(metadata) });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function wireReader(bytes) {
|
|
142
|
+
let offset = 0;
|
|
143
|
+
const varint = () => {
|
|
144
|
+
let result = 0;
|
|
145
|
+
let shift = 1;
|
|
146
|
+
for (let index = 0; index < 8; index += 1) {
|
|
147
|
+
const byte = bytes[offset];
|
|
148
|
+
if (byte === undefined) {
|
|
149
|
+
throw new Error("truncated varint");
|
|
150
|
+
}
|
|
151
|
+
offset += 1;
|
|
152
|
+
result += (byte & 0x7f) * shift;
|
|
153
|
+
if ((byte & 0x80) === 0) {
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
shift *= 128;
|
|
157
|
+
}
|
|
158
|
+
throw new Error("varint too long");
|
|
159
|
+
};
|
|
160
|
+
const rawBytes = () => {
|
|
161
|
+
const length = varint();
|
|
162
|
+
if (offset + length > bytes.length) {
|
|
163
|
+
throw new Error("truncated bytes");
|
|
164
|
+
}
|
|
165
|
+
const slice = bytes.subarray(offset, offset + length);
|
|
166
|
+
offset += length;
|
|
167
|
+
return slice;
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
remaining: () => offset < bytes.length,
|
|
171
|
+
varint,
|
|
172
|
+
bytes: rawBytes,
|
|
173
|
+
string: () => new TextDecoder("utf-8", { fatal: true }).decode(rawBytes()),
|
|
174
|
+
skip: (wireType) => {
|
|
175
|
+
if (wireType === 0) {
|
|
176
|
+
varint();
|
|
177
|
+
}
|
|
178
|
+
else if (wireType === 1) {
|
|
179
|
+
offset += 8;
|
|
180
|
+
}
|
|
181
|
+
else if (wireType === 2) {
|
|
182
|
+
rawBytes();
|
|
183
|
+
}
|
|
184
|
+
else if (wireType === 5) {
|
|
185
|
+
offset += 4;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
throw new Error(`unsupported wire type ${wireType}`);
|
|
189
|
+
}
|
|
190
|
+
if (offset > bytes.length) {
|
|
191
|
+
throw new Error("truncated field");
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function formatMessage(error, status, errorInfo) {
|
|
75
197
|
const operation = [error.serviceName, error.methodName]
|
|
76
198
|
.filter((part) => part !== undefined)
|
|
77
199
|
.join(".");
|
|
78
200
|
const location = operation.length > 0 ? ` from ${operation}` : "";
|
|
79
|
-
|
|
201
|
+
const parts = [];
|
|
202
|
+
// Participants often pair a generic transport message with the real explanation in the status trailer.
|
|
203
|
+
if (status !== undefined && status.message.length > 0 && status.message !== error.message) {
|
|
204
|
+
parts.push(`status: ${status.message}`);
|
|
205
|
+
}
|
|
206
|
+
if (errorInfo !== undefined) {
|
|
207
|
+
const metadataEntries = Object.entries(errorInfo.metadata);
|
|
208
|
+
parts.push(`reason: ${errorInfo.reason}`
|
|
209
|
+
+ (metadataEntries.length === 0 ? "" : ` (${metadataEntries.map(([key, value]) => `${key}=${value}`).join(", ")})`));
|
|
210
|
+
}
|
|
211
|
+
const suffix = parts.length === 0 ? "" : ` [${parts.join("; ")}]`;
|
|
212
|
+
return `gRPC ${error.code}${location}: ${error.message}${suffix}`;
|
|
80
213
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Status as GrpcStatusDetails } from "../../transports/grpc/generated/canton/google/rpc/status.js";
|
|
2
2
|
import { TransportError } from "./transport-error.js";
|
|
3
3
|
export type GrpcErrorMetadata = Readonly<Record<string, readonly string[]>>;
|
|
4
|
+
/** The decoded google.rpc.ErrorInfo detail: the machine-readable reason behind a gRPC failure. */
|
|
5
|
+
export interface GrpcErrorInfo {
|
|
6
|
+
readonly reason: string;
|
|
7
|
+
readonly domain: string;
|
|
8
|
+
readonly metadata: Readonly<Record<string, string>>;
|
|
9
|
+
}
|
|
4
10
|
export type { GrpcStatusDetails };
|
|
5
11
|
export declare class GrpcTransportError extends TransportError {
|
|
6
12
|
readonly grpcCode: string;
|
|
@@ -8,6 +14,7 @@ export declare class GrpcTransportError extends TransportError {
|
|
|
8
14
|
readonly methodName?: string;
|
|
9
15
|
readonly metadata: GrpcErrorMetadata;
|
|
10
16
|
readonly status?: GrpcStatusDetails;
|
|
17
|
+
readonly errorInfo?: GrpcErrorInfo;
|
|
11
18
|
readonly cause: Error;
|
|
12
19
|
private constructor();
|
|
13
20
|
static fromUnknown(error: unknown): GrpcTransportError | undefined;
|
|
@@ -6,14 +6,16 @@ export class GrpcTransportError extends TransportError {
|
|
|
6
6
|
methodName;
|
|
7
7
|
metadata;
|
|
8
8
|
status;
|
|
9
|
+
errorInfo;
|
|
9
10
|
cause;
|
|
10
|
-
constructor(message, rawError, metadata, status) {
|
|
11
|
+
constructor(message, rawError, metadata, status, errorInfo) {
|
|
11
12
|
super(message);
|
|
12
13
|
this.grpcCode = rawError.code;
|
|
13
14
|
this.serviceName = rawError.serviceName;
|
|
14
15
|
this.methodName = rawError.methodName;
|
|
15
16
|
this.metadata = metadata;
|
|
16
17
|
this.status = status;
|
|
18
|
+
this.errorInfo = errorInfo;
|
|
17
19
|
this.cause = rawError;
|
|
18
20
|
}
|
|
19
21
|
static fromUnknown(error) {
|
|
@@ -22,7 +24,8 @@ export class GrpcTransportError extends TransportError {
|
|
|
22
24
|
}
|
|
23
25
|
const metadata = copyMetadata(error.meta);
|
|
24
26
|
const status = decodeStatusDetails(error.meta);
|
|
25
|
-
|
|
27
|
+
const errorInfo = decodeErrorInfo(status);
|
|
28
|
+
return new GrpcTransportError(formatMessage(error, status, errorInfo), error, metadata, status, errorInfo);
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
function isRpcErrorLike(error) {
|
|
@@ -67,10 +70,140 @@ function toBinary(value) {
|
|
|
67
70
|
}
|
|
68
71
|
return value instanceof Uint8Array ? value : undefined;
|
|
69
72
|
}
|
|
70
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Decodes the google.rpc.ErrorInfo entry from a status' packed details. The message type is small and
|
|
75
|
+
* stable (reason/domain/metadata), so it is read directly off the wire instead of adding the whole
|
|
76
|
+
* google.rpc.error_details surface to the generated bindings. Anything malformed yields undefined rather
|
|
77
|
+
* than masking the original RPC failure.
|
|
78
|
+
*/
|
|
79
|
+
function decodeErrorInfo(status) {
|
|
80
|
+
for (const detail of status?.details ?? []) {
|
|
81
|
+
if (typeof detail.typeUrl !== "string" || !detail.typeUrl.endsWith("/google.rpc.ErrorInfo") || !(detail.value instanceof Uint8Array)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const decoded = readErrorInfo(detail.value);
|
|
85
|
+
if (decoded !== undefined) {
|
|
86
|
+
return decoded;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
function readErrorInfo(bytes) {
|
|
92
|
+
try {
|
|
93
|
+
let reason = "";
|
|
94
|
+
let domain = "";
|
|
95
|
+
const metadata = {};
|
|
96
|
+
const reader = wireReader(bytes);
|
|
97
|
+
while (reader.remaining()) {
|
|
98
|
+
const tag = reader.varint();
|
|
99
|
+
const field = Math.floor(tag / 8);
|
|
100
|
+
const wireType = tag % 8;
|
|
101
|
+
if (field === 1 && wireType === 2) {
|
|
102
|
+
reason = reader.string();
|
|
103
|
+
}
|
|
104
|
+
else if (field === 2 && wireType === 2) {
|
|
105
|
+
domain = reader.string();
|
|
106
|
+
}
|
|
107
|
+
else if (field === 3 && wireType === 2) {
|
|
108
|
+
const entry = wireReader(reader.bytes());
|
|
109
|
+
let key = "";
|
|
110
|
+
let value = "";
|
|
111
|
+
while (entry.remaining()) {
|
|
112
|
+
const entryTag = entry.varint();
|
|
113
|
+
if (Math.floor(entryTag / 8) === 1 && entryTag % 8 === 2) {
|
|
114
|
+
key = entry.string();
|
|
115
|
+
}
|
|
116
|
+
else if (Math.floor(entryTag / 8) === 2 && entryTag % 8 === 2) {
|
|
117
|
+
value = entry.string();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
entry.skip(entryTag % 8);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (key.length > 0) {
|
|
124
|
+
metadata[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
reader.skip(wireType);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return reason.length === 0 ? undefined : Object.freeze({ reason, domain, metadata: Object.freeze(metadata) });
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function wireReader(bytes) {
|
|
138
|
+
let offset = 0;
|
|
139
|
+
const varint = () => {
|
|
140
|
+
let result = 0;
|
|
141
|
+
let shift = 1;
|
|
142
|
+
for (let index = 0; index < 8; index += 1) {
|
|
143
|
+
const byte = bytes[offset];
|
|
144
|
+
if (byte === undefined) {
|
|
145
|
+
throw new Error("truncated varint");
|
|
146
|
+
}
|
|
147
|
+
offset += 1;
|
|
148
|
+
result += (byte & 0x7f) * shift;
|
|
149
|
+
if ((byte & 0x80) === 0) {
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
shift *= 128;
|
|
153
|
+
}
|
|
154
|
+
throw new Error("varint too long");
|
|
155
|
+
};
|
|
156
|
+
const rawBytes = () => {
|
|
157
|
+
const length = varint();
|
|
158
|
+
if (offset + length > bytes.length) {
|
|
159
|
+
throw new Error("truncated bytes");
|
|
160
|
+
}
|
|
161
|
+
const slice = bytes.subarray(offset, offset + length);
|
|
162
|
+
offset += length;
|
|
163
|
+
return slice;
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
remaining: () => offset < bytes.length,
|
|
167
|
+
varint,
|
|
168
|
+
bytes: rawBytes,
|
|
169
|
+
string: () => new TextDecoder("utf-8", { fatal: true }).decode(rawBytes()),
|
|
170
|
+
skip: (wireType) => {
|
|
171
|
+
if (wireType === 0) {
|
|
172
|
+
varint();
|
|
173
|
+
}
|
|
174
|
+
else if (wireType === 1) {
|
|
175
|
+
offset += 8;
|
|
176
|
+
}
|
|
177
|
+
else if (wireType === 2) {
|
|
178
|
+
rawBytes();
|
|
179
|
+
}
|
|
180
|
+
else if (wireType === 5) {
|
|
181
|
+
offset += 4;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
throw new Error(`unsupported wire type ${wireType}`);
|
|
185
|
+
}
|
|
186
|
+
if (offset > bytes.length) {
|
|
187
|
+
throw new Error("truncated field");
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function formatMessage(error, status, errorInfo) {
|
|
71
193
|
const operation = [error.serviceName, error.methodName]
|
|
72
194
|
.filter((part) => part !== undefined)
|
|
73
195
|
.join(".");
|
|
74
196
|
const location = operation.length > 0 ? ` from ${operation}` : "";
|
|
75
|
-
|
|
197
|
+
const parts = [];
|
|
198
|
+
// Participants often pair a generic transport message with the real explanation in the status trailer.
|
|
199
|
+
if (status !== undefined && status.message.length > 0 && status.message !== error.message) {
|
|
200
|
+
parts.push(`status: ${status.message}`);
|
|
201
|
+
}
|
|
202
|
+
if (errorInfo !== undefined) {
|
|
203
|
+
const metadataEntries = Object.entries(errorInfo.metadata);
|
|
204
|
+
parts.push(`reason: ${errorInfo.reason}`
|
|
205
|
+
+ (metadataEntries.length === 0 ? "" : ` (${metadataEntries.map(([key, value]) => `${key}=${value}`).join(", ")})`));
|
|
206
|
+
}
|
|
207
|
+
const suffix = parts.length === 0 ? "" : ` [${parts.join("; ")}]`;
|
|
208
|
+
return `gRPC ${error.code}${location}: ${error.message}${suffix}`;
|
|
76
209
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -301,6 +301,7 @@ export { AuthenticationError } from "./core/errors/authentication-error.js";
|
|
|
301
301
|
export { AuthorizationError } from "./core/errors/authorization-error.js";
|
|
302
302
|
export { TransportError } from "./core/errors/transport-error.js";
|
|
303
303
|
export { GrpcTransportError } from "./core/errors/grpc-transport-error.js";
|
|
304
|
+
export type { GrpcErrorInfo } from "./core/errors/grpc-transport-error.js";
|
|
304
305
|
export type { GrpcErrorMetadata, GrpcStatusDetails, } from "./core/errors/grpc-transport-error.js";
|
|
305
306
|
export { SigningError } from "./core/errors/signing-error.js";
|
|
306
307
|
export { TimeoutError } from "./core/errors/timeout-error.js";
|
package/node/start-local.sh
CHANGED
|
@@ -929,7 +929,19 @@ prerequisite_services() {
|
|
|
929
929
|
printf '%s\n' "${services[@]}"
|
|
930
930
|
}
|
|
931
931
|
|
|
932
|
+
resolve_pqs_enabled() {
|
|
933
|
+
local value="${LOCALNET_PQS:-1}"
|
|
934
|
+
if [[ "$value" != "0" && "$value" != "1" ]]; then
|
|
935
|
+
echo "LOCALNET_PQS must be 0 or 1." >&2
|
|
936
|
+
return 1
|
|
937
|
+
fi
|
|
938
|
+
printf '%s\n' "$value"
|
|
939
|
+
}
|
|
940
|
+
|
|
932
941
|
dependent_services() {
|
|
942
|
+
if [[ "$(resolve_pqs_enabled)" == "0" ]]; then
|
|
943
|
+
return 0
|
|
944
|
+
fi
|
|
933
945
|
printf '%s\n' pqs-app-provider pqs-sv
|
|
934
946
|
}
|
|
935
947
|
|
|
@@ -1042,6 +1054,23 @@ wait_for_canton_health() {
|
|
|
1042
1054
|
wait_for_container_readiness canton canton_and_extras_are_ready "$extra_participants"
|
|
1043
1055
|
}
|
|
1044
1056
|
|
|
1057
|
+
container_is_healthy() {
|
|
1058
|
+
local _container="$1"
|
|
1059
|
+
local lifecycle_state="$2"
|
|
1060
|
+
local health_status="$3"
|
|
1061
|
+
[[ "$lifecycle_state" == "running" && "$health_status" == "healthy" ]]
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
wait_for_onboarding_health() {
|
|
1065
|
+
local onboarding_container
|
|
1066
|
+
onboarding_container="$(docker ps -a --filter name=splice-onboarding --format '{{.Names}}' | head -n 1)"
|
|
1067
|
+
if [[ -z "$onboarding_container" ]]; then
|
|
1068
|
+
echo "Unable to resolve the splice-onboarding container for readiness." >&2
|
|
1069
|
+
return 1
|
|
1070
|
+
fi
|
|
1071
|
+
wait_for_container_readiness "$onboarding_container" container_is_healthy
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1045
1074
|
provision_extra_pqs() {
|
|
1046
1075
|
local count="$1"
|
|
1047
1076
|
if (( count == 0 )); then
|
|
@@ -1235,7 +1264,13 @@ start_ledger_stack() {
|
|
|
1235
1264
|
# gating so its healthcheck can retry until those apps are ready; PQS still
|
|
1236
1265
|
# starts afterward and retains its onboarding health dependency.
|
|
1237
1266
|
start_splice_services compose_args
|
|
1238
|
-
|
|
1267
|
+
if (( ${#followup_services[@]} > 0 )); then
|
|
1268
|
+
docker_compose "${compose_args[@]}" up -d --no-recreate "${followup_services[@]}"
|
|
1269
|
+
else
|
|
1270
|
+
# Without PQS nothing carries a service_healthy dependency on splice-onboarding, so wait explicitly —
|
|
1271
|
+
# the participant is not usable until onboarding finishes.
|
|
1272
|
+
wait_for_onboarding_health
|
|
1273
|
+
fi
|
|
1239
1274
|
|
|
1240
1275
|
if (( extra_participants > 0 )); then
|
|
1241
1276
|
wait_for_container_readiness splice container_health_is_ready
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@distrohelena/canton-typescript-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -140,6 +140,8 @@
|
|
|
140
140
|
"test": "vitest run --exclude 'tests/live/**' --maxWorkers=1 --testTimeout=15000",
|
|
141
141
|
"test:property": "vitest run tests/property --maxWorkers=1 --testTimeout=15000",
|
|
142
142
|
"test:live": "vitest run --dir tests/live --maxWorkers=1 --testTimeout=15000",
|
|
143
|
+
"test:live:matrix": "node scripts/live-matrix.mjs",
|
|
144
|
+
"test:full": "npm run test && node scripts/live-matrix.mjs",
|
|
143
145
|
"test:live:fuzz": "vitest run tests/live/specs/live-stateful-fuzzing.test.ts --maxWorkers=1 --testTimeout=300000",
|
|
144
146
|
"test:unit": "vitest run tests/unit",
|
|
145
147
|
"verify:pack": "node ./scripts/verify-npm-pack.mjs"
|