effect-browserbase 0.1.0-beta.102 → 0.1.0-beta.104

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.
@@ -1,20 +1,17 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
1
  import { Identifier } from "./References.mjs";
3
2
  import { BrowserError, InitializationError, SessionError } from "./Errors.mjs";
4
- import { DownloadObservation } from "./Transfers.mjs";
5
3
  import { t as BrowserbaseClient } from "./Client-D1_xYM9o.mjs";
6
- import { ActionResult, AutomationOptions, BrowserPolicy, Checkpoint, CheckpointOptions, ClickRequest, FillRequest, HoverRequest, InlineFiles, InputReceipt, KeyStroke, NavigateRequest, NavigationResult, Observation, ObservationOptions, ObservedElement, PointerMoveRequest, PressRequest, ReadTextRequest, ScreenshotRequest, ScreenshotResult, ScrollRequest, Selector, StartNavigationRequest, Target, TextResult, TypeRequest, Viewport, WheelRequest } from "./BrowserData.mjs";
4
+ import { ActionResult, Checkpoint, CheckpointOptions, ClickRequest, FillRequest, HoverRequest, InputReceipt, KeyStroke, NavigateRequest, NavigationResult, Observation, ObservationOptions, ObservedElement, PointerMoveRequest, PressRequest, ReadTextRequest, ScreenshotRequest, ScreenshotResult, ScrollRequest, StartNavigationRequest, Target, TextResult, TypeRequest, Viewport, WheelRequest } from "./BrowserData.mjs";
7
5
  import { isTerminalSessionStatus } from "./SessionData.mjs";
8
6
  import { n as nowMillis, r as until, t as deadlineAfter } from "./Deadline-CrViaogr.mjs";
9
7
  import { o as issueLiveView, t as BrowserbaseSessions } from "./Sessions-CtbseWEh.mjs";
10
- import { t as issuedUpload } from "./Issued-DejqoMYL.mjs";
11
8
  import { a as reported, i as noLocalConnection, n as reportCleanup, o as connectionAddress, r as makeCleanup, t as acquireRemote } from "./Acquisition-WBafHwXH.mjs";
12
- import { d as empty, h as snapshot, m as invoke, s as Plan } from "./Bootstrap-BXuzZojd.mjs";
9
+ import { h as snapshot, m as invoke, s as Plan } from "./Bootstrap-BXuzZojd.mjs";
13
10
  import { o as publicError } from "./Images-DgscEfSf.mjs";
14
- import { a as bindingImplementation, t as BrowserbaseBrowserBinding } from "./BrowserBinding-qbQA6JhH.mjs";
11
+ import { o as bindingImplementation, t as BrowserbaseBrowserBinding } from "./BrowserBinding-DlJTGYZL.mjs";
15
12
  import { t as associate } from "./Association-CEps4hYP.mjs";
16
13
  import { t as associatePageControl } from "./PageControlAssociation-DabEg5AR.mjs";
17
- import { Cause, Clock, Context, Deferred, Effect, Exit, FiberSet, Layer, Option, Redacted, Schema, Scope, Semaphore } from "effect";
14
+ import { Cause, Clock, Deferred, Effect, Exit, FiberSet, Option, Redacted, Schema, Scope, Semaphore } from "effect";
18
15
  //#region src/internal/browser/BindingRunner.ts
19
16
  const makeBindingRunner = (capacity, handle, onFault) => {
20
17
  if (!Number.isSafeInteger(capacity) || capacity < 1) throw new RangeError("Invalid binding callback capacity");
@@ -333,6 +330,136 @@ const makeBindings = Effect.fnUntraced(function* (plan) {
333
330
  };
334
331
  });
335
332
  //#endregion
333
+ //#region src/internal/browser/PublicSession.ts
334
+ const checked = (schema, value, operation) => Schema.decodeUnknownEffect(schema)(value, { onExcessProperty: "error" }).pipe(Effect.mapError(() => BrowserError.make({
335
+ operation,
336
+ reason: "configuration",
337
+ outcome: "undispatched"
338
+ })));
339
+ const decoded = (schema, operation) => (value) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => BrowserError.make({
340
+ operation,
341
+ reason: "malformed"
342
+ })));
343
+ const action = decoded(ActionResult, "action-result");
344
+ const pointerMoved = decoded(InputReceipt, "pointer-move");
345
+ const hovered = decoded(InputReceipt, "hover");
346
+ const wheeled = decoded(InputReceipt, "wheel");
347
+ const pressed = decoded(InputReceipt, "press");
348
+ const typed = decoded(InputReceipt, "type");
349
+ const makeTarget = (bound) => ({
350
+ navigate: (request) => checked(NavigateRequest, request, "navigate").pipe(Effect.flatMap((value) => bound.navigate(value.url)), Effect.flatMap((url) => decoded(NavigationResult, "navigate")({ url }))),
351
+ startNavigation: (request) => checked(StartNavigationRequest, request, "navigate").pipe(Effect.flatMap((value) => bound.startNavigation(value.url, value.timeoutMillis)), Effect.map((operation) => ({
352
+ target: operation.target,
353
+ completed: operation.completed.pipe(Effect.flatMap((url) => decoded(NavigationResult, "navigate")({ url }))),
354
+ stop: operation.stop
355
+ }))),
356
+ readText: (request) => checked(ReadTextRequest, request, "read-text").pipe(Effect.flatMap((value) => bound.readText(value.selector)), Effect.flatMap((text) => decoded(TextResult, "read-text")({ text }))),
357
+ click: (request) => checked(ClickRequest, request, "click").pipe(Effect.flatMap((value) => bound.click(value.selector)), Effect.flatMap((url) => action({ url }))),
358
+ fill: (request) => checked(FillRequest, request, "fill").pipe(Effect.flatMap((value) => bound.fill(value.selector, value.value)), Effect.flatMap((url) => action({ url }))),
359
+ scroll: (request) => checked(ScrollRequest, request, "scroll").pipe(Effect.flatMap((value) => bound.scroll(value.deltaX, value.deltaY)), Effect.flatMap((url) => action({ url }))),
360
+ pointerMove: (request) => checked(PointerMoveRequest, request, "pointer-move").pipe(Effect.flatMap((value) => bound.pointerMove(value.to)), Effect.flatMap((input) => pointerMoved({
361
+ ...input,
362
+ kind: "pointer-move"
363
+ }))),
364
+ hover: (request) => checked(HoverRequest, request, "hover").pipe(Effect.flatMap((value) => bound.hover(value.selector)), Effect.flatMap((input) => hovered({
365
+ ...input,
366
+ kind: "hover"
367
+ }))),
368
+ wheel: (request) => checked(WheelRequest, request, "wheel").pipe(Effect.flatMap((value) => bound.wheel(value.deltaX, value.deltaY, value.at).pipe(Effect.flatMap((input) => wheeled({
369
+ ...input,
370
+ kind: "wheel",
371
+ delta: {
372
+ x: value.deltaX,
373
+ y: value.deltaY
374
+ }
375
+ }))))),
376
+ press: (request) => checked(PressRequest, request, "press").pipe(Effect.flatMap((value) => bound.press(value.key, value.modifiers ?? [], value.into)), Effect.flatMap((input) => pressed({
377
+ ...input,
378
+ kind: "press"
379
+ }))),
380
+ type: (request) => checked(TypeRequest, request, "type").pipe(Effect.flatMap((value) => bound.type(value.text, value.into)), Effect.flatMap((input) => typed({
381
+ ...input,
382
+ kind: "type"
383
+ }))),
384
+ screenshot: (request) => checked(ScreenshotRequest, request, "screenshot").pipe(Effect.flatMap((value) => bound.screenshot(value.fullPage)), Effect.flatMap((bytes) => decoded(ScreenshotResult, "screenshot")({
385
+ mediaType: "image/png",
386
+ bytes: new Uint8Array(bytes)
387
+ })))
388
+ });
389
+ /** A browser-operation failure keeps its meaning when it is reported as an initialization one. */
390
+ const initialization = (reason) => reason === "busy" ? "busy" : reason === "closed" || reason === "disconnected" ? "closed" : reason === "timeout" ? "timeout" : reason === "stale" ? "stale" : reason === "unsupported" ? "unsupported" : reason === "configuration" ? "configuration" : "native";
391
+ const makeSession = (controls, bindings) => {
392
+ const currentTarget = controls.currentTarget.pipe(Effect.map(() => makeTarget(controls.bind())));
393
+ const Wait = Schema.Struct({
394
+ selector: ClickRequest.fields.selector,
395
+ state: Schema.Literals([
396
+ "visible",
397
+ "hidden",
398
+ "attached",
399
+ "detached"
400
+ ])
401
+ });
402
+ const navigate = (url) => action({ url });
403
+ const session = {
404
+ failure: bindings.failure,
405
+ bindingDiagnostics: bindings.diagnostics,
406
+ bind: () => makeTarget(controls.bind()),
407
+ currentTarget,
408
+ target: controls.currentTarget,
409
+ observe: (options = {}) => checked(ObservationOptions, options, "observe").pipe(Effect.flatMap((value) => controls.observe({
410
+ ...value,
411
+ scope: value.scope ?? "document"
412
+ }))),
413
+ checkpoint: (options = {}) => checked(CheckpointOptions, options, "checkpoint").pipe(Effect.flatMap((value) => controls.checkpoint({
414
+ ...value,
415
+ picture: value.picture ?? false
416
+ })), Effect.flatMap(({ picture, ...sampled }) => decoded(Checkpoint, "checkpoint")({
417
+ ...sampled,
418
+ ...picture === void 0 ? {} : { picture: {
419
+ mediaType: "image/png",
420
+ bytes: new Uint8Array(picture)
421
+ } }
422
+ }))),
423
+ controlFacts: (reference) => checked(ObservedElement, reference, "control-facts").pipe(Effect.flatMap(controls.controlFacts)),
424
+ revalidateElement: (reference) => checked(ObservedElement, reference, "revalidate").pipe(Effect.flatMap((value) => controls.revalidate(value).pipe(Effect.as(value)))),
425
+ clickElement: (reference, admission) => checked(ObservedElement, reference, "click").pipe(Effect.flatMap((value) => controls.bind().click(value, admission?.admit)), Effect.flatMap(navigate)),
426
+ fillElement: (reference, value, admission) => checked(ObservedElement, reference, "fill").pipe(Effect.flatMap((element) => checked(FillRequest.fields.value, value, "fill").pipe(Effect.flatMap((text) => controls.bind().fill(element, text, admission?.admit)))), Effect.flatMap(navigate)),
427
+ hoverElement: (reference, admission) => checked(ObservedElement, reference, "hover").pipe(Effect.flatMap((value) => controls.bind().hover(value, admission?.admit)), Effect.flatMap((input) => hovered({
428
+ ...input,
429
+ kind: "hover"
430
+ }))),
431
+ pressElement: (reference, stroke, admission) => checked(ObservedElement, reference, "press").pipe(Effect.flatMap((element) => checked(KeyStroke, stroke, "press").pipe(Effect.flatMap((value) => controls.bind().press(value.key, value.modifiers ?? [], element, admission?.admit)))), Effect.flatMap((input) => pressed({
432
+ ...input,
433
+ kind: "press"
434
+ }))),
435
+ typeElement: (reference, text, admission) => checked(ObservedElement, reference, "type").pipe(Effect.flatMap((element) => checked(TypeRequest.fields.text, text, "type").pipe(Effect.flatMap((value) => controls.bind().type(value, element, admission?.admit)))), Effect.flatMap((input) => typed({
436
+ ...input,
437
+ kind: "type"
438
+ }))),
439
+ pages: controls.pages,
440
+ frames: controls.frames,
441
+ selectPage: (id) => checked(Identifier, id, "select-page").pipe(Effect.flatMap(controls.selectPage), Effect.andThen(currentTarget)),
442
+ selectFrame: (id) => checked(Identifier, id, "select-frame").pipe(Effect.flatMap(controls.selectFrame), Effect.andThen(currentTarget)),
443
+ createPage: controls.createPage(),
444
+ closePage: (id) => checked(Identifier, id, "close-page").pipe(Effect.flatMap(controls.closePage)),
445
+ resizeViewport: (viewport) => checked(Viewport, viewport, "resize").pipe(Effect.flatMap(controls.resize)),
446
+ waitFor: (request) => checked(Wait, request, "wait").pipe(Effect.flatMap((value) => controls.waitFor(value.selector, value.state))),
447
+ clickAndWait: (request) => checked(ClickRequest, request, "click-and-wait").pipe(Effect.flatMap((value) => controls.clickAndWait(value.selector)), Effect.flatMap((url) => action({ url }))),
448
+ ready: controls.readiness.pipe(Effect.mapError((error) => InitializationError.make({
449
+ operation: "ready",
450
+ step: "session",
451
+ reason: initialization(error.reason)
452
+ })), Effect.flatMap((state) => state._tag === "NotReady" ? Effect.fail(InitializationError.make({
453
+ operation: "ready",
454
+ step: state.step,
455
+ reason: state.reason === "failed" ? "output" : state.reason
456
+ })) : Effect.succeed({ _tag: state._tag })))
457
+ };
458
+ associate(session, controls.capture);
459
+ associatePageControl(session, controls.pageControl);
460
+ return session;
461
+ };
462
+ //#endregion
336
463
  //#region src/internal/session/Attachment.ts
337
464
  const failure = (reason) => SessionError.make({
338
465
  operation: "session-attach",
@@ -580,28 +707,52 @@ const native = (operation, ticket, body) => Effect.callback((resume) => {
580
707
  //#endregion
581
708
  //#region src/internal/browser/Session.ts
582
709
  /** Allocates the session, and therefore owns releasing it. */
583
- const ownedRemote = (options) => (local, lifetimeDeadline) => acquireRemote({
584
- launch: options.launch,
585
- ...options.contextWriter === void 0 ? {} : { contextWriter: options.contextWriter },
586
- allocationDeadline: lifetimeDeadline,
587
- ...options.onCleanup === void 0 ? {} : { onCleanup: options.onCleanup },
588
- ...options.onAllocationUncertain === void 0 ? {} : { onAllocationUncertain: options.onAllocationUncertain }
589
- }, local);
710
+ const providerControls = (client, sessions, reference) => ({
711
+ liveView: (ttl) => issueLiveView(client, reference, ttl),
712
+ verifyReconnect: sessions.retrieve(reference).pipe(Effect.mapError((error) => BrowserError.make({
713
+ operation: "reconnect",
714
+ reason: error.reason
715
+ })), Effect.flatMap((status) => status.status === "RUNNING" ? Effect.void : Effect.fail(BrowserError.make({
716
+ operation: "reconnect",
717
+ reason: "expired"
718
+ }))))
719
+ });
720
+ const ownedRemote = (options) => (local, lifetimeDeadline) => Effect.gen(function* () {
721
+ const client = yield* BrowserbaseClient;
722
+ const sessions = yield* BrowserbaseSessions;
723
+ const acquired = yield* acquireRemote({
724
+ launch: options.launch,
725
+ ...options.contextWriter === void 0 ? {} : { contextWriter: options.contextWriter },
726
+ allocationDeadline: lifetimeDeadline,
727
+ ...options.onCleanup === void 0 ? {} : { onCleanup: options.onCleanup },
728
+ ...options.onAllocationUncertain === void 0 ? {} : { onAllocationUncertain: options.onAllocationUncertain }
729
+ }, local);
730
+ return {
731
+ ...acquired,
732
+ ...providerControls(client, sessions, acquired.reference)
733
+ };
734
+ });
590
735
  /** Borrows a running session: local connection only, and never a release request. */
591
- const borrowedRemote = (options) => (local) => attachRemote(options, local);
736
+ const borrowedRemote = (options) => (local) => Effect.gen(function* () {
737
+ const client = yield* BrowserbaseClient;
738
+ const sessions = yield* BrowserbaseSessions;
739
+ const acquired = yield* attachRemote(options, local);
740
+ return {
741
+ ...acquired,
742
+ ...providerControls(client, sessions, acquired.reference)
743
+ };
744
+ });
592
745
  /**
593
746
  * The one owned browser. Remote allocation, release and terminal observation belong to the
594
747
  * canonical control plane; this owner supplies only the local connection's cleanup evidence.
595
748
  */
596
749
  const acquireSession = Effect.fnUntraced(function* (limits, options) {
597
- const engine = bindingImplementation(yield* BrowserbaseBrowserBinding);
750
+ const engine = options.engine ?? bindingImplementation(yield* BrowserbaseBrowserBinding);
598
751
  if (engine === void 0) return yield* BrowserError.make({
599
752
  operation: "connect",
600
753
  reason: "configuration",
601
754
  outcome: "undispatched"
602
755
  });
603
- const client = yield* BrowserbaseClient;
604
- const sessions = yield* BrowserbaseSessions;
605
756
  const parentScope = yield* Scope.Scope;
606
757
  const bindingLifetime = yield* Scope.fork(parentScope, "sequential");
607
758
  const ended = yield* Deferred.make();
@@ -685,6 +836,7 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
685
836
  owner.fence("closing", "closed");
686
837
  handoffToken = void 0;
687
838
  activeConnection = void 0;
839
+ Deferred.doneUnsafe(ended, Effect.void);
688
840
  }),
689
841
  capture: Effect.suspend(() => Effect.forEach([...capture.captureLeases.values()], (lease) => lease.stop, { discard: true })),
690
842
  initialization: disposeBindings,
@@ -705,6 +857,8 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
705
857
  };
706
858
  const acquired = yield* options.remote(local, owner.lifetimeDeadline);
707
859
  const ref = acquired.reference;
860
+ const release = acquired.release;
861
+ const cleanupResult = acquired.cleanupResult;
708
862
  const closeScope = acquired.release.pipe(Effect.ensuring(Deferred.succeed(ended, void 0)), Effect.asVoid);
709
863
  const connectionEvents = (connectionLease) => ({
710
864
  invalidate: (reason) => {
@@ -1058,8 +1212,8 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
1058
1212
  capture,
1059
1213
  bind,
1060
1214
  currentTarget: readSelected,
1061
- cleanupResult: acquired.cleanupResult,
1062
- close: acquired.release.pipe(Effect.ensuring(Deferred.succeed(ended, void 0))),
1215
+ cleanupResult,
1216
+ close: release,
1063
1217
  observe: (reading = { scope: "document" }) => textBudget("observe", reading.maxTextBytes).pipe(Effect.flatMap((bytes) => owner.guard("observe", (ticket) => observeInside(ticket, bytes, reading.maxControls ?? 32, true, reading.scope)))),
1064
1218
  /**
1065
1219
  * Passive: not a mutation, so the action observation and the selection are left exactly as
@@ -1110,19 +1264,23 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
1110
1264
  clickForDownload: (target) => nativeOperation("download-action", (driver, ticket) => driver.clickForDownload(target, ticket), { mutation: true }),
1111
1265
  selectFiles: (target, files) => nativeOperation("select-files", (driver, ticket) => driver.selectFiles(target, files, ticket), { mutation: true }),
1112
1266
  clickForFileSelection: (target, files) => nativeOperation("file-chooser", (driver, ticket) => driver.clickForFileSelection(target, files, ticket), { mutation: true }),
1113
- liveView: (ttl) => owner.guard("live-view", () => issueLiveView(client, ref, ttl), {
1267
+ liveView: (ttl) => owner.guard("live-view", () => acquired.liveView === void 0 ? Effect.fail(BrowserError.make({
1268
+ operation: "live-view",
1269
+ reason: "unsupported",
1270
+ outcome: "undispatched"
1271
+ })) : acquired.liveView(ttl), {
1114
1272
  charge: false,
1115
1273
  phases: ["open", "paused"]
1116
1274
  }),
1117
1275
  beginHandoff: (ttl) => owner.guard("handoff", () => Effect.gen(function* () {
1118
- if (options.driver.pageControl) return yield* BrowserError.make({
1276
+ if (options.driver.pageControl || acquired.liveView === void 0) return yield* BrowserError.make({
1119
1277
  operation: "handoff",
1120
1278
  reason: "unsupported",
1121
1279
  outcome: "undispatched"
1122
1280
  });
1123
1281
  if (owner.state.phase === "open") owner.fence("paused", "paused");
1124
1282
  handoffToken ??= globalThis.crypto.randomUUID();
1125
- const view = yield* issueLiveView(client, ref, ttl);
1283
+ const view = yield* acquired.liveView(ttl);
1126
1284
  return {
1127
1285
  token: Redacted.make(handoffToken),
1128
1286
  view
@@ -1178,18 +1336,12 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
1178
1336
  verifyAfter: false
1179
1337
  }).pipe(Effect.onError(() => Effect.sync(() => owner.fence("uncertain", "uncertain")))),
1180
1338
  reconnect: (operatorReleasedControl) => owner.guard("reconnect", (ticket) => Effect.gen(function* () {
1181
- if (!options.keepAlive || reconnectTarget === void 0 || !operatorReleasedControl) return yield* BrowserError.make({
1339
+ if (!options.keepAlive || reconnectTarget === void 0 || !operatorReleasedControl || acquired.verifyReconnect === void 0) return yield* BrowserError.make({
1182
1340
  operation: "reconnect",
1183
1341
  reason: "unsupported",
1184
1342
  outcome: "undispatched"
1185
1343
  });
1186
- if ((yield* sessions.retrieve(ref).pipe(Effect.mapError((error) => BrowserError.make({
1187
- operation: "reconnect",
1188
- reason: error.reason
1189
- })))).status !== "RUNNING") return yield* BrowserError.make({
1190
- operation: "reconnect",
1191
- reason: "expired"
1192
- });
1344
+ yield* acquired.verifyReconnect;
1193
1345
  owner.state.phase = "acquiring";
1194
1346
  const endpoint = yield* connectionUrl("reconnect");
1195
1347
  yield* connectNative(endpoint, {
@@ -1222,336 +1374,6 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
1222
1374
  };
1223
1375
  });
1224
1376
  //#endregion
1225
- //#region src/Browser.ts
1226
- var Browser_exports = /* @__PURE__ */ __exportAll({ BrowserbaseBrowser: () => BrowserbaseBrowser });
1227
- const checked = (schema, value, operation) => Schema.decodeUnknownEffect(schema)(value, { onExcessProperty: "error" }).pipe(Effect.mapError(() => BrowserError.make({
1228
- operation,
1229
- reason: "configuration",
1230
- outcome: "undispatched"
1231
- })));
1232
- const decoded = (schema, operation) => (value) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => BrowserError.make({
1233
- operation,
1234
- reason: "malformed"
1235
- })));
1236
- const action = decoded(ActionResult, "action-result");
1237
- const pointerMoved = decoded(InputReceipt, "pointer-move");
1238
- const hovered = decoded(InputReceipt, "hover");
1239
- const wheeled = decoded(InputReceipt, "wheel");
1240
- const pressed = decoded(InputReceipt, "press");
1241
- const typed = decoded(InputReceipt, "type");
1242
- const makeTarget = (bound) => ({
1243
- navigate: (request) => checked(NavigateRequest, request, "navigate").pipe(Effect.flatMap((value) => bound.navigate(value.url)), Effect.flatMap((url) => decoded(NavigationResult, "navigate")({ url }))),
1244
- startNavigation: (request) => checked(StartNavigationRequest, request, "navigate").pipe(Effect.flatMap((value) => bound.startNavigation(value.url, value.timeoutMillis)), Effect.map((operation) => ({
1245
- target: operation.target,
1246
- completed: operation.completed.pipe(Effect.flatMap((url) => decoded(NavigationResult, "navigate")({ url }))),
1247
- stop: operation.stop
1248
- }))),
1249
- readText: (request) => checked(ReadTextRequest, request, "read-text").pipe(Effect.flatMap((value) => bound.readText(value.selector)), Effect.flatMap((text) => decoded(TextResult, "read-text")({ text }))),
1250
- click: (request) => checked(ClickRequest, request, "click").pipe(Effect.flatMap((value) => bound.click(value.selector)), Effect.flatMap((url) => action({ url }))),
1251
- fill: (request) => checked(FillRequest, request, "fill").pipe(Effect.flatMap((value) => bound.fill(value.selector, value.value)), Effect.flatMap((url) => action({ url }))),
1252
- scroll: (request) => checked(ScrollRequest, request, "scroll").pipe(Effect.flatMap((value) => bound.scroll(value.deltaX, value.deltaY)), Effect.flatMap((url) => action({ url }))),
1253
- pointerMove: (request) => checked(PointerMoveRequest, request, "pointer-move").pipe(Effect.flatMap((value) => bound.pointerMove(value.to)), Effect.flatMap((input) => pointerMoved({
1254
- ...input,
1255
- kind: "pointer-move"
1256
- }))),
1257
- hover: (request) => checked(HoverRequest, request, "hover").pipe(Effect.flatMap((value) => bound.hover(value.selector)), Effect.flatMap((input) => hovered({
1258
- ...input,
1259
- kind: "hover"
1260
- }))),
1261
- wheel: (request) => checked(WheelRequest, request, "wheel").pipe(Effect.flatMap((value) => bound.wheel(value.deltaX, value.deltaY, value.at).pipe(Effect.flatMap((input) => wheeled({
1262
- ...input,
1263
- kind: "wheel",
1264
- delta: {
1265
- x: value.deltaX,
1266
- y: value.deltaY
1267
- }
1268
- }))))),
1269
- press: (request) => checked(PressRequest, request, "press").pipe(Effect.flatMap((value) => bound.press(value.key, value.modifiers ?? [], value.into)), Effect.flatMap((input) => pressed({
1270
- ...input,
1271
- kind: "press"
1272
- }))),
1273
- type: (request) => checked(TypeRequest, request, "type").pipe(Effect.flatMap((value) => bound.type(value.text, value.into)), Effect.flatMap((input) => typed({
1274
- ...input,
1275
- kind: "type"
1276
- }))),
1277
- screenshot: (request) => checked(ScreenshotRequest, request, "screenshot").pipe(Effect.flatMap((value) => bound.screenshot(value.fullPage)), Effect.flatMap((bytes) => decoded(ScreenshotResult, "screenshot")({
1278
- mediaType: "image/png",
1279
- bytes: new Uint8Array(bytes)
1280
- })))
1281
- });
1282
- /**
1283
- * Uploaded selection is admitted by the identity of a receipt this package issued for this
1284
- * exact session, never by a path read off the caller's value. A fabricated receipt has no
1285
- * issuance record, so an arbitrary server pathname can never become an attached file.
1286
- */
1287
- const selection = (request, reference, operation) => Effect.suspend(() => {
1288
- const invalid = BrowserError.make({
1289
- operation,
1290
- reason: "configuration",
1291
- outcome: "undispatched"
1292
- });
1293
- if (request.selection._tag === "Inline") return checked(InlineFiles, request.selection.files, operation).pipe(Effect.map((files) => files.map((file) => ({
1294
- _tag: "Inline",
1295
- name: file.name,
1296
- mediaType: file.mediaType,
1297
- bytes: new Uint8Array(file.bytes)
1298
- }))));
1299
- const uploads = request.selection.uploads;
1300
- if (!Array.isArray(uploads) || uploads.length < 1 || uploads.length > 8) return Effect.fail(invalid);
1301
- const files = [];
1302
- for (const receipt of uploads) {
1303
- const issued = issuedUpload(receipt);
1304
- if (issued === void 0) return Effect.fail(BrowserError.make({
1305
- operation,
1306
- reason: "authorization",
1307
- outcome: "undispatched"
1308
- }));
1309
- if (issued.reference.projectId !== reference.projectId || issued.reference.sessionId !== reference.sessionId) return Effect.fail(BrowserError.make({
1310
- operation,
1311
- reason: "authorization",
1312
- outcome: "undispatched"
1313
- }));
1314
- files.push({
1315
- _tag: "Remote",
1316
- path: issued.remotePath
1317
- });
1318
- }
1319
- return Effect.succeed(files);
1320
- });
1321
- /** A browser-operation failure keeps its meaning when it is reported as an initialization one. */
1322
- const initialization = (reason) => reason === "busy" ? "busy" : reason === "closed" || reason === "disconnected" ? "closed" : reason === "timeout" ? "timeout" : reason === "stale" ? "stale" : reason === "unsupported" ? "unsupported" : reason === "configuration" ? "configuration" : "native";
1323
- const makeSession = (controls, bindings) => {
1324
- const currentTarget = controls.currentTarget.pipe(Effect.map(() => makeTarget(controls.bind())));
1325
- const Wait = Schema.Struct({
1326
- selector: ClickRequest.fields.selector,
1327
- state: Schema.Literals([
1328
- "visible",
1329
- "hidden",
1330
- "attached",
1331
- "detached"
1332
- ])
1333
- });
1334
- const navigate = (url) => action({ url });
1335
- const session = {
1336
- reference: controls.reference,
1337
- failure: bindings.failure,
1338
- bindingDiagnostics: bindings.diagnostics,
1339
- bind: () => makeTarget(controls.bind()),
1340
- currentTarget,
1341
- target: controls.currentTarget,
1342
- observe: (options = {}) => checked(ObservationOptions, options, "observe").pipe(Effect.flatMap((value) => controls.observe({
1343
- ...value,
1344
- scope: value.scope ?? "document"
1345
- }))),
1346
- checkpoint: (options = {}) => checked(CheckpointOptions, options, "checkpoint").pipe(Effect.flatMap((value) => controls.checkpoint({
1347
- ...value,
1348
- picture: value.picture ?? false
1349
- })), Effect.flatMap(({ picture, ...sampled }) => decoded(Checkpoint, "checkpoint")({
1350
- ...sampled,
1351
- ...picture === void 0 ? {} : { picture: {
1352
- mediaType: "image/png",
1353
- bytes: new Uint8Array(picture)
1354
- } }
1355
- }))),
1356
- controlFacts: (reference) => checked(ObservedElement, reference, "control-facts").pipe(Effect.flatMap(controls.controlFacts)),
1357
- revalidateElement: (reference) => checked(ObservedElement, reference, "revalidate").pipe(Effect.flatMap((value) => controls.revalidate(value).pipe(Effect.as(value)))),
1358
- clickElement: (reference, admission) => checked(ObservedElement, reference, "click").pipe(Effect.flatMap((value) => controls.bind().click(value, admission?.admit)), Effect.flatMap(navigate)),
1359
- fillElement: (reference, value, admission) => checked(ObservedElement, reference, "fill").pipe(Effect.flatMap((element) => checked(FillRequest.fields.value, value, "fill").pipe(Effect.flatMap((text) => controls.bind().fill(element, text, admission?.admit)))), Effect.flatMap(navigate)),
1360
- hoverElement: (reference, admission) => checked(ObservedElement, reference, "hover").pipe(Effect.flatMap((value) => controls.bind().hover(value, admission?.admit)), Effect.flatMap((input) => hovered({
1361
- ...input,
1362
- kind: "hover"
1363
- }))),
1364
- pressElement: (reference, stroke, admission) => checked(ObservedElement, reference, "press").pipe(Effect.flatMap((element) => checked(KeyStroke, stroke, "press").pipe(Effect.flatMap((value) => controls.bind().press(value.key, value.modifiers ?? [], element, admission?.admit)))), Effect.flatMap((input) => pressed({
1365
- ...input,
1366
- kind: "press"
1367
- }))),
1368
- typeElement: (reference, text, admission) => checked(ObservedElement, reference, "type").pipe(Effect.flatMap((element) => checked(TypeRequest.fields.text, text, "type").pipe(Effect.flatMap((value) => controls.bind().type(value, element, admission?.admit)))), Effect.flatMap((input) => typed({
1369
- ...input,
1370
- kind: "type"
1371
- }))),
1372
- pages: controls.pages,
1373
- frames: controls.frames,
1374
- selectPage: (id) => checked(Identifier, id, "select-page").pipe(Effect.flatMap(controls.selectPage), Effect.andThen(currentTarget)),
1375
- selectFrame: (id) => checked(Identifier, id, "select-frame").pipe(Effect.flatMap(controls.selectFrame), Effect.andThen(currentTarget)),
1376
- createPage: controls.createPage(),
1377
- closePage: (id) => checked(Identifier, id, "close-page").pipe(Effect.flatMap(controls.closePage)),
1378
- resizeViewport: (viewport) => checked(Viewport, viewport, "resize").pipe(Effect.flatMap(controls.resize)),
1379
- waitFor: (request) => checked(Wait, request, "wait").pipe(Effect.flatMap((value) => controls.waitFor(value.selector, value.state))),
1380
- clickAndWait: (request) => checked(ClickRequest, request, "click-and-wait").pipe(Effect.flatMap((value) => controls.clickAndWait(value.selector)), Effect.flatMap((url) => action({ url }))),
1381
- clickForDownload: (request) => checked(ClickRequest, request, "download-action").pipe(Effect.flatMap((value) => controls.clickForDownload(value.selector)), Effect.flatMap((raw) => decoded(DownloadObservation, "download-action")({
1382
- ...raw,
1383
- reference: controls.reference
1384
- }))),
1385
- selectFiles: (request) => checked(Selector, request.selector, "select-files").pipe(Effect.flatMap((selector) => selection(request, controls.reference, "select-files").pipe(Effect.flatMap((files) => controls.selectFiles(selector, files)))), Effect.flatMap(navigate)),
1386
- clickForFileSelection: (request) => checked(Selector, request.selector, "file-chooser").pipe(Effect.flatMap((selector) => selection(request, controls.reference, "file-chooser").pipe(Effect.flatMap((files) => controls.clickForFileSelection(selector, files)))), Effect.flatMap(navigate)),
1387
- ready: controls.readiness.pipe(Effect.mapError((error) => InitializationError.make({
1388
- operation: "ready",
1389
- step: "session",
1390
- reason: initialization(error.reason)
1391
- })), Effect.flatMap((state) => state._tag === "NotReady" ? Effect.fail(InitializationError.make({
1392
- operation: "ready",
1393
- step: state.step,
1394
- reason: state.reason === "failed" ? "output" : state.reason
1395
- })) : Effect.succeed({ _tag: state._tag }))),
1396
- liveView: (ttl = 60) => controls.liveView(ttl),
1397
- beginHandoff: (ttl = 60) => controls.beginHandoff(ttl),
1398
- resume: (token, released) => controls.resume(token, released),
1399
- detach: controls.detach,
1400
- reconnect: (released) => controls.reconnect(released),
1401
- close: controls.close,
1402
- cleanupResult: controls.cleanupResult
1403
- };
1404
- associate(session, controls.capture);
1405
- associatePageControl(session, controls.pageControl);
1406
- return session;
1407
- };
1408
- /**
1409
- * Credentials, budgets and connection lifetime are fixed when this Layer is built. Building it
1410
- * allocates nothing and loads no native peer; every acquisition is scoped by its caller.
1411
- */
1412
- var BrowserbaseBrowser = class BrowserbaseBrowser extends Context.Service()("effect-browserbase/Browser") {
1413
- static layer(options) {
1414
- return Layer.effect(BrowserbaseBrowser, Effect.gen(function* () {
1415
- const client = yield* BrowserbaseClient;
1416
- const sessions = yield* BrowserbaseSessions;
1417
- const binding = yield* BrowserbaseBrowserBinding;
1418
- const automation = yield* checked(AutomationOptions, projected(options), "configure");
1419
- const viewport = yield* checked(Viewport, options.launch.viewport._tag === "Fixed" ? {
1420
- width: options.launch.viewport.width,
1421
- height: options.launch.viewport.height
1422
- } : {
1423
- width: 1280,
1424
- height: 720
1425
- }, "configure");
1426
- const actionTimeoutMillis = automation.actionTimeoutMillis ?? 1e4;
1427
- const maxPages = automation.maxPages ?? 10;
1428
- const pageControl = automation.pageControl ?? false;
1429
- const popupPolicy = automation.popupPolicy ?? "retain";
1430
- const dialogPolicy = automation.dialogPolicy ?? "dismiss";
1431
- if (pageControl && (options.launch.keepAlive === true || popupPolicy === "pause" || dialogPolicy === "pause")) return yield* BrowserError.make({
1432
- operation: "configure",
1433
- reason: "unsupported",
1434
- outcome: "undispatched"
1435
- });
1436
- if (options.launch.context?.persist === true && options.contextWriter === void 0) return yield* BrowserError.make({
1437
- operation: "configure",
1438
- reason: "context-lease"
1439
- });
1440
- if (Object.prototype.hasOwnProperty.call(options, "bootstrap")) return yield* BrowserError.make({
1441
- operation: "configure",
1442
- reason: "configuration",
1443
- outcome: "undispatched"
1444
- });
1445
- const driver = {
1446
- pageControl,
1447
- viewport,
1448
- maxPages,
1449
- popupPolicy,
1450
- dialogPolicy,
1451
- preserveViewport: options.launch.viewport._tag !== "Fixed",
1452
- ...automation.initialPage === void 0 ? {} : "targetId" in automation.initialPage ? { initialTargetId: automation.initialPage.targetId } : { newPage: true }
1453
- };
1454
- const acquire = Effect.fnUntraced(function* (policy, request = {}) {
1455
- const fixed = yield* checked(BrowserPolicy, policy, "configure");
1456
- const plan = yield* preparePlan(request.bootstrap ?? empty);
1457
- const scope = yield* Scope.fork(yield* Scope.Scope, "sequential");
1458
- const bindings = yield* makeBindings(plan).pipe(Scope.provide(scope));
1459
- const bootstrap = compileBootstrap(plan);
1460
- const acquired = yield* acquireSession({
1461
- maxActions: fixed.maxActions,
1462
- maxElapsedMillis: fixed.maxElapsedMillis,
1463
- actionTimeoutMillis
1464
- }, {
1465
- remote: ownedRemote({
1466
- launch: options.launch,
1467
- ...options.contextWriter === void 0 ? {} : { contextWriter: options.contextWriter },
1468
- ...options.onCleanup === void 0 ? {} : { onCleanup: options.onCleanup },
1469
- ...options.onAllocationUncertain === void 0 ? {} : { onAllocationUncertain: options.onAllocationUncertain }
1470
- }),
1471
- keepAlive: options.launch.keepAlive === true,
1472
- driver: {
1473
- ...driver,
1474
- ...bootstrap === void 0 ? {} : { bootstrap }
1475
- },
1476
- connectBindings: bindings.connect,
1477
- maxReturnedBytes: fixed.maxReturnedBytes
1478
- }).pipe(Effect.provideService(BrowserbaseClient, client), Effect.provideService(BrowserbaseSessions, sessions), Effect.provideService(BrowserbaseBrowserBinding, binding), Scope.provide(scope));
1479
- const connected = yield* Effect.cached(acquired.connect.pipe(Effect.map((controls) => makeSession(controls, bindings))));
1480
- return {
1481
- reference: acquired.reference,
1482
- attempt: acquired.lease.attempt,
1483
- failure: bindings.failure,
1484
- close: acquired.close,
1485
- connect: Effect.raceFirst(bindings.failure, acquired.connect.pipe(Effect.andThen(connected))).pipe(Effect.onError(() => acquired.close.pipe(Effect.asVoid)))
1486
- };
1487
- });
1488
- /**
1489
- * Borrowing a session another process allocated. The launch recipe is not replayed and
1490
- * no allocation is attempted: the requested target is resolved explicitly, credentials
1491
- * are fetched fresh because expiry races the caller's own status read, and this scope
1492
- * disconnects locally without ever requesting release. Detaching and reattaching inside
1493
- * a borrowed scope is deliberately absent; attaching again is the cross-process path,
1494
- * and it revalidates the session instead of assuming it is still there.
1495
- */
1496
- const attach = Effect.fnUntraced(function* (reference, request) {
1497
- const fixed = yield* checked(BrowserPolicy, request.policy, "configure");
1498
- const plan = yield* preparePlan(request.bootstrap ?? empty);
1499
- const scope = yield* Scope.fork(yield* Scope.Scope, "sequential");
1500
- const bindings = yield* makeBindings(plan).pipe(Scope.provide(scope));
1501
- const bootstrap = compileBootstrap(plan);
1502
- const targetId = request.target === void 0 ? void 0 : yield* checked(Identifier, request.target.targetId, "configure");
1503
- const acquired = yield* acquireSession({
1504
- maxActions: fixed.maxActions,
1505
- maxElapsedMillis: fixed.maxElapsedMillis,
1506
- actionTimeoutMillis
1507
- }, {
1508
- remote: borrowedRemote({
1509
- reference,
1510
- ...request.pendingWaitMillis === void 0 ? {} : { pendingWaitMillis: request.pendingWaitMillis },
1511
- ...options.onCleanup === void 0 ? {} : { onCleanup: options.onCleanup }
1512
- }),
1513
- keepAlive: false,
1514
- driver: {
1515
- ...driver,
1516
- ...bootstrap === void 0 ? {} : { bootstrap },
1517
- newPage: false,
1518
- preserveViewport: true,
1519
- ...targetId === void 0 ? {} : { initialTargetId: targetId }
1520
- },
1521
- maxReturnedBytes: fixed.maxReturnedBytes,
1522
- connectBindings: bindings.connect
1523
- }).pipe(Effect.provideService(BrowserbaseClient, client), Effect.provideService(BrowserbaseSessions, sessions), Effect.provideService(BrowserbaseBrowserBinding, binding), Scope.provide(scope));
1524
- return yield* Effect.raceFirst(bindings.failure, acquired.connect.pipe(Effect.map((controls) => makeSession(controls, bindings)))).pipe(Effect.onError(() => acquired.close.pipe(Effect.asVoid)));
1525
- });
1526
- return BrowserbaseBrowser.of({
1527
- acquire,
1528
- attach,
1529
- open: (policy, request) => acquire(policy, request).pipe(Effect.flatMap((acquired) => acquired.connect)),
1530
- withBrowser: (policy, request, use) => Effect.scoped(Effect.gen(function* () {
1531
- const session = yield* (yield* acquire(policy, request)).connect;
1532
- const result = yield* Effect.raceFirst(session.failure, Effect.suspend(() => use(session)));
1533
- const cleanup = yield* session.close;
1534
- if (cleanup.remote !== "confirmed" || cleanup.local !== "closed" || cleanup.issues.length > 0) return yield* BrowserError.make({
1535
- operation: "close",
1536
- reason: "provider",
1537
- outcome: "unknown"
1538
- });
1539
- return result;
1540
- }))
1541
- });
1542
- }));
1543
- }
1544
- };
1545
- /** Only the automation fields cross the schema boundary; launch and callbacks are separate. */
1546
- const projected = (options) => ({
1547
- ...options.actionTimeoutMillis === void 0 ? {} : { actionTimeoutMillis: options.actionTimeoutMillis },
1548
- ...options.maxPages === void 0 ? {} : { maxPages: options.maxPages },
1549
- ...options.initialPage === void 0 ? {} : { initialPage: options.initialPage },
1550
- ...options.popupPolicy === void 0 ? {} : { popupPolicy: options.popupPolicy },
1551
- ...options.dialogPolicy === void 0 ? {} : { dialogPolicy: options.dialogPolicy },
1552
- ...options.pageControl === void 0 ? {} : { pageControl: options.pageControl }
1553
- });
1554
- //#endregion
1555
- export { BrowserbaseBrowser as n, Browser_exports as t };
1377
+ export { decoded as a, preparePlan as c, checked as i, compileBootstrap as l, borrowedRemote as n, makeSession as o, ownedRemote as r, makeBindings as s, acquireSession as t };
1556
1378
 
1557
- //# sourceMappingURL=Browser-joE4Zgys.mjs.map
1379
+ //# sourceMappingURL=Session-D4nT9hOO.mjs.map