@orkestrel/browser 0.0.23 → 0.0.24

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.
@@ -107,7 +107,7 @@ export declare const BROWSER_FRAME_WORLD_NAME = "__orkestrelBrowserFrame";
107
107
  */
108
108
  export declare const BROWSER_HAR_CREATOR: Readonly<{
109
109
  name: "@orkestrel/browser";
110
- version: "0.0.23";
110
+ version: "0.0.24";
111
111
  }>;
112
112
 
113
113
  /** Names accessibility roles that receive actionable outline references. */
@@ -159,7 +159,7 @@ export declare const BROWSER_JOURNEY_STEP_KEYS: readonly string[];
159
159
 
160
160
  /**
161
161
  * Names the journey tools a toolset constructed with `journeys` registers and reserves: `record`,
162
- * `save`, `journeys`, `edit`, `replay`, and `forget`.
162
+ * `save`, `journeys`, `edit`, `replay`, `forget`, and `capture`.
163
163
  */
164
164
  export declare const BROWSER_JOURNEY_TOOL_NAMES: readonly BrowserToolName[];
165
165
 
@@ -1801,7 +1801,7 @@ export declare type BrowserJourneyEditRequest = BrowserJourneyEdit | {
1801
1801
  *
1802
1802
  * @remarks
1803
1803
  * - `store` — keeps the journeys the tools record, list, edit, and replay
1804
- * - `runs` — keeps each replay's run; omitting it writes no run
1804
+ * - `runs` — keeps each replay's run and saves standalone images when it provides `snapshot`
1805
1805
  * - `limit` — positive integer listing cap in characters; Default: the owner's `limit`, including direct construction
1806
1806
  * - `readonly` — if `true`, refuses `record`, `save`, `edit`, and `forget` before any store access, and
1807
1807
  * `replay` still writes runs; if `false` or omitted, every tool runs
@@ -1930,13 +1930,13 @@ export declare interface BrowserJourneyTarget {
1930
1930
  }
1931
1931
 
1932
1932
  /**
1933
- * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, and `forget` over a toolset
1933
+ * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, `forget`, and `capture` over a toolset
1934
1934
  * and owns the recording and the active replay.
1935
1935
  *
1936
1936
  * @remarks
1937
1937
  * The journey toolset composes the toolset's public members: `perform` reads the view a receipt
1938
1938
  * carries through `look`, `hold` and `emitter` drive the recorder and the replay, `view` converts
1939
- * an edit's `ref` to a target, and `tools` receives the six tools at construction, which refuses
1939
+ * an edit's `ref` to a target, and `tools` receives the journey tools at construction, which refuses
1940
1940
  * with `BROWSER_TOOLSET_RESERVED` when the manager already holds one of the names. Every refusal
1941
1941
  * is a sentence that names the next call, and a reason inside it is a clause without a directive
1942
1942
  * or a final period.
@@ -1952,7 +1952,7 @@ export declare interface BrowserJourneyTarget {
1952
1952
  * by the view after the run.
1953
1953
  *
1954
1954
  * `destroy()` aborts the active replay and waits for it to finish, stops a recording without
1955
- * saving it, and removes the six tools the manager still holds under the instances it added.
1955
+ * saving it, and removes the journey tools the manager still holds under the instances it added.
1956
1956
  *
1957
1957
  * @example
1958
1958
  * ```ts
@@ -1972,7 +1972,7 @@ export declare class BrowserJourneyToolset implements BrowserJourneyToolsetInter
1972
1972
  }
1973
1973
 
1974
1974
  /**
1975
- * Registers the six journey tools over a toolset and owns the recording and the active replay.
1975
+ * Registers the journey tools over a toolset and owns the recording and the active replay.
1976
1976
  *
1977
1977
  * @remarks
1978
1978
  * - `recording` — the name of the journey being recorded, or `undefined` when none is
@@ -1981,7 +1981,7 @@ export declare class BrowserJourneyToolset implements BrowserJourneyToolsetInter
1981
1981
  export declare interface BrowserJourneyToolsetInterface {
1982
1982
  readonly recording: string | undefined;
1983
1983
  readonly replaying: string | undefined;
1984
- /** Aborts the active replay and removes the six journey tools from the manager. */
1984
+ /** Aborts the active replay and removes the journey tools from the manager. */
1985
1985
  destroy(): Promise<void>;
1986
1986
  }
1987
1987
 
@@ -3483,6 +3483,14 @@ export declare interface BrowserRunStep {
3483
3483
 
3484
3484
  /** Keeps runs by the journey name and run id the run carries. */
3485
3485
  export declare interface BrowserRunStoreInterface {
3486
+ /**
3487
+ * Saves a standalone PNG under the runs root without replacing an existing file.
3488
+ * @param bytes - PNG bytes to persist
3489
+ * @param options - Cancellation options
3490
+ * @returns Absolute path of the saved image
3491
+ * @remarks Stores without standalone file storage omit this capability.
3492
+ */
3493
+ snapshot?(bytes: Uint8Array, options?: BrowserStoreOptions): Promise<string>;
3486
3494
  /** Mints a run id for the journey and creates its run directory exclusively. */
3487
3495
  open(name: string, options?: BrowserStoreOptions): Promise<BrowserRunSlot>;
3488
3496
  /** Returns the run stored under the journey name and id, or `undefined` when none is stored. */
@@ -3967,9 +3975,9 @@ export declare interface BrowserRunStoreInterface {
3967
3975
  /**
3968
3976
  * Names a tool the browser toolset reserves: the generic tools, the staged `dialog`, the
3969
3977
  * opt-in `tabs` and `switch`, and the journey tools `record`, `save`, `journeys`, `edit`,
3970
- * `replay`, and `forget`, which a toolset constructed with `journeys` reserves.
3978
+ * `replay`, `forget`, and `capture`, which a toolset constructed with `journeys` reserves.
3971
3979
  */
3972
- export declare type BrowserToolName = 'look' | 'read' | 'plain' | 'click' | 'type' | 'press' | 'navigate' | 'wait' | 'dialog' | 'tabs' | 'switch' | 'record' | 'save' | 'journeys' | 'edit' | 'replay' | 'forget';
3980
+ export declare type BrowserToolName = 'look' | 'read' | 'plain' | 'click' | 'type' | 'press' | 'navigate' | 'wait' | 'dialog' | 'tabs' | 'switch' | 'record' | 'save' | 'journeys' | 'edit' | 'replay' | 'forget' | 'capture';
3973
3981
 
3974
3982
  /** Identifies a removed WebMCP tool by document and name. */
3975
3983
  export declare interface BrowserToolRemoval {
@@ -107,7 +107,7 @@ export declare const BROWSER_FRAME_WORLD_NAME = "__orkestrelBrowserFrame";
107
107
  */
108
108
  export declare const BROWSER_HAR_CREATOR: Readonly<{
109
109
  name: "@orkestrel/browser";
110
- version: "0.0.23";
110
+ version: "0.0.24";
111
111
  }>;
112
112
 
113
113
  /** Names accessibility roles that receive actionable outline references. */
@@ -159,7 +159,7 @@ export declare const BROWSER_JOURNEY_STEP_KEYS: readonly string[];
159
159
 
160
160
  /**
161
161
  * Names the journey tools a toolset constructed with `journeys` registers and reserves: `record`,
162
- * `save`, `journeys`, `edit`, `replay`, and `forget`.
162
+ * `save`, `journeys`, `edit`, `replay`, `forget`, and `capture`.
163
163
  */
164
164
  export declare const BROWSER_JOURNEY_TOOL_NAMES: readonly BrowserToolName[];
165
165
 
@@ -1801,7 +1801,7 @@ export declare type BrowserJourneyEditRequest = BrowserJourneyEdit | {
1801
1801
  *
1802
1802
  * @remarks
1803
1803
  * - `store` — keeps the journeys the tools record, list, edit, and replay
1804
- * - `runs` — keeps each replay's run; omitting it writes no run
1804
+ * - `runs` — keeps each replay's run and saves standalone images when it provides `snapshot`
1805
1805
  * - `limit` — positive integer listing cap in characters; Default: the owner's `limit`, including direct construction
1806
1806
  * - `readonly` — if `true`, refuses `record`, `save`, `edit`, and `forget` before any store access, and
1807
1807
  * `replay` still writes runs; if `false` or omitted, every tool runs
@@ -1930,13 +1930,13 @@ export declare interface BrowserJourneyTarget {
1930
1930
  }
1931
1931
 
1932
1932
  /**
1933
- * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, and `forget` over a toolset
1933
+ * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, `forget`, and `capture` over a toolset
1934
1934
  * and owns the recording and the active replay.
1935
1935
  *
1936
1936
  * @remarks
1937
1937
  * The journey toolset composes the toolset's public members: `perform` reads the view a receipt
1938
1938
  * carries through `look`, `hold` and `emitter` drive the recorder and the replay, `view` converts
1939
- * an edit's `ref` to a target, and `tools` receives the six tools at construction, which refuses
1939
+ * an edit's `ref` to a target, and `tools` receives the journey tools at construction, which refuses
1940
1940
  * with `BROWSER_TOOLSET_RESERVED` when the manager already holds one of the names. Every refusal
1941
1941
  * is a sentence that names the next call, and a reason inside it is a clause without a directive
1942
1942
  * or a final period.
@@ -1952,7 +1952,7 @@ export declare interface BrowserJourneyTarget {
1952
1952
  * by the view after the run.
1953
1953
  *
1954
1954
  * `destroy()` aborts the active replay and waits for it to finish, stops a recording without
1955
- * saving it, and removes the six tools the manager still holds under the instances it added.
1955
+ * saving it, and removes the journey tools the manager still holds under the instances it added.
1956
1956
  *
1957
1957
  * @example
1958
1958
  * ```ts
@@ -1972,7 +1972,7 @@ export declare class BrowserJourneyToolset implements BrowserJourneyToolsetInter
1972
1972
  }
1973
1973
 
1974
1974
  /**
1975
- * Registers the six journey tools over a toolset and owns the recording and the active replay.
1975
+ * Registers the journey tools over a toolset and owns the recording and the active replay.
1976
1976
  *
1977
1977
  * @remarks
1978
1978
  * - `recording` — the name of the journey being recorded, or `undefined` when none is
@@ -1981,7 +1981,7 @@ export declare class BrowserJourneyToolset implements BrowserJourneyToolsetInter
1981
1981
  export declare interface BrowserJourneyToolsetInterface {
1982
1982
  readonly recording: string | undefined;
1983
1983
  readonly replaying: string | undefined;
1984
- /** Aborts the active replay and removes the six journey tools from the manager. */
1984
+ /** Aborts the active replay and removes the journey tools from the manager. */
1985
1985
  destroy(): Promise<void>;
1986
1986
  }
1987
1987
 
@@ -3483,6 +3483,14 @@ export declare interface BrowserRunStep {
3483
3483
 
3484
3484
  /** Keeps runs by the journey name and run id the run carries. */
3485
3485
  export declare interface BrowserRunStoreInterface {
3486
+ /**
3487
+ * Saves a standalone PNG under the runs root without replacing an existing file.
3488
+ * @param bytes - PNG bytes to persist
3489
+ * @param options - Cancellation options
3490
+ * @returns Absolute path of the saved image
3491
+ * @remarks Stores without standalone file storage omit this capability.
3492
+ */
3493
+ snapshot?(bytes: Uint8Array, options?: BrowserStoreOptions): Promise<string>;
3486
3494
  /** Mints a run id for the journey and creates its run directory exclusively. */
3487
3495
  open(name: string, options?: BrowserStoreOptions): Promise<BrowserRunSlot>;
3488
3496
  /** Returns the run stored under the journey name and id, or `undefined` when none is stored. */
@@ -3967,9 +3975,9 @@ export declare interface BrowserRunStoreInterface {
3967
3975
  /**
3968
3976
  * Names a tool the browser toolset reserves: the generic tools, the staged `dialog`, the
3969
3977
  * opt-in `tabs` and `switch`, and the journey tools `record`, `save`, `journeys`, `edit`,
3970
- * `replay`, and `forget`, which a toolset constructed with `journeys` reserves.
3978
+ * `replay`, `forget`, and `capture`, which a toolset constructed with `journeys` reserves.
3971
3979
  */
3972
- export declare type BrowserToolName = 'look' | 'read' | 'plain' | 'click' | 'type' | 'press' | 'navigate' | 'wait' | 'dialog' | 'tabs' | 'switch' | 'record' | 'save' | 'journeys' | 'edit' | 'replay' | 'forget';
3980
+ export declare type BrowserToolName = 'look' | 'read' | 'plain' | 'click' | 'type' | 'press' | 'navigate' | 'wait' | 'dialog' | 'tabs' | 'switch' | 'record' | 'save' | 'journeys' | 'edit' | 'replay' | 'forget' | 'capture';
3973
3981
 
3974
3982
  /** Identifies a removed WebMCP tool by document and name. */
3975
3983
  export declare interface BrowserToolRemoval {
@@ -162,7 +162,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
162
162
  */
163
163
  var BROWSER_HAR_CREATOR = Object.freeze({
164
164
  name: "@orkestrel/browser",
165
- version: "0.0.23"
165
+ version: "0.0.24"
166
166
  });
167
167
  /** Names the attribute that tags temporary screenshot styles and masks. */
168
168
  var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
@@ -741,7 +741,7 @@ var BROWSER_TOOL_COPY = Object.freeze({
741
741
  description: "The journey name, such as add-kettle."
742
742
  }),
743
743
  edits: Object.freeze({
744
- description: "The changes, as an array or a JSON string of the array, applied in order; one invalid change refuses them all.",
744
+ description: "The changes, as an array or its JSON string, applied in order; one invalid change refuses them all.",
745
745
  anyOf: Object.freeze([Object.freeze({
746
746
  type: "array",
747
747
  items: Object.freeze({
@@ -817,6 +817,18 @@ var BROWSER_TOOL_COPY = Object.freeze({
817
817
  required: Object.freeze(["journey"])
818
818
  })
819
819
  }),
820
+ capture: Object.freeze({
821
+ name: "capture",
822
+ description: "Saves the current view as a PNG in the runs directory and returns its path.",
823
+ parameters: Object.freeze({
824
+ type: "object",
825
+ properties: Object.freeze({ full: Object.freeze({
826
+ type: "boolean",
827
+ description: "True captures the full page; false captures the viewport."
828
+ }) }),
829
+ required: Object.freeze(["full"])
830
+ })
831
+ }),
820
832
  forget: Object.freeze({
821
833
  name: "forget",
822
834
  description: "Removes a saved journey and all its runs; the name is free to record again.",
@@ -885,7 +897,7 @@ var BROWSER_ACTION_STAGES = Object.freeze([
885
897
  var BROWSER_JOURNEY_EMPTY_LISTING = "No journeys are saved; call record to start one.";
886
898
  /**
887
899
  * Names the journey tools a toolset constructed with `journeys` registers and reserves: `record`,
888
- * `save`, `journeys`, `edit`, `replay`, and `forget`.
900
+ * `save`, `journeys`, `edit`, `replay`, `forget`, and `capture`.
889
901
  */
890
902
  var BROWSER_JOURNEY_TOOL_NAMES = Object.freeze([
891
903
  "record",
@@ -893,7 +905,8 @@ var BROWSER_JOURNEY_TOOL_NAMES = Object.freeze([
893
905
  "journeys",
894
906
  "edit",
895
907
  "replay",
896
- "forget"
908
+ "forget",
909
+ "capture"
897
910
  ]);
898
911
  /** Names observation and journey tools that cannot become journey steps. */
899
912
  var BROWSER_JOURNEY_NON_STEP_TOOLS = Object.freeze([...BROWSER_OBSERVATION_TOOL_NAMES, ...BROWSER_JOURNEY_TOOL_NAMES]);
@@ -6450,9 +6463,11 @@ var BrowserPageElement = class {
6450
6463
  object: result["object"]["objectId"],
6451
6464
  context
6452
6465
  };
6453
- } catch {
6466
+ } catch (error) {
6454
6467
  options?.signal?.throwIfAborted();
6455
- throw new BrowserElementError(this.reference, "GONE");
6468
+ if (isCDPTimeoutError(error) || isCDPConnectionError(error)) throw error;
6469
+ if (isCDPError(error) && /Could not find node|No node with given id|No node found for given backend id/i.test(error.message)) throw new BrowserElementError(this.reference, "GONE");
6470
+ throw error;
6456
6471
  }
6457
6472
  }
6458
6473
  async #call(declaration, options, target) {
@@ -6481,7 +6496,7 @@ var BrowserPageElement = class {
6481
6496
  }
6482
6497
  #failure(options, error) {
6483
6498
  options?.signal?.throwIfAborted();
6484
- if (isBrowserElementError(error)) throw error;
6499
+ if (isBrowserElementError(error) || isCDPTimeoutError(error) || isCDPConnectionError(error)) throw error;
6485
6500
  const message = (isError(error) ? error.message : String(error)).split("\n", 1)[0] ?? "";
6486
6501
  const known = BROWSER_ELEMENT_REFUSALS.get(message.replace(/^Error: /, ""));
6487
6502
  if (known !== void 0) throw new BrowserElementError(this.reference, known.reason, known.detail);
@@ -6513,11 +6528,20 @@ var BrowserPageElement = class {
6513
6528
  session: this.#input.node.session,
6514
6529
  ...options
6515
6530
  }).catch(this.#failure.bind(this, options));
6516
- await this.#call(compileActionabilityFunction({
6531
+ await this.#call(`async function() {
6532
+ await (${compileActionabilityFunction({
6517
6533
  visible: true,
6518
6534
  enabled: true,
6519
6535
  stable: true
6520
- }), options);
6536
+ })}).call(this)
6537
+ let previous
6538
+ for (;;) {
6539
+ await new Promise((resolve) => requestAnimationFrame(resolve))
6540
+ const current = [window.scrollX, window.scrollY]
6541
+ if (previous && current.every((value, index) => value === previous[index])) return true
6542
+ previous = current
6543
+ }
6544
+ }`, options);
6521
6545
  const geometry = await this.#geometry(options);
6522
6546
  const point = geometry.page.center;
6523
6547
  const local = geometry.local.center;
@@ -6528,6 +6552,10 @@ var BrowserPageElement = class {
6528
6552
  }, {
6529
6553
  session: this.#input.node.session,
6530
6554
  ...options
6555
+ }).catch((error) => {
6556
+ options?.signal?.throwIfAborted();
6557
+ if (isCDPError(error) && error.context?.["message"] === "No node found at given location") throw new BrowserElementError(this.reference, "OCCLUDED", "hit-test location held no node");
6558
+ throw error;
6531
6559
  });
6532
6560
  if (!isRecord(hit) || !isInteger(hit["backendNodeId"]) || hit["frameId"] !== void 0 && hit["frameId"] !== this.#input.frame) throw new BrowserElementError(this.reference, "OCCLUDED");
6533
6561
  if (hit["backendNodeId"] !== this.#input.backend && await this.#call(compileHitFunction(), options, hit["backendNodeId"]) !== true) {
@@ -12277,13 +12305,13 @@ function createMemoryBrowserRunStore() {
12277
12305
  //#endregion
12278
12306
  //#region src/core/BrowserJourneyToolset.ts
12279
12307
  /**
12280
- * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, and `forget` over a toolset
12308
+ * Registers the journey tools `record`, `save`, `journeys`, `edit`, `replay`, `forget`, and `capture` over a toolset
12281
12309
  * and owns the recording and the active replay.
12282
12310
  *
12283
12311
  * @remarks
12284
12312
  * The journey toolset composes the toolset's public members: `perform` reads the view a receipt
12285
12313
  * carries through `look`, `hold` and `emitter` drive the recorder and the replay, `view` converts
12286
- * an edit's `ref` to a target, and `tools` receives the six tools at construction, which refuses
12314
+ * an edit's `ref` to a target, and `tools` receives the journey tools at construction, which refuses
12287
12315
  * with `BROWSER_TOOLSET_RESERVED` when the manager already holds one of the names. Every refusal
12288
12316
  * is a sentence that names the next call, and a reason inside it is a clause without a directive
12289
12317
  * or a final period.
@@ -12299,7 +12327,7 @@ function createMemoryBrowserRunStore() {
12299
12327
  * by the view after the run.
12300
12328
  *
12301
12329
  * `destroy()` aborts the active replay and waits for it to finish, stops a recording without
12302
- * saving it, and removes the six tools the manager still holds under the instances it added.
12330
+ * saving it, and removes the journey tools the manager still holds under the instances it added.
12303
12331
  *
12304
12332
  * @example
12305
12333
  * ```ts
@@ -12340,7 +12368,8 @@ var BrowserJourneyToolset = class {
12340
12368
  this.#create("journeys", this.#journeys.bind(this)),
12341
12369
  this.#create("edit", this.#edit.bind(this)),
12342
12370
  this.#create("replay", this.#replayJourney.bind(this)),
12343
- this.#create("forget", this.#forget.bind(this))
12371
+ this.#create("forget", this.#forget.bind(this)),
12372
+ this.#create("capture", this.#capture.bind(this))
12344
12373
  ]);
12345
12374
  toolset.tools.add(this.#tools);
12346
12375
  }
@@ -12367,6 +12396,22 @@ var BrowserJourneyToolset = class {
12367
12396
  signal.throwIfAborted();
12368
12397
  return handler(args, signal);
12369
12398
  }
12399
+ async #capture(args, signal) {
12400
+ const full = args["full"];
12401
+ if (!isBoolean(full)) throw new BrowserError("The full parameter must be true or false.", "BROWSER_TOOLSET_ARGUMENT");
12402
+ this.#idleReplay();
12403
+ const view = this.#toolset.view;
12404
+ if (!view.trusted) throw new BrowserError("This view is untrusted; capture requires a trusted browser view.", "BROWSER_CAPTURE_UNTRUSTED");
12405
+ if (view.screenshot === void 0) throw new BrowserError("This view cannot capture an image.", "BROWSER_CAPTURE_UNAVAILABLE");
12406
+ const runs = this.#runs;
12407
+ if (runs?.snapshot === void 0) throw new BrowserError("Capture requires a runs store with standalone file storage.", "BROWSER_CAPTURE_UNAVAILABLE");
12408
+ const screenshot = await view.screenshot({
12409
+ format: "png",
12410
+ full
12411
+ });
12412
+ signal.throwIfAborted();
12413
+ return runs.snapshot(screenshot.bytes, { signal });
12414
+ }
12370
12415
  async #record(args, signal) {
12371
12416
  if (this.#readonly) throw new BrowserError(BROWSER_JOURNEY_READONLY_REFUSAL, "BROWSER_JOURNEY_READONLY");
12372
12417
  this.#idleReplay();