@orkestrel/browser 0.0.24 → 0.0.25

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/bin/main.js CHANGED
@@ -7,10 +7,11 @@ process.stderr.on("error", () => {
7
7
  process.exitCode = 1;
8
8
  });
9
9
  try {
10
- const { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY, BROWSE_POOL, BROWSE_VIEWPORT } = process.env;
10
+ const { BROWSE_ROOT, BROWSE_HEADLESS, BROWSE_EXECUTABLE, BROWSE_READONLY, BROWSE_POOL, BROWSE_CONTEXTS, BROWSE_VIEWPORT } = process.env;
11
11
  const headless = parseBoolean(BROWSE_HEADLESS);
12
12
  const readonly = parseBoolean(BROWSE_READONLY);
13
13
  const size = parseInteger(BROWSE_POOL);
14
+ const contexts = parseInteger(BROWSE_CONTEXTS);
14
15
  const viewport = parseBrowserViewport(BROWSE_VIEWPORT);
15
16
  for (const [name, value, parsed] of [[
16
17
  "BROWSE_HEADLESS",
@@ -32,12 +33,19 @@ try {
32
33
  name: "BROWSE_VIEWPORT",
33
34
  value: BROWSE_VIEWPORT
34
35
  });
36
+ if (BROWSE_CONTEXTS !== void 0 && BROWSE_CONTEXTS !== "" && contexts === void 0) throw new BrowserError(`BROWSE_CONTEXTS must be an integer, not ${JSON.stringify(BROWSE_CONTEXTS)}`, "BROWSER_SERVER_ENVIRONMENT", {
37
+ name: "BROWSE_CONTEXTS",
38
+ value: BROWSE_CONTEXTS
39
+ });
35
40
  await createBrowserMCPServer({
36
41
  ...BROWSE_ROOT === void 0 || BROWSE_ROOT === "" ? {} : { root: BROWSE_ROOT },
37
42
  ...headless === void 0 ? {} : { headless },
38
43
  ...BROWSE_EXECUTABLE === void 0 || BROWSE_EXECUTABLE === "" ? {} : { executable: BROWSE_EXECUTABLE },
39
44
  ...readonly === void 0 ? {} : { readonly },
40
- ...size === void 0 ? {} : { pool: { size } },
45
+ pool: {
46
+ ...size === void 0 ? {} : { size },
47
+ ...contexts === void 0 ? {} : { contexts }
48
+ },
41
49
  ...viewport === void 0 ? {} : { viewport }
42
50
  }).start();
43
51
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","names":[],"sources":["../../src/bin/main.ts"],"sourcesContent":["import { parseBoolean, parseInteger } from '@orkestrel/contract'\nimport { BrowserError, isBrowserError } from '@src/core'\nimport { createBrowserMCPServer, parseBrowserViewport } from '@src/server'\n\n// The entry owns stderr; a closed reader must not interrupt the server's teardown barrier.\nprocess.stderr.on('error', () => {\n\tprocess.exitCode = 1\n})\n\ntry {\n\tconst {\n\t\tBROWSE_ROOT,\n\t\tBROWSE_HEADLESS,\n\t\tBROWSE_EXECUTABLE,\n\t\tBROWSE_READONLY,\n\t\tBROWSE_POOL,\n\t\tBROWSE_VIEWPORT,\n\t} = process.env\n\tconst headless = parseBoolean(BROWSE_HEADLESS)\n\tconst readonly = parseBoolean(BROWSE_READONLY)\n\tconst size = parseInteger(BROWSE_POOL)\n\tconst viewport = parseBrowserViewport(BROWSE_VIEWPORT)\n\t// An empty variable counts as unset, as a shell's `NAME=` leaves it.\n\tfor (const [name, value, parsed] of [\n\t\t['BROWSE_HEADLESS', BROWSE_HEADLESS, headless],\n\t\t['BROWSE_READONLY', BROWSE_READONLY, readonly],\n\t] as const) {\n\t\tif (value !== undefined && value !== '' && parsed === undefined) {\n\t\t\tthrow new BrowserError(\n\t\t\t\t`${name} must be true, false, 1, or 0, not ${JSON.stringify(value)}`,\n\t\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t\t{ name, value },\n\t\t\t)\n\t\t}\n\t}\n\tif (BROWSE_POOL !== undefined && BROWSE_POOL !== '' && size === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_POOL must be an integer, not ${JSON.stringify(BROWSE_POOL)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_POOL', value: BROWSE_POOL },\n\t\t)\n\tif (BROWSE_VIEWPORT !== undefined && BROWSE_VIEWPORT !== '' && viewport === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_VIEWPORT must be positive integers in WIDTHxHEIGHT form, not ${JSON.stringify(BROWSE_VIEWPORT)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_VIEWPORT', value: BROWSE_VIEWPORT },\n\t\t)\n\tawait createBrowserMCPServer({\n\t\t...(BROWSE_ROOT === undefined || BROWSE_ROOT === '' ? {} : { root: BROWSE_ROOT }),\n\t\t...(headless === undefined ? {} : { headless }),\n\t\t...(BROWSE_EXECUTABLE === undefined || BROWSE_EXECUTABLE === ''\n\t\t\t? {}\n\t\t\t: { executable: BROWSE_EXECUTABLE }),\n\t\t...(readonly === undefined ? {} : { readonly }),\n\t\t...(size === undefined ? {} : { pool: { size } }),\n\t\t...(viewport === undefined ? {} : { viewport }),\n\t}).start()\n} catch (error) {\n\tif (!isBrowserError(error)) throw error\n\tconsole.error(`browse: ${error.code}: ${error.message.split(/\\r\\n|\\n/u).join(' ')}`)\n\tprocess.exitCode = 1\n}\n"],"mappings":";;;;;AAKA,QAAQ,OAAO,GAAG,eAAe;CAChC,QAAQ,WAAW;AACpB,CAAC;AAED,IAAI;CACH,MAAM,EACL,aACA,iBACA,mBACA,iBACA,aACA,oBACG,QAAQ;CACZ,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,OAAO,aAAa,WAAW;CACrC,MAAM,WAAW,qBAAqB,eAAe;CAErD,KAAK,MAAM,CAAC,MAAM,OAAO,WAAW,CACnC;EAAC;EAAmB;EAAiB;CAAQ,GAC7C;EAAC;EAAmB;EAAiB;CAAQ,CAC9C,GACC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,WAAW,KAAA,GACrD,MAAM,IAAI,aACT,GAAG,KAAK,qCAAqC,KAAK,UAAU,KAAK,KACjE,8BACA;EAAE;EAAM;CAAM,CACf;CAGF,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,MAAM,SAAS,KAAA,GAC/D,MAAM,IAAI,aACT,uCAAuC,KAAK,UAAU,WAAW,KACjE,8BACA;EAAE,MAAM;EAAe,OAAO;CAAY,CAC3C;CACD,IAAI,oBAAoB,KAAA,KAAa,oBAAoB,MAAM,aAAa,KAAA,GAC3E,MAAM,IAAI,aACT,uEAAuE,KAAK,UAAU,eAAe,KACrG,8BACA;EAAE,MAAM;EAAmB,OAAO;CAAgB,CACnD;CACD,MAAM,uBAAuB;EAC5B,GAAI,gBAAgB,KAAA,KAAa,gBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,YAAY;EAC/E,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,sBAAsB,KAAA,KAAa,sBAAsB,KAC1D,CAAC,IACD,EAAE,YAAY,kBAAkB;EACnC,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;EAC/C,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;CAC9C,CAAC,CAAC,CAAC,MAAM;AACV,SAAS,OAAO;CACf,IAAI,CAAC,eAAe,KAAK,GAAG,MAAM;CAClC,QAAQ,MAAM,WAAW,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,UAAU,CAAC,CAAC,KAAK,GAAG,GAAG;CACnF,QAAQ,WAAW;AACpB"}
1
+ {"version":3,"file":"main.js","names":[],"sources":["../../src/bin/main.ts"],"sourcesContent":["import { parseBoolean, parseInteger } from '@orkestrel/contract'\nimport { BrowserError, isBrowserError } from '@src/core'\nimport { createBrowserMCPServer, parseBrowserViewport } from '@src/server'\n\n// The entry owns stderr; a closed reader must not interrupt the server's teardown barrier.\nprocess.stderr.on('error', () => {\n\tprocess.exitCode = 1\n})\n\ntry {\n\tconst {\n\t\tBROWSE_ROOT,\n\t\tBROWSE_HEADLESS,\n\t\tBROWSE_EXECUTABLE,\n\t\tBROWSE_READONLY,\n\t\tBROWSE_POOL,\n\t\tBROWSE_CONTEXTS,\n\t\tBROWSE_VIEWPORT,\n\t} = process.env\n\tconst headless = parseBoolean(BROWSE_HEADLESS)\n\tconst readonly = parseBoolean(BROWSE_READONLY)\n\tconst size = parseInteger(BROWSE_POOL)\n\tconst contexts = parseInteger(BROWSE_CONTEXTS)\n\tconst viewport = parseBrowserViewport(BROWSE_VIEWPORT)\n\t// An empty variable counts as unset, as a shell's `NAME=` leaves it.\n\tfor (const [name, value, parsed] of [\n\t\t['BROWSE_HEADLESS', BROWSE_HEADLESS, headless],\n\t\t['BROWSE_READONLY', BROWSE_READONLY, readonly],\n\t] as const) {\n\t\tif (value !== undefined && value !== '' && parsed === undefined) {\n\t\t\tthrow new BrowserError(\n\t\t\t\t`${name} must be true, false, 1, or 0, not ${JSON.stringify(value)}`,\n\t\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t\t{ name, value },\n\t\t\t)\n\t\t}\n\t}\n\tif (BROWSE_POOL !== undefined && BROWSE_POOL !== '' && size === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_POOL must be an integer, not ${JSON.stringify(BROWSE_POOL)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_POOL', value: BROWSE_POOL },\n\t\t)\n\tif (BROWSE_VIEWPORT !== undefined && BROWSE_VIEWPORT !== '' && viewport === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_VIEWPORT must be positive integers in WIDTHxHEIGHT form, not ${JSON.stringify(BROWSE_VIEWPORT)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_VIEWPORT', value: BROWSE_VIEWPORT },\n\t\t)\n\tif (BROWSE_CONTEXTS !== undefined && BROWSE_CONTEXTS !== '' && contexts === undefined)\n\t\tthrow new BrowserError(\n\t\t\t`BROWSE_CONTEXTS must be an integer, not ${JSON.stringify(BROWSE_CONTEXTS)}`,\n\t\t\t'BROWSER_SERVER_ENVIRONMENT',\n\t\t\t{ name: 'BROWSE_CONTEXTS', value: BROWSE_CONTEXTS },\n\t\t)\n\tawait createBrowserMCPServer({\n\t\t...(BROWSE_ROOT === undefined || BROWSE_ROOT === '' ? {} : { root: BROWSE_ROOT }),\n\t\t...(headless === undefined ? {} : { headless }),\n\t\t...(BROWSE_EXECUTABLE === undefined || BROWSE_EXECUTABLE === ''\n\t\t\t? {}\n\t\t\t: { executable: BROWSE_EXECUTABLE }),\n\t\t...(readonly === undefined ? {} : { readonly }),\n\t\tpool: {\n\t\t\t...(size === undefined ? {} : { size }),\n\t\t\t...(contexts === undefined ? {} : { contexts }),\n\t\t},\n\t\t...(viewport === undefined ? {} : { viewport }),\n\t}).start()\n} catch (error) {\n\tif (!isBrowserError(error)) throw error\n\tconsole.error(`browse: ${error.code}: ${error.message.split(/\\r\\n|\\n/u).join(' ')}`)\n\tprocess.exitCode = 1\n}\n"],"mappings":";;;;;AAKA,QAAQ,OAAO,GAAG,eAAe;CAChC,QAAQ,WAAW;AACpB,CAAC;AAED,IAAI;CACH,MAAM,EACL,aACA,iBACA,mBACA,iBACA,aACA,iBACA,oBACG,QAAQ;CACZ,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,OAAO,aAAa,WAAW;CACrC,MAAM,WAAW,aAAa,eAAe;CAC7C,MAAM,WAAW,qBAAqB,eAAe;CAErD,KAAK,MAAM,CAAC,MAAM,OAAO,WAAW,CACnC;EAAC;EAAmB;EAAiB;CAAQ,GAC7C;EAAC;EAAmB;EAAiB;CAAQ,CAC9C,GACC,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,WAAW,KAAA,GACrD,MAAM,IAAI,aACT,GAAG,KAAK,qCAAqC,KAAK,UAAU,KAAK,KACjE,8BACA;EAAE;EAAM;CAAM,CACf;CAGF,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,MAAM,SAAS,KAAA,GAC/D,MAAM,IAAI,aACT,uCAAuC,KAAK,UAAU,WAAW,KACjE,8BACA;EAAE,MAAM;EAAe,OAAO;CAAY,CAC3C;CACD,IAAI,oBAAoB,KAAA,KAAa,oBAAoB,MAAM,aAAa,KAAA,GAC3E,MAAM,IAAI,aACT,uEAAuE,KAAK,UAAU,eAAe,KACrG,8BACA;EAAE,MAAM;EAAmB,OAAO;CAAgB,CACnD;CACD,IAAI,oBAAoB,KAAA,KAAa,oBAAoB,MAAM,aAAa,KAAA,GAC3E,MAAM,IAAI,aACT,2CAA2C,KAAK,UAAU,eAAe,KACzE,8BACA;EAAE,MAAM;EAAmB,OAAO;CAAgB,CACnD;CACD,MAAM,uBAAuB;EAC5B,GAAI,gBAAgB,KAAA,KAAa,gBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,YAAY;EAC/E,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,sBAAsB,KAAA,KAAa,sBAAsB,KAC1D,CAAC,IACD,EAAE,YAAY,kBAAkB;EACnC,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,MAAM;GACL,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;GACrC,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC9C;EACA,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;CAC9C,CAAC,CAAC,CAAC,MAAM;AACV,SAAS,OAAO;CACf,IAAI,CAAC,eAAe,KAAK,GAAG,MAAM;CAClC,QAAQ,MAAM,WAAW,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,UAAU,CAAC,CAAC,KAAK,GAAG,GAAG;CACnF,QAAQ,WAAW;AACpB"}
@@ -163,7 +163,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
163
163
  */
164
164
  var BROWSER_HAR_CREATOR = Object.freeze({
165
165
  name: "@orkestrel/browser",
166
- version: "0.0.24"
166
+ version: "0.0.25"
167
167
  });
168
168
  /** Names the attribute that tags temporary screenshot styles and masks. */
169
169
  var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
@@ -1013,9 +1013,7 @@ var CDPError = class extends BrowserError {
1013
1013
  }
1014
1014
  };
1015
1015
  /**
1016
- * Reports that a CDP request could not be sent or completed because the client was not in a
1017
- * connectable state — not connected, closed while connecting, or the connection dropped
1018
- * mid-request — under the code `BROWSER_CDP_CONNECTION_ERROR`.
1016
+ * Reports that a CDP request could not be sent or completed because the client was not connected, closed while connecting, lost its connection, or lost the request's target session, under the code `BROWSER_CDP_CONNECTION_ERROR`.
1019
1017
  */
1020
1018
  var CDPConnectionError = class extends BrowserError {
1021
1019
  constructor(message, context) {
@@ -5225,6 +5223,7 @@ var CDPClient = class {
5225
5223
  const listener = signal === void 0 ? void 0 : this.#abort(id, signal);
5226
5224
  this.#pending.set(id, {
5227
5225
  method,
5226
+ session: options?.session,
5228
5227
  resolve,
5229
5228
  reject,
5230
5229
  timer,
@@ -5369,6 +5368,16 @@ var CDPClient = class {
5369
5368
  const method = parsed["method"];
5370
5369
  const rawParams = parsed["params"];
5371
5370
  const params = (0, _orkestrel_contract.isRecord)(rawParams) ? rawParams : Object.freeze({});
5371
+ if (method === "Target.detachedFromTarget" && (0, _orkestrel_contract.isString)(params["sessionId"])) {
5372
+ const detached = params["sessionId"];
5373
+ for (const [id, entry] of this.#pending) {
5374
+ if (entry.session !== detached) continue;
5375
+ this.#settle(id)?.reject(new CDPConnectionError("CDP session detached", {
5376
+ method: entry.method,
5377
+ session: detached
5378
+ }));
5379
+ }
5380
+ }
5372
5381
  const global = this.#subscriptions.get(void 0)?.get(method);
5373
5382
  if (global !== void 0) this.#dispatch(global, method, params);
5374
5383
  const session = parsed["sessionId"];
@@ -7208,10 +7217,12 @@ var BrowserRegistry = class {
7208
7217
  for (const frame of new Set([...this.tools(), ...this.#pending].map((entry) => entry.frame))) this.#invalidate(frame);
7209
7218
  }
7210
7219
  #navigated(params) {
7220
+ if (this.#page.closed || this.#destroying !== void 0) return;
7211
7221
  const frame = params["frame"];
7212
7222
  if ((0, _orkestrel_contract.isRecord)(frame) && (0, _orkestrel_contract.isString)(frame["id"])) this.#invalidate(frame["id"]);
7213
7223
  }
7214
7224
  #detached(params) {
7225
+ if (this.#page.closed || this.#destroying !== void 0) return;
7215
7226
  if ((0, _orkestrel_contract.isString)(params["frameId"])) this.#invalidate(params["frameId"]);
7216
7227
  }
7217
7228
  #invalidate(frame) {
@@ -7225,6 +7236,7 @@ var BrowserRegistry = class {
7225
7236
  if (removed) this.#emitter.emit("change");
7226
7237
  }
7227
7238
  #add(session, params) {
7239
+ if (this.#page.closed || this.#destroying !== void 0) return;
7228
7240
  if (!(0, _orkestrel_contract.isArray)(params["tools"])) return;
7229
7241
  for (const value of params["tools"]) {
7230
7242
  const tool = parseBrowserTool(value);
@@ -7235,6 +7247,7 @@ var BrowserRegistry = class {
7235
7247
  this.#emitter.emit("change");
7236
7248
  }
7237
7249
  #remove(params) {
7250
+ if (this.#page.closed || this.#destroying !== void 0) return;
7238
7251
  if (!(0, _orkestrel_contract.isArray)(params["tools"])) return;
7239
7252
  for (const value of params["tools"]) {
7240
7253
  const key = parseBrowserRemoval(value);
@@ -10170,10 +10183,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
10170
10183
  } catch {}
10171
10184
  }
10172
10185
  async #close() {
10173
- await this.#release();
10174
10186
  try {
10175
10187
  await this.#client.send("Target.closeTarget", { targetId: this.#targetId });
10176
10188
  } catch {}
10189
+ await this.#release();
10177
10190
  }
10178
10191
  #release() {
10179
10192
  const active = this.#releasing;
@@ -10474,12 +10487,14 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
10474
10487
  }
10475
10488
  async #attachWorker(session, id, url, category) {
10476
10489
  try {
10477
- await this.#client.send("Runtime.enable", void 0, { session });
10490
+ const enabling = this.#client.send("Runtime.enable", void 0, { session });
10491
+ if (category === "service_worker") this.#resumeTarget(session);
10492
+ await enabling;
10478
10493
  if (this.#closed) {
10479
10494
  await this.#detachChild(session, this.#sessionId);
10480
10495
  return;
10481
10496
  }
10482
- this.#resumeTarget(session);
10497
+ if (category !== "service_worker") this.#resumeTarget(session);
10483
10498
  const worker = new BrowserWorker(this.#client, session, id, url, category);
10484
10499
  this.#workers.set(id, worker);
10485
10500
  this.#emitter.emit("worker", worker);
@@ -10816,6 +10831,7 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
10816
10831
  this.#emitter.emit("navigate", params["url"], true);
10817
10832
  }
10818
10833
  #handleFrameDetached(session, params) {
10834
+ if (this.#closed) return;
10819
10835
  const frame = params["frameId"];
10820
10836
  if (!(0, _orkestrel_contract.isString)(frame) || !this.#accepts(session, frame)) return;
10821
10837
  const swapped = params["reason"] === "swap";
@@ -10916,7 +10932,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
10916
10932
  const session = params["sessionId"];
10917
10933
  if (!(0, _orkestrel_contract.isRecord)(target) || !(0, _orkestrel_contract.isString)(target["targetId"]) || !(0, _orkestrel_contract.isString)(session)) return;
10918
10934
  const category = target["type"];
10919
- if (owner !== this.#sessionId && category !== "iframe") return;
10935
+ if (owner !== this.#sessionId && category !== "iframe") {
10936
+ this.#detachChild(session, owner);
10937
+ return;
10938
+ }
10920
10939
  if (category === "worker" || category === "service_worker" || category === "shared_worker") {
10921
10940
  this.#attachWorker(session, target["targetId"], (0, _orkestrel_contract.isString)(target["url"]) ? target["url"] : "", category);
10922
10941
  return;
@@ -11224,6 +11243,7 @@ var BrowserContext = class {
11224
11243
  #publishing = /* @__PURE__ */ new Map();
11225
11244
  #observed = /* @__PURE__ */ new WeakSet();
11226
11245
  #shutdown;
11246
+ #disposal;
11227
11247
  #reference = 0;
11228
11248
  constructor(client, id, viewport, writer, emulation, downloads, options) {
11229
11249
  this.#client = client;
@@ -11248,6 +11268,9 @@ var BrowserContext = class {
11248
11268
  get id() {
11249
11269
  return this.#id;
11250
11270
  }
11271
+ get disposal() {
11272
+ return this.#disposal;
11273
+ }
11251
11274
  get cookies() {
11252
11275
  return this.#cookies;
11253
11276
  }
@@ -11290,14 +11313,14 @@ var BrowserContext = class {
11290
11313
  destroy() {
11291
11314
  const active = this.#shutdown;
11292
11315
  if (active !== void 0) return active;
11293
- const shutdown = this.#destroyResources();
11316
+ const shutdown = Promise.resolve().then(() => this.#destroyResources());
11294
11317
  this.#shutdown = shutdown;
11295
11318
  return shutdown;
11296
11319
  }
11297
11320
  close() {
11298
11321
  const active = this.#shutdown;
11299
11322
  if (active !== void 0) return active;
11300
- const shutdown = this.#closeResources();
11323
+ const shutdown = Promise.resolve().then(() => this.#closeResources());
11301
11324
  this.#shutdown = shutdown;
11302
11325
  return shutdown;
11303
11326
  }
@@ -11419,9 +11442,15 @@ var BrowserContext = class {
11419
11442
  let failure = await settleBrowserTeardown(...[...this.#pages.values()].map((page) => () => page.close()));
11420
11443
  this.#pages.clear();
11421
11444
  const id = this.#id;
11422
- if (id !== void 0) {
11423
- const settled = await settleBrowserTeardown(() => this.#client.send("Target.disposeBrowserContext", { browserContextId: id }));
11424
- failure ??= settled;
11445
+ if (id !== void 0) try {
11446
+ await this.#client.send("Target.disposeBrowserContext", { browserContextId: id });
11447
+ this.#disposal = { confirmed: true };
11448
+ } catch (error) {
11449
+ this.#disposal = {
11450
+ confirmed: false,
11451
+ error
11452
+ };
11453
+ failure ??= error;
11425
11454
  }
11426
11455
  if (failure !== void 0) throw failure;
11427
11456
  } finally {