browserscale-ts 1.8.0 → 1.10.0
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/README.md +6 -3
- package/dist/browser.d.ts +1 -1
- package/dist/browserscale.browser.js +207 -66
- package/dist/client.d.ts +82 -4
- package/dist/client.js +132 -4
- package/dist/gen/wrc_pb.d.ts +236 -17
- package/dist/gen/wrc_pb.js +122 -97
- package/dist/index.d.ts +7 -4
- package/dist/index.js +9 -8
- package/dist/internal/convert.d.ts +4 -2
- package/dist/internal/convert.js +31 -3
- package/dist/network-capture.d.ts +2 -2
- package/dist/network-capture.js +2 -2
- package/dist/types.d.ts +82 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CloudBrowser } from "./client.ts";
|
|
2
2
|
import { BrowserConfig } from "./config.ts";
|
|
3
|
-
import type { BrowserInfo } from "./types.ts";
|
|
3
|
+
import type { BrowserInfo, SessionUsage } from "./types.ts";
|
|
4
4
|
export { CloudBrowser } from "./client.ts";
|
|
5
5
|
export { BrowserConfig } from "./config.ts";
|
|
6
6
|
export { Locator, css, js, node, at, AllFrames } from "./locator.ts";
|
|
7
7
|
export { DefaultWaitTimeoutMs, DefaultVisible, DefaultSteadyMs, } from "./defaults.ts";
|
|
8
8
|
export { BrowserScaleError, CommandError, ClickError, FillError, DragError, ScrollError, MoveError, SelectOptionError, WaitError, } from "./errors.ts";
|
|
9
|
-
export type { Rect, FrameInfo, PageInfo, Header, InterceptedRequest, InterceptedResponse, NetworkExchange, NetworkResourceType, NetworkServedFrom, NetworkBodies, NetworkCaptureOptions, WaitResult, WaitConditionStatus, OccluderInfo, ElementRef, NavigateResult, EvaluateResult, ElementResult, DragResult, SelectOptionResult, ScreenshotResult, ReadCanvasResult, DOMResult, InspectResult, RentResponse, BrowserInfo, IceServer, StreamAnswer, ReactionInfo, } from "./types.ts";
|
|
9
|
+
export type { Rect, FrameInfo, PageInfo, Header, InterceptedRequest, InterceptedResponse, NetworkExchange, NetworkResourceType, NetworkServedFrom, NetworkBodies, NetworkBody, NetworkBodyRange, NetworkCaptureOptions, WaitResult, WaitConditionStatus, OccluderInfo, ElementRef, NavigateResult, EvaluateResult, ElementResult, DragResult, SelectOptionResult, ScreenshotResult, ReadCanvasResult, DOMResult, InspectResult, RentResponse, BrowserInfo, SessionUsage, IceServer, StreamAnswer, ReactionInfo, } from "./types.ts";
|
|
10
10
|
export type { Button, ClickAction, ClickOpts, FillOpts, ReactionOpts, SelectOpts, WaitOpts, WaitUntil, NavigateOpts, LoadHTMLOpts, GetDOMOpts, GetObservationOpts, ScreenshotOpts, ReadCanvasOpts, } from "./options.ts";
|
|
11
11
|
export { type RequestPattern, type HeaderModification, type HeaderModificationAction, } from "./network.ts";
|
|
12
12
|
export { ScriptRun, ScriptFollow, type ScriptEvent, type ScriptEventHandler, type ScriptFinished, type ScriptLogEntry, type ScriptResult, type ScriptRunInfo, } from "./scripts.ts";
|
|
@@ -88,13 +88,16 @@ export declare function connectSession(grpcUrl: string, apiKey: string, sessionI
|
|
|
88
88
|
* @param apiKey - API key the session was rented with
|
|
89
89
|
* @param sessionId - id of the session to release
|
|
90
90
|
*
|
|
91
|
+
* @returns SessionUsage what the session consumed over its whole life;
|
|
92
|
+
* undefined when the server could not report it
|
|
93
|
+
*
|
|
91
94
|
* Rejects when the stop API refuses the request. Stopping a session that is
|
|
92
95
|
* already gone is a no-op rather than a failure.
|
|
93
96
|
*
|
|
94
97
|
* @example
|
|
95
|
-
* await stopBrowser(apiKey, sessionId);
|
|
98
|
+
* const usage = await stopBrowser(apiKey, sessionId);
|
|
96
99
|
*/
|
|
97
|
-
export declare function stopBrowser(apiKey: string, sessionId: string): Promise<
|
|
100
|
+
export declare function stopBrowser(apiKey: string, sessionId: string): Promise<SessionUsage | undefined>;
|
|
98
101
|
/**
|
|
99
102
|
* Reports the sessions an API key currently holds.
|
|
100
103
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
2
2
|
import { CloudBrowser } from "./client.js";
|
|
3
|
+
import { sessionUsageFromJson } from "./internal/convert.js";
|
|
3
4
|
// Public API ──────────────────────────────────────────────────────────
|
|
4
5
|
export { CloudBrowser } from "./client.js";
|
|
5
6
|
export { BrowserConfig } from "./config.js";
|
|
@@ -61,9 +62,7 @@ export async function rentBrowser(config) {
|
|
|
61
62
|
const transport = createGrpcTransport({
|
|
62
63
|
baseUrl: grpcBaseUrl(rentResp.grpcUrl),
|
|
63
64
|
});
|
|
64
|
-
return new CloudBrowser(transport, rentResp.sessionId, config.apiKey, rentResp.fingerprint,
|
|
65
|
-
await callStopApi(config.apiKey, rentResp.sessionId);
|
|
66
|
-
});
|
|
65
|
+
return new CloudBrowser(transport, rentResp.sessionId, config.apiKey, rentResp.fingerprint, () => callStopApi(config.apiKey, rentResp.sessionId));
|
|
67
66
|
}
|
|
68
67
|
/**
|
|
69
68
|
* Attaches to an already-rented session over a fresh gRPC connection.
|
|
@@ -90,9 +89,7 @@ export async function rentBrowser(config) {
|
|
|
90
89
|
*/
|
|
91
90
|
export function connectSession(grpcUrl, apiKey, sessionId) {
|
|
92
91
|
const transport = createGrpcTransport({ baseUrl: grpcBaseUrl(grpcUrl) });
|
|
93
|
-
return new CloudBrowser(transport, sessionId, apiKey, "",
|
|
94
|
-
await callStopApi(apiKey, sessionId);
|
|
95
|
-
});
|
|
92
|
+
return new CloudBrowser(transport, sessionId, apiKey, "", () => callStopApi(apiKey, sessionId));
|
|
96
93
|
}
|
|
97
94
|
/**
|
|
98
95
|
* Releases a session without needing a {@link CloudBrowser} handle.
|
|
@@ -104,14 +101,17 @@ export function connectSession(grpcUrl, apiKey, sessionId) {
|
|
|
104
101
|
* @param apiKey - API key the session was rented with
|
|
105
102
|
* @param sessionId - id of the session to release
|
|
106
103
|
*
|
|
104
|
+
* @returns SessionUsage what the session consumed over its whole life;
|
|
105
|
+
* undefined when the server could not report it
|
|
106
|
+
*
|
|
107
107
|
* Rejects when the stop API refuses the request. Stopping a session that is
|
|
108
108
|
* already gone is a no-op rather than a failure.
|
|
109
109
|
*
|
|
110
110
|
* @example
|
|
111
|
-
* await stopBrowser(apiKey, sessionId);
|
|
111
|
+
* const usage = await stopBrowser(apiKey, sessionId);
|
|
112
112
|
*/
|
|
113
113
|
export async function stopBrowser(apiKey, sessionId) {
|
|
114
|
-
await callStopApi(apiKey, sessionId);
|
|
114
|
+
return await callStopApi(apiKey, sessionId);
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* Reports the sessions an API key currently holds.
|
|
@@ -224,4 +224,5 @@ async function callStopApi(apiKey, sessionId) {
|
|
|
224
224
|
if (!resp.ok || !data.success) {
|
|
225
225
|
throw new Error(`stop failed: ${data.error ?? resp.statusText}`);
|
|
226
226
|
}
|
|
227
|
+
return sessionUsageFromJson(data.usage);
|
|
227
228
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Rect as ProtoRect, type ClickResult as ProtoClickResult, type FillResult as ProtoFillResult, type MoveResult as ProtoMoveResult, type ScrollResult as ProtoScrollResult, type DragResult as ProtoDragResult, type SelectOptionResult as ProtoSelectOptionResult, type WaitResult as ProtoWaitResult, type FrameInfo as ProtoFrameInfo, type PageInfo as ProtoPageInfo, type Header as ProtoHeader, type InterceptedRequest as ProtoInterceptedRequest, type InterceptedResponse as ProtoInterceptedResponse, type NetworkExchange as ProtoNetworkExchange, type HeaderModification as ProtoHeaderModification, type CookieParam as ProtoCookieParam, type StorageOriginEntry as ProtoStorageOriginEntry, type AuthSession as ProtoAuthSession } from "../gen/wrc_pb.ts";
|
|
2
|
-
import type { DragResult, ElementResult, FrameInfo, Header, InterceptedRequest, InterceptedResponse, NetworkExchange, PageInfo, Rect, SelectOptionResult, WaitResult } from "../types.ts";
|
|
1
|
+
import { type Rect as ProtoRect, type ClickResult as ProtoClickResult, type FillResult as ProtoFillResult, type MoveResult as ProtoMoveResult, type ScrollResult as ProtoScrollResult, type DragResult as ProtoDragResult, type SelectOptionResult as ProtoSelectOptionResult, type WaitResult as ProtoWaitResult, type FrameInfo as ProtoFrameInfo, type PageInfo as ProtoPageInfo, type SessionUsage as ProtoSessionUsage, type Header as ProtoHeader, type InterceptedRequest as ProtoInterceptedRequest, type InterceptedResponse as ProtoInterceptedResponse, type NetworkExchange as ProtoNetworkExchange, type HeaderModification as ProtoHeaderModification, type CookieParam as ProtoCookieParam, type StorageOriginEntry as ProtoStorageOriginEntry, type AuthSession as ProtoAuthSession } from "../gen/wrc_pb.ts";
|
|
2
|
+
import type { DragResult, ElementResult, FrameInfo, Header, InterceptedRequest, InterceptedResponse, NetworkExchange, PageInfo, Rect, SelectOptionResult, SessionUsage, WaitResult } from "../types.ts";
|
|
3
3
|
import type { CookieParam } from "../cookies.ts";
|
|
4
4
|
import type { StorageOriginEntry } from "../storage.ts";
|
|
5
5
|
import type { AuthSession } from "../auth-session.ts";
|
|
@@ -36,6 +36,8 @@ export declare function unwrapSelect(r: ProtoSelectOptionResult): SelectOptionRe
|
|
|
36
36
|
export declare function unwrapWait(r: ProtoWaitResult): WaitResult;
|
|
37
37
|
export declare function frameInfoFromProto(f: ProtoFrameInfo): FrameInfo;
|
|
38
38
|
export declare function pageInfoFromProto(p: ProtoPageInfo): PageInfo;
|
|
39
|
+
export declare function sessionUsageFromProto(u: ProtoSessionUsage): SessionUsage;
|
|
40
|
+
export declare function sessionUsageFromJson(v: unknown): SessionUsage | undefined;
|
|
39
41
|
export declare function headerFromProto(h: ProtoHeader): Header;
|
|
40
42
|
export declare function headersToProto(headers: Header[] | undefined): ProtoHeader[];
|
|
41
43
|
export declare function interceptedRequestFromProto(r: ProtoInterceptedRequest | undefined): InterceptedRequest | null;
|
package/dist/internal/convert.js
CHANGED
|
@@ -228,6 +228,33 @@ export function pageInfoFromProto(p) {
|
|
|
228
228
|
: {},
|
|
229
229
|
};
|
|
230
230
|
}
|
|
231
|
+
export function sessionUsageFromProto(u) {
|
|
232
|
+
return {
|
|
233
|
+
wallTime: u.wallTime,
|
|
234
|
+
cpuTime: u.cpuTime,
|
|
235
|
+
minMemory: u.minMemory,
|
|
236
|
+
averageMemory: u.averageMemory,
|
|
237
|
+
peakMemory: u.peakMemory,
|
|
238
|
+
renderersUsed: u.renderersUsed,
|
|
239
|
+
framesCreated: u.framesCreated,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// The stop endpoint answers in JSON, with the same field names as SessionUsage.
|
|
243
|
+
export function sessionUsageFromJson(v) {
|
|
244
|
+
if (!v || typeof v !== "object")
|
|
245
|
+
return undefined;
|
|
246
|
+
const u = v;
|
|
247
|
+
const num = (x) => (typeof x === "number" ? x : 0);
|
|
248
|
+
return {
|
|
249
|
+
wallTime: num(u.wallTime),
|
|
250
|
+
cpuTime: num(u.cpuTime),
|
|
251
|
+
minMemory: num(u.minMemory),
|
|
252
|
+
averageMemory: num(u.averageMemory),
|
|
253
|
+
peakMemory: num(u.peakMemory),
|
|
254
|
+
renderersUsed: num(u.renderersUsed),
|
|
255
|
+
framesCreated: num(u.framesCreated),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
231
258
|
// ──────────────────────────────────────────────────────────────────────
|
|
232
259
|
// Headers
|
|
233
260
|
// ──────────────────────────────────────────────────────────────────────
|
|
@@ -279,7 +306,8 @@ export function networkExchangeFromProto(e) {
|
|
|
279
306
|
initiatorUrl: e.initiatorUrl,
|
|
280
307
|
requestHeaders: e.requestHeaders.map(headerFromProto),
|
|
281
308
|
requestHeadersAreWire: e.requestHeadersAreWire,
|
|
282
|
-
|
|
309
|
+
requestBodyId: e.requestBodyId,
|
|
310
|
+
requestBodySize: Number(e.requestBodySize),
|
|
283
311
|
requestBodyTruncated: e.requestBodyTruncated,
|
|
284
312
|
hasResponse: e.hasResponse,
|
|
285
313
|
statusCode: e.statusCode,
|
|
@@ -290,9 +318,9 @@ export function networkExchangeFromProto(e) {
|
|
|
290
318
|
servedFrom: e.servedFrom,
|
|
291
319
|
responseHeaders: e.responseHeaders.map(headerFromProto),
|
|
292
320
|
responseHeadersAreWire: e.responseHeadersAreWire,
|
|
293
|
-
|
|
321
|
+
responseBodyId: e.responseBodyId,
|
|
322
|
+
responseBodySize: Number(e.responseBodySize),
|
|
294
323
|
responseBodyTruncated: e.responseBodyTruncated,
|
|
295
|
-
responseBodyCaptured: e.responseBodyCaptured,
|
|
296
324
|
encodedDataLength: Number(e.encodedDataLength),
|
|
297
325
|
error: e.error,
|
|
298
326
|
};
|
|
@@ -52,8 +52,8 @@ export declare class NetworkCapture {
|
|
|
52
52
|
get error(): Error | null;
|
|
53
53
|
/**
|
|
54
54
|
* How many exchanges the server discarded because this reader fell behind.
|
|
55
|
-
* Anything above zero means the log has holes: make the handler cheaper
|
|
56
|
-
* narrow `patterns
|
|
55
|
+
* Anything above zero means the log has holes: make the handler cheaper or
|
|
56
|
+
* narrow `patterns`.
|
|
57
57
|
*/
|
|
58
58
|
get dropped(): number;
|
|
59
59
|
/**
|
package/dist/network-capture.js
CHANGED
|
@@ -57,8 +57,8 @@ export class NetworkCapture {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* How many exchanges the server discarded because this reader fell behind.
|
|
60
|
-
* Anything above zero means the log has holes: make the handler cheaper
|
|
61
|
-
* narrow `patterns
|
|
60
|
+
* Anything above zero means the log has holes: make the handler cheaper or
|
|
61
|
+
* narrow `patterns`.
|
|
62
62
|
*/
|
|
63
63
|
get dropped() {
|
|
64
64
|
return this.droppedCount;
|
package/dist/types.d.ts
CHANGED
|
@@ -26,6 +26,46 @@ export interface PageInfo {
|
|
|
26
26
|
viewport: Rect;
|
|
27
27
|
frameTree: FrameInfo;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* SessionUsage is what a session's browser has consumed since the session
|
|
31
|
+
* started: the CPU time and memory of every process that rendered its pages —
|
|
32
|
+
* main frames, cross-site iframes and the workers they host — including
|
|
33
|
+
* processes that have since exited. Work done on the session's behalf in
|
|
34
|
+
* processes it shares with other sessions is not included.
|
|
35
|
+
*
|
|
36
|
+
* {@link CloudBrowser.getUsage} reports it while the session runs, and
|
|
37
|
+
* {@link CloudBrowser.stopBrowser} resolves with the final figures, so there is
|
|
38
|
+
* no need to read it right before stopping.
|
|
39
|
+
*/
|
|
40
|
+
export interface SessionUsage {
|
|
41
|
+
/** Real time since the session's browser was created, in seconds. */
|
|
42
|
+
wallTime: number;
|
|
43
|
+
/**
|
|
44
|
+
* User plus kernel CPU time in seconds. Only time a thread actually ran on a
|
|
45
|
+
* core counts; waiting and idling do not.
|
|
46
|
+
*/
|
|
47
|
+
cpuTime: number;
|
|
48
|
+
/**
|
|
49
|
+
* The least memory the session held once its browser was ready, in bytes.
|
|
50
|
+
* Sampled about once a second.
|
|
51
|
+
*/
|
|
52
|
+
minMemory: number;
|
|
53
|
+
/**
|
|
54
|
+
* The memory held, averaged over wallTime, in bytes; averageMemory * wallTime
|
|
55
|
+
* is the memory-time used. Sampled about once a second, so short spikes count
|
|
56
|
+
* toward the peak but barely toward the average.
|
|
57
|
+
*/
|
|
58
|
+
averageMemory: number;
|
|
59
|
+
/** The most memory held at any one moment, in bytes. */
|
|
60
|
+
peakMemory: number;
|
|
61
|
+
/**
|
|
62
|
+
* Renderer processes that hosted at least one of the session's frames: one
|
|
63
|
+
* per site its pages and cross-site iframes needed.
|
|
64
|
+
*/
|
|
65
|
+
renderersUsed: number;
|
|
66
|
+
/** Child frames created in the session's pages, whether or not they got a process of their own. */
|
|
67
|
+
framesCreated: number;
|
|
68
|
+
}
|
|
29
69
|
/** Header is a single HTTP header (name/value pair) on an intercepted request or response. */
|
|
30
70
|
export interface Header {
|
|
31
71
|
name: string;
|
|
@@ -102,10 +142,16 @@ export interface NetworkExchange {
|
|
|
102
142
|
*/
|
|
103
143
|
requestHeadersAreWire: boolean;
|
|
104
144
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
145
|
+
* Names the request body; read it with {@link CloudBrowser.readNetworkBody}.
|
|
146
|
+
* Empty when the request had no body. Bodies never travel with the exchange.
|
|
147
|
+
*/
|
|
148
|
+
requestBodyId: string;
|
|
149
|
+
/** Bytes kept for the request body. */
|
|
150
|
+
requestBodySize: number;
|
|
151
|
+
/**
|
|
152
|
+
* Part of the request body is missing: it hit the per-body cap, or it was a
|
|
153
|
+
* file or streamed upload, which are not kept.
|
|
107
154
|
*/
|
|
108
|
-
requestBody: Uint8Array;
|
|
109
155
|
requestBodyTruncated: boolean;
|
|
110
156
|
/** False when the request failed before any response arrived; see error. */
|
|
111
157
|
hasResponse: boolean;
|
|
@@ -119,26 +165,49 @@ export interface NetworkExchange {
|
|
|
119
165
|
responseHeaders: Header[];
|
|
120
166
|
responseHeadersAreWire: boolean;
|
|
121
167
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* Binary content does not survive the browser boundary intact — see
|
|
168
|
+
* Names the response body; read it with {@link CloudBrowser.readNetworkBody}.
|
|
169
|
+
* Empty when body capture did not apply to this exchange — see
|
|
125
170
|
* {@link NetworkCaptureOptions.bodies}.
|
|
126
171
|
*/
|
|
127
|
-
|
|
172
|
+
responseBodyId: string;
|
|
173
|
+
/** Bytes kept for the response body, after content decoding. */
|
|
174
|
+
responseBodySize: number;
|
|
175
|
+
/**
|
|
176
|
+
* The kept body is shorter than the one the page received: it hit the
|
|
177
|
+
* per-body cap or the load ended early.
|
|
178
|
+
*/
|
|
128
179
|
responseBodyTruncated: boolean;
|
|
129
|
-
responseBodyCaptured: boolean;
|
|
130
180
|
/** Bytes on the wire, not body size; 0 for a response served from cache. */
|
|
131
181
|
encodedDataLength: number;
|
|
132
182
|
/** Net error name (e.g. "net::ERR_ABORTED"), empty on success. */
|
|
133
183
|
error: string;
|
|
134
184
|
}
|
|
135
|
-
/**
|
|
185
|
+
/** One range of a captured body, from {@link CloudBrowser.readNetworkBodyRange}. */
|
|
186
|
+
export interface NetworkBodyRange {
|
|
187
|
+
/** The bytes read; empty past the end of the body. */
|
|
188
|
+
data: Uint8Array;
|
|
189
|
+
/** Bytes kept for the body as a whole. */
|
|
190
|
+
totalSize: number;
|
|
191
|
+
/** Matches the exchange's truncated flag for this body. */
|
|
192
|
+
truncated: boolean;
|
|
193
|
+
}
|
|
194
|
+
/** A whole captured body, from {@link CloudBrowser.readNetworkBody}. */
|
|
195
|
+
export interface NetworkBody {
|
|
196
|
+
data: Uint8Array;
|
|
197
|
+
/** The kept body is shorter than the original; see the exchange's flag. */
|
|
198
|
+
truncated: boolean;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Which response bodies a network capture keeps. Kept bodies are not part of
|
|
202
|
+
* the exchange; read them with {@link CloudBrowser.readNetworkBody}.
|
|
203
|
+
*/
|
|
136
204
|
export type NetworkBodies = "none" | "text" | "all";
|
|
137
205
|
/**
|
|
138
206
|
* Configures {@link CloudBrowser.captureNetwork}.
|
|
139
207
|
*
|
|
140
|
-
* There is deliberately no byte-cap option:
|
|
141
|
-
* machine shared with other sessions, so the server owns
|
|
208
|
+
* There is deliberately no byte-cap option: kept bodies are stored on a
|
|
209
|
+
* machine shared with other sessions, so the server owns the quota. When a
|
|
210
|
+
* session's bodies exceed it, the oldest are dropped first.
|
|
142
211
|
*/
|
|
143
212
|
export interface NetworkCaptureOptions {
|
|
144
213
|
/**
|
|
@@ -149,9 +218,8 @@ export interface NetworkCaptureOptions {
|
|
|
149
218
|
patterns?: string[];
|
|
150
219
|
/**
|
|
151
220
|
* Response-body capture. `"text"` keeps bodies whose MIME type is textual,
|
|
152
|
-
* `"all"` keeps every body
|
|
153
|
-
*
|
|
154
|
-
* the bodies are textual. Defaults to `"none"`, headers and status only.
|
|
221
|
+
* `"all"` keeps every body, binary included. Defaults to `"none"`, headers
|
|
222
|
+
* and status only. Request bodies are kept whenever a request has one.
|
|
155
223
|
*/
|
|
156
224
|
bodies?: NetworkBodies;
|
|
157
225
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browserscale-ts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Official SDK for browserscale — undetected stealth browsers in the cloud. Rent real Chromium sessions with unique fingerprints, built-in proxies, captcha solving, human-like input and live video — scale web scraping and automation without running a single browser yourself.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|