recker 1.0.80 → 1.0.82-next.b8590bb
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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/raffel/client.d.ts +38 -0
- package/dist/raffel/client.js +282 -0
- package/dist/raffel/index.d.ts +2 -0
- package/dist/raffel/index.js +2 -0
- package/dist/raffel/types.d.ts +40 -0
- package/dist/raffel/types.js +14 -0
- package/dist/recker.d.ts +3 -0
- package/dist/recker.js +2 -0
- package/dist/version.js +1 -1
- package/package.json +6 -2
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,7 @@ export * from './utils/concurrency.js';
|
|
|
74
74
|
export * as presets from './presets/index.js';
|
|
75
75
|
export * as testing from './testing/index.js';
|
|
76
76
|
export * as protocols from './protocols/index.js';
|
|
77
|
+
export * from './raffel/index.js';
|
|
77
78
|
export * from './mcp/client.js';
|
|
78
79
|
export * from './mcp/contract.js';
|
|
79
80
|
export * as template from './template/index.js';
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ export * from './utils/concurrency.js';
|
|
|
74
74
|
export * as presets from './presets/index.js';
|
|
75
75
|
export * as testing from './testing/index.js';
|
|
76
76
|
export * as protocols from './protocols/index.js';
|
|
77
|
+
export * from './raffel/index.js';
|
|
77
78
|
export * from './mcp/client.js';
|
|
78
79
|
export * from './mcp/contract.js';
|
|
79
80
|
export * as template from './template/index.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ReckerWebSocket } from '../websocket/client.js';
|
|
2
|
+
import type { RaffelClientOptions, RaffelCallOptions, ChannelEventHandler } from './types.js';
|
|
3
|
+
type Listener = (...args: any[]) => void;
|
|
4
|
+
declare class SimpleEmitter {
|
|
5
|
+
private listeners;
|
|
6
|
+
on(event: string, listener: Listener): this;
|
|
7
|
+
once(event: string, listener: Listener): this;
|
|
8
|
+
off(event: string, listener: Listener): this;
|
|
9
|
+
emit(event: string, ...args: any[]): boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class RaffelClient extends SimpleEmitter {
|
|
12
|
+
private ws;
|
|
13
|
+
private pendingCalls;
|
|
14
|
+
private subscribedChannels;
|
|
15
|
+
private idCounter;
|
|
16
|
+
private defaultTimeout;
|
|
17
|
+
private onEvent?;
|
|
18
|
+
constructor(url: string, options?: RaffelClientOptions);
|
|
19
|
+
connect(): Promise<void>;
|
|
20
|
+
close(code?: number, reason?: string): void;
|
|
21
|
+
get isConnected(): boolean;
|
|
22
|
+
get raw(): ReckerWebSocket;
|
|
23
|
+
call<T = unknown>(procedure: string, payload?: unknown, options?: RaffelCallOptions): Promise<T>;
|
|
24
|
+
notify(procedure: string, payload?: unknown): void;
|
|
25
|
+
subscribe(channel: string, handler?: ChannelEventHandler): void;
|
|
26
|
+
unsubscribe(channel: string): void;
|
|
27
|
+
publish(channel: string, event: string, data?: unknown): void;
|
|
28
|
+
cancel(id: string): void;
|
|
29
|
+
private handleMessage;
|
|
30
|
+
private handleEnvelope;
|
|
31
|
+
private handleChannelMessage;
|
|
32
|
+
private nextId;
|
|
33
|
+
private extractBaseId;
|
|
34
|
+
private sendChannelSubscribe;
|
|
35
|
+
private sendRaw;
|
|
36
|
+
}
|
|
37
|
+
export declare function createRaffelClient(url: string, options?: RaffelClientOptions): RaffelClient;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { ReckerWebSocket } from '../websocket/client.js';
|
|
2
|
+
import { RaffelError } from './types.js';
|
|
3
|
+
class SimpleEmitter {
|
|
4
|
+
listeners = new Map();
|
|
5
|
+
on(event, listener) {
|
|
6
|
+
const set = this.listeners.get(event) ?? new Set();
|
|
7
|
+
set.add(listener);
|
|
8
|
+
this.listeners.set(event, set);
|
|
9
|
+
return this;
|
|
10
|
+
}
|
|
11
|
+
once(event, listener) {
|
|
12
|
+
const wrapped = (...args) => {
|
|
13
|
+
this.off(event, wrapped);
|
|
14
|
+
listener(...args);
|
|
15
|
+
};
|
|
16
|
+
return this.on(event, wrapped);
|
|
17
|
+
}
|
|
18
|
+
off(event, listener) {
|
|
19
|
+
const set = this.listeners.get(event);
|
|
20
|
+
if (set) {
|
|
21
|
+
set.delete(listener);
|
|
22
|
+
if (set.size === 0)
|
|
23
|
+
this.listeners.delete(event);
|
|
24
|
+
}
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
emit(event, ...args) {
|
|
28
|
+
const set = this.listeners.get(event);
|
|
29
|
+
if (!set)
|
|
30
|
+
return false;
|
|
31
|
+
for (const listener of [...set]) {
|
|
32
|
+
listener(...args);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class RaffelClient extends SimpleEmitter {
|
|
38
|
+
ws;
|
|
39
|
+
pendingCalls = new Map();
|
|
40
|
+
subscribedChannels = new Map();
|
|
41
|
+
idCounter = 0;
|
|
42
|
+
defaultTimeout;
|
|
43
|
+
onEvent;
|
|
44
|
+
constructor(url, options = {}) {
|
|
45
|
+
super();
|
|
46
|
+
const { channels, channelHandlers, onEvent, defaultTimeout, ...wsOptions } = options;
|
|
47
|
+
this.defaultTimeout = defaultTimeout ?? 30_000;
|
|
48
|
+
this.onEvent = onEvent;
|
|
49
|
+
this.ws = new ReckerWebSocket(url, wsOptions);
|
|
50
|
+
if (channels) {
|
|
51
|
+
for (const ch of channels) {
|
|
52
|
+
this.subscribedChannels.set(ch, channelHandlers?.[ch] ?? null);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (channelHandlers) {
|
|
56
|
+
for (const [ch, handler] of Object.entries(channelHandlers)) {
|
|
57
|
+
if (!this.subscribedChannels.has(ch)) {
|
|
58
|
+
this.subscribedChannels.set(ch, handler);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
this.ws.on('message', (msg) => this.handleMessage(msg));
|
|
63
|
+
this.ws.on('open', () => {
|
|
64
|
+
this.emit('raffel:connected');
|
|
65
|
+
for (const [channel] of this.subscribedChannels) {
|
|
66
|
+
this.sendChannelSubscribe(channel);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
this.ws.on('close', (_code, _reason) => {
|
|
70
|
+
this.emit('raffel:disconnected');
|
|
71
|
+
for (const [id, pending] of this.pendingCalls) {
|
|
72
|
+
if (pending.timer)
|
|
73
|
+
clearTimeout(pending.timer);
|
|
74
|
+
pending.reject(new Error(`Connection closed while waiting for response to ${id}`));
|
|
75
|
+
}
|
|
76
|
+
this.pendingCalls.clear();
|
|
77
|
+
});
|
|
78
|
+
this.ws.on('reconnecting', (attempt, delay) => {
|
|
79
|
+
this.emit('ws:reconnecting', attempt, delay);
|
|
80
|
+
});
|
|
81
|
+
this.ws.on('error', (err) => {
|
|
82
|
+
this.emit('ws:error', err);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async connect() {
|
|
86
|
+
return this.ws.connect();
|
|
87
|
+
}
|
|
88
|
+
close(code, reason) {
|
|
89
|
+
for (const [id, pending] of this.pendingCalls) {
|
|
90
|
+
if (pending.timer)
|
|
91
|
+
clearTimeout(pending.timer);
|
|
92
|
+
pending.reject(new Error(`Connection closed while waiting for response to ${id}`));
|
|
93
|
+
}
|
|
94
|
+
this.pendingCalls.clear();
|
|
95
|
+
this.ws.close(code, reason);
|
|
96
|
+
}
|
|
97
|
+
get isConnected() {
|
|
98
|
+
return this.ws.isConnected;
|
|
99
|
+
}
|
|
100
|
+
get raw() {
|
|
101
|
+
return this.ws;
|
|
102
|
+
}
|
|
103
|
+
async call(procedure, payload, options) {
|
|
104
|
+
const id = this.nextId();
|
|
105
|
+
const timeout = options?.timeout ?? this.defaultTimeout;
|
|
106
|
+
const envelope = {
|
|
107
|
+
id,
|
|
108
|
+
procedure,
|
|
109
|
+
type: 'request',
|
|
110
|
+
payload: payload ?? {},
|
|
111
|
+
metadata: {},
|
|
112
|
+
};
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
let timer = null;
|
|
115
|
+
const cleanup = () => {
|
|
116
|
+
this.pendingCalls.delete(id);
|
|
117
|
+
if (timer)
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
if (options?.signal) {
|
|
120
|
+
options.signal.removeEventListener('abort', onAbort);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const onAbort = () => {
|
|
124
|
+
cleanup();
|
|
125
|
+
this.sendRaw({ id, type: 'cancel' });
|
|
126
|
+
reject(new Error(`Call to ${procedure} was aborted`));
|
|
127
|
+
};
|
|
128
|
+
if (timeout > 0) {
|
|
129
|
+
timer = setTimeout(() => {
|
|
130
|
+
cleanup();
|
|
131
|
+
reject(new Error(`Call to ${procedure} timed out after ${timeout}ms`));
|
|
132
|
+
}, timeout);
|
|
133
|
+
}
|
|
134
|
+
if (options?.signal) {
|
|
135
|
+
if (options.signal.aborted) {
|
|
136
|
+
reject(new Error(`Call to ${procedure} was aborted`));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
140
|
+
}
|
|
141
|
+
this.pendingCalls.set(id, { resolve, reject, timer });
|
|
142
|
+
this.ws.sendJSON(envelope);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
notify(procedure, payload) {
|
|
146
|
+
const envelope = {
|
|
147
|
+
id: this.nextId(),
|
|
148
|
+
procedure,
|
|
149
|
+
type: 'event',
|
|
150
|
+
payload: payload ?? {},
|
|
151
|
+
metadata: {},
|
|
152
|
+
};
|
|
153
|
+
this.ws.sendJSON(envelope);
|
|
154
|
+
}
|
|
155
|
+
subscribe(channel, handler) {
|
|
156
|
+
this.subscribedChannels.set(channel, handler ?? null);
|
|
157
|
+
if (this.ws.isConnected) {
|
|
158
|
+
this.sendChannelSubscribe(channel);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
unsubscribe(channel) {
|
|
162
|
+
this.subscribedChannels.delete(channel);
|
|
163
|
+
if (this.ws.isConnected) {
|
|
164
|
+
const msg = {
|
|
165
|
+
id: this.nextId(),
|
|
166
|
+
type: 'unsubscribe',
|
|
167
|
+
channel,
|
|
168
|
+
};
|
|
169
|
+
this.ws.sendJSON(msg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
publish(channel, event, data) {
|
|
173
|
+
const msg = {
|
|
174
|
+
id: this.nextId(),
|
|
175
|
+
type: 'publish',
|
|
176
|
+
channel,
|
|
177
|
+
event,
|
|
178
|
+
data,
|
|
179
|
+
};
|
|
180
|
+
this.ws.sendJSON(msg);
|
|
181
|
+
}
|
|
182
|
+
cancel(id) {
|
|
183
|
+
const pending = this.pendingCalls.get(id);
|
|
184
|
+
if (pending) {
|
|
185
|
+
if (pending.timer)
|
|
186
|
+
clearTimeout(pending.timer);
|
|
187
|
+
pending.reject(new Error(`Call ${id} was cancelled`));
|
|
188
|
+
this.pendingCalls.delete(id);
|
|
189
|
+
}
|
|
190
|
+
this.sendRaw({ id, type: 'cancel' });
|
|
191
|
+
}
|
|
192
|
+
handleMessage(msg) {
|
|
193
|
+
if (typeof msg.data !== 'string')
|
|
194
|
+
return;
|
|
195
|
+
let parsed;
|
|
196
|
+
try {
|
|
197
|
+
parsed = JSON.parse(msg.data);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (parsed.channel) {
|
|
203
|
+
this.handleChannelMessage(parsed);
|
|
204
|
+
}
|
|
205
|
+
else if (parsed.procedure || parsed.type === 'cancel') {
|
|
206
|
+
this.handleEnvelope(parsed);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.emit('raffel:unknown', parsed);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
handleEnvelope(envelope) {
|
|
213
|
+
const { id, type, procedure, payload } = envelope;
|
|
214
|
+
if (type === 'event' && procedure) {
|
|
215
|
+
this.onEvent?.(procedure, payload);
|
|
216
|
+
this.emit('raffel:event', procedure, payload);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (type === 'response' || type === 'error') {
|
|
220
|
+
const baseId = this.extractBaseId(id);
|
|
221
|
+
const pending = this.pendingCalls.get(baseId);
|
|
222
|
+
if (!pending)
|
|
223
|
+
return;
|
|
224
|
+
if (pending.timer)
|
|
225
|
+
clearTimeout(pending.timer);
|
|
226
|
+
this.pendingCalls.delete(baseId);
|
|
227
|
+
if (type === 'error') {
|
|
228
|
+
pending.reject(new RaffelError(payload, procedure));
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
pending.resolve(payload);
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
handleChannelMessage(msg) {
|
|
237
|
+
const { type, channel } = msg;
|
|
238
|
+
switch (type) {
|
|
239
|
+
case 'subscribed':
|
|
240
|
+
this.emit('raffel:channel:subscribed', channel, msg.members);
|
|
241
|
+
break;
|
|
242
|
+
case 'unsubscribed':
|
|
243
|
+
this.emit('raffel:channel:unsubscribed', channel);
|
|
244
|
+
break;
|
|
245
|
+
case 'event': {
|
|
246
|
+
const event = msg.event ?? '';
|
|
247
|
+
const data = msg.data;
|
|
248
|
+
const handler = this.subscribedChannels.get(channel);
|
|
249
|
+
if (handler)
|
|
250
|
+
handler(event, data);
|
|
251
|
+
this.emit('raffel:channel:event', channel, event, data);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
nextId() {
|
|
257
|
+
return `req-${++this.idCounter}`;
|
|
258
|
+
}
|
|
259
|
+
extractBaseId(id) {
|
|
260
|
+
const suffixes = [':response', ':error'];
|
|
261
|
+
for (const suffix of suffixes) {
|
|
262
|
+
if (id.endsWith(suffix)) {
|
|
263
|
+
return id.slice(0, -suffix.length);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return id;
|
|
267
|
+
}
|
|
268
|
+
sendChannelSubscribe(channel) {
|
|
269
|
+
const msg = {
|
|
270
|
+
id: this.nextId(),
|
|
271
|
+
type: 'subscribe',
|
|
272
|
+
channel,
|
|
273
|
+
};
|
|
274
|
+
this.ws.sendJSON(msg);
|
|
275
|
+
}
|
|
276
|
+
sendRaw(data) {
|
|
277
|
+
this.ws.sendJSON(data);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
export function createRaffelClient(url, options) {
|
|
281
|
+
return new RaffelClient(url, options);
|
|
282
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { WebSocketOptions } from '../websocket/client.js';
|
|
2
|
+
export interface RaffelEnvelope {
|
|
3
|
+
id: string;
|
|
4
|
+
procedure?: string;
|
|
5
|
+
type: string;
|
|
6
|
+
payload?: unknown;
|
|
7
|
+
metadata?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface RaffelChannelMessage {
|
|
10
|
+
id?: string;
|
|
11
|
+
type: string;
|
|
12
|
+
channel: string;
|
|
13
|
+
event?: string;
|
|
14
|
+
data?: unknown;
|
|
15
|
+
members?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface RaffelErrorPayload {
|
|
18
|
+
code: string;
|
|
19
|
+
status: number;
|
|
20
|
+
message: string;
|
|
21
|
+
details?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export type ChannelEventHandler = (event: string, data: unknown) => void;
|
|
24
|
+
export interface RaffelClientOptions extends WebSocketOptions {
|
|
25
|
+
channels?: string[];
|
|
26
|
+
channelHandlers?: Record<string, ChannelEventHandler>;
|
|
27
|
+
onEvent?: (procedure: string, payload: unknown) => void;
|
|
28
|
+
defaultTimeout?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface RaffelCallOptions {
|
|
31
|
+
timeout?: number;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export declare class RaffelError extends Error {
|
|
35
|
+
code: string;
|
|
36
|
+
status: number;
|
|
37
|
+
details?: unknown;
|
|
38
|
+
procedure?: string;
|
|
39
|
+
constructor(payload: RaffelErrorPayload, procedure?: string);
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class RaffelError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
details;
|
|
5
|
+
procedure;
|
|
6
|
+
constructor(payload, procedure) {
|
|
7
|
+
super(payload.message);
|
|
8
|
+
this.name = 'RaffelError';
|
|
9
|
+
this.code = payload.code;
|
|
10
|
+
this.status = payload.status;
|
|
11
|
+
this.details = payload.details;
|
|
12
|
+
this.procedure = procedure;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/recker.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { getVersion, getVersionInfo } from './version.js';
|
|
|
3
3
|
import { type RequestPromise } from './core/request-promise.js';
|
|
4
4
|
import type { RequestOptions } from './types/index.js';
|
|
5
5
|
import { type WebSocketOptions, type ReckerWebSocket } from './websocket/client.js';
|
|
6
|
+
import { type RaffelClient } from './raffel/client.js';
|
|
7
|
+
import type { RaffelClientOptions } from './raffel/types.js';
|
|
6
8
|
import { createWhois, type WhoisResult, type WhoisOptions } from './utils/whois.js';
|
|
7
9
|
import { type DNSClientOptions, type DNSClient } from './dns/index.js';
|
|
8
10
|
import type { AIClientConfig } from './types/ai.js';
|
|
@@ -43,6 +45,7 @@ export declare const recker: {
|
|
|
43
45
|
dns: typeof dns;
|
|
44
46
|
dnsSecurity: typeof dnsSecurity;
|
|
45
47
|
ws: typeof ws;
|
|
48
|
+
raffel: (url: string, options?: RaffelClientOptions) => RaffelClient;
|
|
46
49
|
video: typeof video;
|
|
47
50
|
isVideoSupported: typeof isVideoSupported;
|
|
48
51
|
extractVideo: typeof extractVideo;
|
package/dist/recker.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createClient } from './core/client.js';
|
|
2
2
|
import { getVersion, getVersionSync, getVersionInfo } from './version.js';
|
|
3
3
|
import { createWebSocket } from './websocket/client.js';
|
|
4
|
+
import { createRaffelClient } from './raffel/client.js';
|
|
4
5
|
import { whois as whoisLookup, isDomainAvailable, createWhois } from './utils/whois.js';
|
|
5
6
|
import { createDNS } from './dns/index.js';
|
|
6
7
|
import { createAI } from './ai/index.js';
|
|
@@ -150,6 +151,7 @@ export const recker = {
|
|
|
150
151
|
dns,
|
|
151
152
|
dnsSecurity,
|
|
152
153
|
ws,
|
|
154
|
+
raffel: (url, options) => createRaffelClient(url, options),
|
|
153
155
|
video,
|
|
154
156
|
isVideoSupported,
|
|
155
157
|
extractVideo,
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.82-next.b8590bb",
|
|
4
4
|
"description": "Multi-Protocol SDK for the AI Era - HTTP, WebSocket, DNS, FTP, SFTP, Telnet, HLS unified with AI providers and MCP tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -211,6 +211,10 @@
|
|
|
211
211
|
"types": "./dist/presets/*.d.ts",
|
|
212
212
|
"import": "./dist/presets/*.js"
|
|
213
213
|
},
|
|
214
|
+
"./raffel": {
|
|
215
|
+
"types": "./dist/raffel/index.d.ts",
|
|
216
|
+
"import": "./dist/raffel/index.js"
|
|
217
|
+
},
|
|
214
218
|
"./package.json": "./package.json"
|
|
215
219
|
},
|
|
216
220
|
"browser": "./dist/browser/browser/index.js",
|
|
@@ -303,7 +307,7 @@
|
|
|
303
307
|
"build:browser": "tsc -p tsconfig.browser.json",
|
|
304
308
|
"build:browser:bundle": "pnpm build:browser && node scripts/bundle-browser.js",
|
|
305
309
|
"build:cli": "node scripts/bundle-cli.js",
|
|
306
|
-
"build:binary": "
|
|
310
|
+
"build:binary": "node scripts/build-sea.js",
|
|
307
311
|
"test": "NODE_OPTIONS='--max-old-space-size=512' vitest run",
|
|
308
312
|
"test:coverage": "NODE_OPTIONS='--max-old-space-size=512' vitest run --coverage",
|
|
309
313
|
"test:curl-impersonate:coverage": "NODE_OPTIONS='--max-old-space-size=512' vitest run --config vitest.config.curl-impersonate.ts --coverage",
|