@solarisdk/browser 0.1.3 → 0.1.4
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/browser-session.d.ts +27 -4
- package/dist/browser-session.js +30 -4
- package/dist/index.d.ts +0 -5
- package/dist/index.js +59 -3
- package/package.json +1 -1
|
@@ -5,8 +5,14 @@ export declare class BrowserSession {
|
|
|
5
5
|
private readonly client;
|
|
6
6
|
readonly session: Session;
|
|
7
7
|
private readonly browser;
|
|
8
|
+
/** The session's primary context — seeded with the attached profile's
|
|
9
|
+
* storageState when there is one. See {@link context}. */
|
|
10
|
+
private readonly primaryContext?;
|
|
8
11
|
private closed;
|
|
9
|
-
constructor(client: Solari, session: Session, browser: Browser
|
|
12
|
+
constructor(client: Solari, session: Session, browser: Browser,
|
|
13
|
+
/** The session's primary context — seeded with the attached profile's
|
|
14
|
+
* storageState when there is one. See {@link context}. */
|
|
15
|
+
primaryContext?: BrowserContext | undefined);
|
|
10
16
|
/** Session id. */
|
|
11
17
|
get id(): string;
|
|
12
18
|
/** Plan-tier deadline (ISO 8601 UTC); session auto-releases at this point. */
|
|
@@ -23,11 +29,28 @@ export declare class BrowserSession {
|
|
|
23
29
|
isConnected(): boolean;
|
|
24
30
|
/** Browser version string (e.g. `"Chromium/130.0.6723.31"`). */
|
|
25
31
|
version(): string;
|
|
26
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* The session's primary context — the one carrying the attached profile's
|
|
34
|
+
* cookies and localStorage. Use this (or {@link newPage}) rather than
|
|
35
|
+
* `contexts()[0]`, which is not guaranteed to be the seeded one.
|
|
36
|
+
*/
|
|
37
|
+
get context(): BrowserContext | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* All open contexts, straight from the browser.
|
|
40
|
+
*
|
|
41
|
+
* NOTE: this is NOT guaranteed to be non-empty, and `contexts()[0]` is NOT
|
|
42
|
+
* guaranteed to be the profile-seeded context — the pool only pre-creates an
|
|
43
|
+
* eager context when a proxy was requested. Prefer {@link context}.
|
|
44
|
+
*/
|
|
27
45
|
contexts(): BrowserContext[];
|
|
28
|
-
/** Open
|
|
46
|
+
/** Open an ADDITIONAL, empty browser context. This does NOT carry the
|
|
47
|
+
* attached profile — use {@link context} for that. */
|
|
29
48
|
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
|
30
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Open a page in the session's primary context, so an attached profile's
|
|
51
|
+
* cookies/localStorage are actually present. Falls back to a browser-level
|
|
52
|
+
* page only when there is no primary context.
|
|
53
|
+
*/
|
|
31
54
|
newPage(): Promise<Page>;
|
|
32
55
|
/** Close the browser and release the session. Idempotent. */
|
|
33
56
|
close(): Promise<void>;
|
package/dist/browser-session.js
CHANGED
|
@@ -2,11 +2,16 @@ export class BrowserSession {
|
|
|
2
2
|
client;
|
|
3
3
|
session;
|
|
4
4
|
browser;
|
|
5
|
+
primaryContext;
|
|
5
6
|
closed = false;
|
|
6
|
-
constructor(client, session, browser
|
|
7
|
+
constructor(client, session, browser,
|
|
8
|
+
/** The session's primary context — seeded with the attached profile's
|
|
9
|
+
* storageState when there is one. See {@link context}. */
|
|
10
|
+
primaryContext) {
|
|
7
11
|
this.client = client;
|
|
8
12
|
this.session = session;
|
|
9
13
|
this.browser = browser;
|
|
14
|
+
this.primaryContext = primaryContext;
|
|
10
15
|
}
|
|
11
16
|
/** Session id. */
|
|
12
17
|
get id() {
|
|
@@ -40,16 +45,37 @@ export class BrowserSession {
|
|
|
40
45
|
version() {
|
|
41
46
|
return this.browser.version();
|
|
42
47
|
}
|
|
43
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* The session's primary context — the one carrying the attached profile's
|
|
50
|
+
* cookies and localStorage. Use this (or {@link newPage}) rather than
|
|
51
|
+
* `contexts()[0]`, which is not guaranteed to be the seeded one.
|
|
52
|
+
*/
|
|
53
|
+
get context() {
|
|
54
|
+
return this.primaryContext;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* All open contexts, straight from the browser.
|
|
58
|
+
*
|
|
59
|
+
* NOTE: this is NOT guaranteed to be non-empty, and `contexts()[0]` is NOT
|
|
60
|
+
* guaranteed to be the profile-seeded context — the pool only pre-creates an
|
|
61
|
+
* eager context when a proxy was requested. Prefer {@link context}.
|
|
62
|
+
*/
|
|
44
63
|
contexts() {
|
|
45
64
|
return this.browser.contexts();
|
|
46
65
|
}
|
|
47
|
-
/** Open
|
|
66
|
+
/** Open an ADDITIONAL, empty browser context. This does NOT carry the
|
|
67
|
+
* attached profile — use {@link context} for that. */
|
|
48
68
|
async newContext(options) {
|
|
49
69
|
return this.browser.newContext(options);
|
|
50
70
|
}
|
|
51
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Open a page in the session's primary context, so an attached profile's
|
|
73
|
+
* cookies/localStorage are actually present. Falls back to a browser-level
|
|
74
|
+
* page only when there is no primary context.
|
|
75
|
+
*/
|
|
52
76
|
async newPage() {
|
|
77
|
+
if (this.primaryContext)
|
|
78
|
+
return this.primaryContext.newPage();
|
|
53
79
|
return this.browser.newPage();
|
|
54
80
|
}
|
|
55
81
|
/** Close the browser and release the session. Idempotent. */
|
package/dist/index.d.ts
CHANGED
|
@@ -103,11 +103,6 @@ export interface CreateSessionOptions {
|
|
|
103
103
|
stealth?: boolean;
|
|
104
104
|
/** Enable managed captcha solving. Requires `stealth: true`. */
|
|
105
105
|
captcha?: boolean;
|
|
106
|
-
/** Opt in to Cloudflare Web Bot Auth — sign every outbound HTTP
|
|
107
|
-
* request with an Ed25519 key registered to our verified bot
|
|
108
|
-
* directory. Independent of `stealth`. Silently inert if the
|
|
109
|
-
* acquired slot has no signing key configured. */
|
|
110
|
-
webBotAuth?: boolean;
|
|
111
106
|
/** Request managed proxy egress. Requires `stealth: true`.
|
|
112
107
|
*
|
|
113
108
|
* proxy: "us"
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,39 @@ import { chromium } from "patchright-core";
|
|
|
2
2
|
import { LocalProxy } from "./local-proxy.js";
|
|
3
3
|
import { BrowserSession } from "./browser-session.js";
|
|
4
4
|
export { BrowserSession } from "./browser-session.js";
|
|
5
|
+
/**
|
|
6
|
+
* Narrow a {@link StorageState} to the shape `newContext({storageState})` wants.
|
|
7
|
+
*
|
|
8
|
+
* Our StorageState is deliberately permissive — every field optional, plus an
|
|
9
|
+
* index signature — because it doubles as the INPUT to `profiles.save()`, where
|
|
10
|
+
* callers hand-build partial objects. Playwright's parameter type requires the
|
|
11
|
+
* cookie fields to be present, so passing ours straight through is a TS2322 and
|
|
12
|
+
* callers were resorting to `as any`.
|
|
13
|
+
*
|
|
14
|
+
* Rather than tighten the public type (which would break anyone constructing a
|
|
15
|
+
* partial state to save), fill the defaults at this one seam. Values that are
|
|
16
|
+
* actually present always win; the defaults only cover fields the gateway
|
|
17
|
+
* omitted, and they match Playwright's own defaults for a host-only cookie.
|
|
18
|
+
*/
|
|
19
|
+
function toPlaywrightStorageState(state) {
|
|
20
|
+
return {
|
|
21
|
+
cookies: (state.cookies ?? []).map((c) => ({
|
|
22
|
+
name: c.name,
|
|
23
|
+
value: c.value,
|
|
24
|
+
domain: c.domain ?? "",
|
|
25
|
+
path: c.path ?? "/",
|
|
26
|
+
// -1 is Playwright's "session cookie" sentinel.
|
|
27
|
+
expires: c.expires ?? -1,
|
|
28
|
+
httpOnly: c.httpOnly ?? false,
|
|
29
|
+
secure: c.secure ?? false,
|
|
30
|
+
sameSite: c.sameSite ?? "Lax",
|
|
31
|
+
})),
|
|
32
|
+
origins: (state.origins ?? []).map((o) => ({
|
|
33
|
+
origin: o.origin,
|
|
34
|
+
localStorage: o.localStorage ?? [],
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
5
38
|
export class SolariError extends Error {
|
|
6
39
|
status;
|
|
7
40
|
cause;
|
|
@@ -168,8 +201,6 @@ class SessionsResource {
|
|
|
168
201
|
body.stealth = true;
|
|
169
202
|
if (options?.captcha === true)
|
|
170
203
|
body.captcha = true;
|
|
171
|
-
if (options?.webBotAuth === true)
|
|
172
|
-
body.webBotAuth = true;
|
|
173
204
|
if (options?.proxy !== undefined)
|
|
174
205
|
body.proxy = options.proxy;
|
|
175
206
|
const res = await this.client.request("POST", "/sessions", Object.keys(body).length ? body : undefined);
|
|
@@ -322,7 +353,32 @@ export class Solari {
|
|
|
322
353
|
if (probeEnabled) {
|
|
323
354
|
await this._probeBrowserHealth(browser, probeTimeoutMs);
|
|
324
355
|
}
|
|
325
|
-
|
|
356
|
+
// Materialise ONE primary context for the session.
|
|
357
|
+
//
|
|
358
|
+
// The gateway already fetches an attached profile's cookies +
|
|
359
|
+
// origins into session.storageState (and its own comment says it
|
|
360
|
+
// does so "so the SDK skips the newContext({storageState}) seed")
|
|
361
|
+
// — but the SDK never performed that seed, so every run wrote a
|
|
362
|
+
// new profile version and read none back. Seed it here.
|
|
363
|
+
//
|
|
364
|
+
// Also makes a context reliably available: the pool only
|
|
365
|
+
// pre-creates one when a proxy was requested, so on a plain
|
|
366
|
+
// launch() browser.contexts() was empty and contexts()[0]
|
|
367
|
+
// undefined — which the SDK's own docs pointed callers at.
|
|
368
|
+
const ctxOptions = {};
|
|
369
|
+
const tz = session.proxy?.timezoneId;
|
|
370
|
+
if (tz)
|
|
371
|
+
ctxOptions.timezoneId = tz;
|
|
372
|
+
if (session.storageState) {
|
|
373
|
+
ctxOptions.storageState = toPlaywrightStorageState(session.storageState);
|
|
374
|
+
}
|
|
375
|
+
const primary =
|
|
376
|
+
// Reuse the pool's eager context only when we have nothing
|
|
377
|
+
// to seed — storageState can only be applied at creation.
|
|
378
|
+
!ctxOptions.storageState && browser.contexts().length > 0
|
|
379
|
+
? browser.contexts()[0]
|
|
380
|
+
: await browser.newContext(ctxOptions);
|
|
381
|
+
return new BrowserSession(this, session, browser, primary);
|
|
326
382
|
}
|
|
327
383
|
catch (err) {
|
|
328
384
|
if (browser) {
|