@effect-agent/platform-cloudflare 0.1.0-beta.41 → 0.1.0-beta.44

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.
Files changed (44) hide show
  1. package/dist/browser-quick-action.mjs.map +1 -1
  2. package/dist/browser-rest-capture.mjs.map +1 -1
  3. package/dist/browser-rest-crawl.mjs.map +1 -1
  4. package/dist/browser-session-lifecycle-DqntvG-Y.d.mts +21 -0
  5. package/dist/browser-session-lifecycle-ZGgb3pnK.mjs +84 -0
  6. package/dist/browser-session-lifecycle-ZGgb3pnK.mjs.map +1 -0
  7. package/dist/index.d.mts +85 -65
  8. package/dist/index.mjs +7 -2
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/interactive-browser.d.mts +1 -18
  11. package/dist/interactive-browser.mjs +2 -81
  12. package/dist/interactive-browser.mjs.map +1 -1
  13. package/dist/prepared-admission-BKp_Upw2.mjs.map +1 -1
  14. package/dist/protected-browser.d.mts +62 -0
  15. package/dist/protected-browser.mjs +714 -0
  16. package/dist/protected-browser.mjs.map +1 -0
  17. package/dist/scheduling.mjs.map +1 -1
  18. package/dist/subscriptions.mjs.map +1 -1
  19. package/package.json +1 -81
  20. package/src/alarm.ts +41 -0
  21. package/src/bindings.ts +5 -0
  22. package/src/boundary.ts +4 -0
  23. package/src/browser-quick-action.ts +52 -0
  24. package/src/browser-rest-capture.ts +30 -0
  25. package/src/browser-rest-crawl.ts +43 -0
  26. package/src/browser-session-lifecycle.ts +18 -0
  27. package/src/client.ts +40 -0
  28. package/src/code-mode-executor.ts +50 -0
  29. package/src/index.ts +1 -1
  30. package/src/interactive-browser.ts +176 -0
  31. package/src/layers.ts +16 -1
  32. package/src/memory.ts +42 -3
  33. package/src/prepared-admission.ts +1 -0
  34. package/src/progress-wait.ts +14 -0
  35. package/src/protected-browser/binding.ts +185 -0
  36. package/src/protected-browser/inspect-frame.ts +82 -0
  37. package/src/protected-browser/native.ts +384 -0
  38. package/src/protected-browser/policy.ts +594 -0
  39. package/src/protected-browser.ts +2 -0
  40. package/src/scheduling.ts +34 -0
  41. package/src/subscriptions.ts +50 -0
  42. package/src/thread-object.ts +55 -1
  43. package/src/transport.ts +1 -0
  44. package/src/wake-scheduler.ts +1 -0
@@ -69,16 +69,20 @@ const ACTION_NETWORK_SETTLE_MILLIS = 2_000;
69
69
  const ACTION_POST_STATE_MILLIS = 250;
70
70
  const MAX_OBSERVED_CONTROLS = 64;
71
71
  const PositiveInt = Schema.Int.check(Schema.isGreaterThan(0));
72
+
72
73
  const BoundedHostText = Schema.String.check(
73
74
  Schema.isMinLength(1),
74
75
  Schema.isMaxLength(MAX_HOST_TEXT_LENGTH),
75
76
  );
77
+
76
78
  const BoundedRemoteText = Schema.String.check(Schema.isMaxLength(MAX_TEXT_LENGTH));
79
+
77
80
  const TextObservation = Schema.Union([
78
81
  Schema.Struct({ _tag: Schema.Literal("Text"), text: BoundedRemoteText }),
79
82
  Schema.Struct({ _tag: Schema.Literal("MissingElement") }),
80
83
  Schema.Struct({ _tag: Schema.Literal("OverLimit"), observed: Schema.Natural }),
81
84
  ]);
85
+
82
86
  const ActionTargetState = Schema.Struct({
83
87
  matchCount: Schema.Natural,
84
88
  invalidSelector: Schema.optionalKey(Schema.Boolean),
@@ -92,6 +96,7 @@ const ActionTargetState = Schema.Struct({
92
96
  valid: Schema.optionalKey(Schema.Boolean),
93
97
  formValid: Schema.optionalKey(Schema.Boolean),
94
98
  });
99
+
95
100
  const ActionNetworkState = Schema.Struct({
96
101
  total: Schema.Natural,
97
102
  status2xx: Schema.Natural,
@@ -102,12 +107,14 @@ const ActionNetworkState = Schema.Struct({
102
107
  pending: Schema.Natural,
103
108
  settleTimedOut: Schema.Boolean,
104
109
  });
110
+
105
111
  const ActionObservation = Schema.Struct({
106
112
  before: ActionTargetState,
107
113
  after: Schema.optionalKey(ActionTargetState),
108
114
  afterUnavailable: Schema.Boolean,
109
115
  network: ActionNetworkState,
110
116
  });
117
+
111
118
  const PageObservation = Schema.fromJsonString(
112
119
  Schema.Struct({
113
120
  pageText: BoundedRemoteText,
@@ -128,6 +135,7 @@ const PageObservation = Schema.fromJsonString(
128
135
  ).check(Schema.isMaxLength(MAX_OBSERVED_CONTROLS)),
129
136
  }),
130
137
  );
138
+
131
139
  const PngBytes = Schema.Uint8Array.check(
132
140
  Schema.isMaxLength(MAX_SCREENSHOT_BYTES),
133
141
  Schema.makeFilter(
@@ -144,6 +152,7 @@ const PngBytes = Schema.Uint8Array.check(
144
152
  { title: "PNG bytes" },
145
153
  ),
146
154
  );
155
+
147
156
  const BrowserRunSessionId = Schema.String.check(
148
157
  Schema.isMinLength(1),
149
158
  Schema.isMaxLength(256),
@@ -151,12 +160,14 @@ const BrowserRunSessionId = Schema.String.check(
151
160
  title: "a Browser Run session identifier",
152
161
  }),
153
162
  );
163
+
154
164
  const LiveViewUrl = Schema.String.check(
155
165
  Schema.isMaxLength(MAX_HOST_TEXT_LENGTH),
156
166
  Schema.makeFilter(
157
167
  (value) => {
158
168
  try {
159
169
  const url = new URL(value);
170
+
160
171
  return (
161
172
  url.protocol === "https:" &&
162
173
  url.host === "live.browser.run" &&
@@ -173,11 +184,14 @@ const LiveViewUrl = Schema.String.check(
173
184
  { title: "a Cloudflare Live View HTTPS URL" },
174
185
  ),
175
186
  );
187
+
176
188
  const LiveViewObservation = Schema.Struct({ devtoolsFrontendUrl: LiveViewUrl });
177
189
  const HandoffObservation = Schema.Struct({ handoffId: BoundedHostText });
190
+
178
191
  const HandoffDuration = Schema.Natural.check(
179
192
  Schema.isLessThanOrEqualTo(MAX_HANDOFF_TIMEOUT_MILLIS),
180
193
  );
194
+
181
195
  const HandoffStateObservation = Schema.Union([
182
196
  Schema.Struct({
183
197
  active: Schema.Literal(true),
@@ -353,8 +367,10 @@ export class BrowserRunInteractiveBinding extends Context.Service<
353
367
  return Layer.effect(BrowserRunInteractiveBinding)(
354
368
  Effect.gen(function* () {
355
369
  const lifecycle = yield* BrowserRunSessionLifecycle;
370
+
356
371
  const viewport =
357
372
  options.viewport === undefined ? undefined : yield* decodeViewport(options.viewport);
373
+
358
374
  return {
359
375
  launch: async (keepAliveMillis: number) =>
360
376
  makeProductionBrowser(
@@ -418,6 +434,7 @@ const makeProductionRequest = (request: HTTPRequest): BrowserRunInteractiveReque
418
434
  const makeProductionCdpSession = (session: CDPSession): BrowserRunInteractiveCdpSession => ({
419
435
  send: async (command, parameters) => {
420
436
  const send = Reflect.get(session, "send");
437
+
421
438
  return await Reflect.apply(send, session, [command, parameters]);
422
439
  },
423
440
  detach: () => session.detach(),
@@ -436,6 +453,7 @@ const readActionTarget = (page: Page, selector: string): Promise<unknown> =>
436
453
  page.evaluate((requestedSelector) => {
437
454
  const pageDocument = Reflect.get(globalThis, "document");
438
455
  let matches;
456
+
439
457
  try {
440
458
  matches = Reflect.apply(Reflect.get(pageDocument, "querySelectorAll"), pageDocument, [
441
459
  requestedSelector,
@@ -449,14 +467,17 @@ const readActionTarget = (page: Page, selector: string): Promise<unknown> =>
449
467
  if (typeof matches !== "object" || matches === null) throw new Error("Invalid query result");
450
468
  const matchCount = Math.min(10_000, Reflect.get(matches, "length"));
451
469
  const element = Reflect.get(matches, 0);
470
+
452
471
  if (element === undefined) return { matchCount };
453
472
 
454
473
  const associated = Reflect.get(element, "control") ?? element;
455
474
  const tagName = String(Reflect.get(associated, "tagName") ?? "").toLowerCase();
456
475
  const inputType = String(Reflect.get(associated, "type") ?? "").toLowerCase();
476
+
457
477
  const role = String(
458
478
  Reflect.apply(Reflect.get(element, "getAttribute"), element, ["role"]) ?? "",
459
479
  ).toLowerCase();
480
+
460
481
  const kind =
461
482
  tagName === "button" || role === "button"
462
483
  ? "button"
@@ -471,23 +492,30 @@ const readActionTarget = (page: Page, selector: string): Promise<unknown> =>
471
492
  : tagName === "a"
472
493
  ? "link"
473
494
  : "other";
495
+
474
496
  const checked = Reflect.get(associated, "checked");
497
+
475
498
  const selected =
476
499
  tagName === "select"
477
500
  ? Reflect.get(associated, "selectedIndex") >= 0
478
501
  : Reflect.get(associated, "selected");
502
+
479
503
  const disabled = Reflect.get(associated, "disabled");
480
504
  const required = Reflect.get(associated, "required");
481
505
  const validity = Reflect.get(associated, "validity");
482
506
  const form = Reflect.get(associated, "form");
507
+
483
508
  const formMatches =
484
509
  form === null || form === undefined ? undefined : Reflect.get(form, "matches");
510
+
485
511
  const ariaChecked = Reflect.apply(Reflect.get(element, "getAttribute"), element, [
486
512
  "aria-checked",
487
513
  ]);
514
+
488
515
  const ariaDisabled = Reflect.apply(Reflect.get(element, "getAttribute"), element, [
489
516
  "aria-disabled",
490
517
  ]);
518
+
491
519
  const ariaSelected = Reflect.apply(Reflect.get(element, "getAttribute"), element, [
492
520
  "aria-selected",
493
521
  ]);
@@ -523,6 +551,7 @@ const boundedBestEffort = async <A>(
523
551
  millis: number,
524
552
  ): Promise<A | undefined> => {
525
553
  let timer: ReturnType<typeof setTimeout> | number | undefined;
554
+
526
555
  try {
527
556
  return await Promise.race([
528
557
  promise.catch(() => undefined),
@@ -556,17 +585,21 @@ const makeActionRequestTracker = (page: Page, signal: AbortSignal) => {
556
585
 
557
586
  const relevant = (request: HTTPRequest) => {
558
587
  const resourceType = request.resourceType();
588
+
559
589
  return resourceType === "fetch" || resourceType === "xhr";
560
590
  };
591
+
561
592
  const onRequest = (request: HTTPRequest) => {
562
593
  if (!relevant(request)) return;
563
594
  pending.add(request);
564
595
  total++;
565
596
  lastChange = performance.now();
566
597
  };
598
+
567
599
  const onFinished = (request: HTTPRequest) => {
568
600
  if (!pending.delete(request)) return;
569
601
  let status: number | undefined;
602
+
570
603
  try {
571
604
  status = request.response()?.status();
572
605
  } catch {
@@ -580,11 +613,13 @@ const makeActionRequestTracker = (page: Page, signal: AbortSignal) => {
580
613
  }
581
614
  lastChange = performance.now();
582
615
  };
616
+
583
617
  const onFailed = (request: HTTPRequest) => {
584
618
  if (!pending.delete(request)) return;
585
619
  failed++;
586
620
  lastChange = performance.now();
587
621
  };
622
+
588
623
  const close = () => {
589
624
  if (closed) return;
590
625
  closed = true;
@@ -610,8 +645,10 @@ const makeActionRequestTracker = (page: Page, signal: AbortSignal) => {
610
645
  const wait = async () => {
611
646
  const startedAt = performance.now();
612
647
  let settleTimedOut = false;
648
+
613
649
  while (!closed) {
614
650
  const now = performance.now();
651
+
615
652
  if (pending.size === 0 && now - lastChange >= ACTION_NETWORK_QUIET_MILLIS) break;
616
653
  if (now - startedAt >= ACTION_NETWORK_SETTLE_MILLIS) {
617
654
  settleTimedOut = true;
@@ -623,6 +660,7 @@ const makeActionRequestTracker = (page: Page, signal: AbortSignal) => {
623
660
  });
624
661
  wake = undefined;
625
662
  }
663
+
626
664
  return {
627
665
  total,
628
666
  status2xx,
@@ -659,13 +697,16 @@ const runObservedPageAction = async (
659
697
  action: (element: NonNullable<Awaited<ReturnType<Page["$"]>>>) => Promise<void>,
660
698
  ): Promise<unknown> => {
661
699
  if (signal.aborted) throw new BrowserRunActionUndispatched(0);
700
+
662
701
  const before = Schema.decodeUnknownSync(ActionTargetState)(
663
702
  await readActionTarget(page, selector),
664
703
  );
704
+
665
705
  if (before.matchCount !== 1 || before.invalidSelector === true) {
666
706
  throw new BrowserRunActionUndispatched(before.matchCount);
667
707
  }
668
708
  const matches = await page.$$(selector);
709
+
669
710
  if (matches.length !== 1 || matches[0] === undefined) {
670
711
  await disposeActionHandles(matches);
671
712
  throw new BrowserRunActionUndispatched(Math.min(10_000, matches.length));
@@ -678,9 +719,11 @@ const runObservedPageAction = async (
678
719
  let tracker: ReturnType<typeof makeActionRequestTracker> | undefined;
679
720
  let disposing: Promise<void> | undefined;
680
721
  const dispose = () => (disposing ??= disposeActionHandles(matches));
722
+
681
723
  const onAbort = () => {
682
724
  void dispose();
683
725
  };
726
+
684
727
  signal.addEventListener("abort", onAbort, { once: true });
685
728
  try {
686
729
  tracker = makeActionRequestTracker(page, signal);
@@ -692,6 +735,7 @@ const runObservedPageAction = async (
692
735
  tracker.markActionSettled();
693
736
  const network = await tracker.wait();
694
737
  const after = signal.aborted ? undefined : await readActionTargetAfter(page, selector);
738
+
695
739
  return {
696
740
  before,
697
741
  ...(after === undefined ? {} : { after }),
@@ -707,17 +751,20 @@ const runObservedPageAction = async (
707
751
 
708
752
  const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
709
753
  const listeners = new Map<BrowserRunInteractiveRequestListener, (request: HTTPRequest) => void>();
754
+
710
755
  return {
711
756
  close: () => page.close(),
712
757
  setBypassServiceWorker: (enabled) => page.setBypassServiceWorker(enabled),
713
758
  setRequestInterception: (enabled) => page.setRequestInterception(enabled),
714
759
  onRequest: (listener) => {
715
760
  const sdkListener = (request: HTTPRequest) => listener(makeProductionRequest(request));
761
+
716
762
  listeners.set(listener, sdkListener);
717
763
  page.on("request", sdkListener);
718
764
  },
719
765
  offRequest: (listener) => {
720
766
  const sdkListener = listeners.get(listener);
767
+
721
768
  if (sdkListener !== undefined) {
722
769
  page.off("request", sdkListener);
723
770
  listeners.delete(listener);
@@ -732,48 +779,60 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
732
779
  await page.evaluate(
733
780
  (requestedSelector, maximum, maximumControls) => {
734
781
  const pageDocument = Reflect.get(globalThis, "document");
782
+
735
783
  const matches =
736
784
  requestedSelector === undefined
737
785
  ? undefined
738
786
  : Reflect.apply(Reflect.get(pageDocument, "querySelectorAll"), pageDocument, [
739
787
  requestedSelector,
740
788
  ]);
789
+
741
790
  const selectorMatchCount =
742
791
  matches === undefined || matches === null
743
792
  ? 1
744
793
  : Math.min(10_000, Reflect.get(matches, "length"));
794
+
745
795
  const element =
746
796
  matches === undefined || matches === null
747
797
  ? Reflect.get(pageDocument, "body")
748
798
  : Reflect.get(matches, 0);
799
+
749
800
  if (element === null) return { _tag: "MissingElement" };
750
801
  if (element === undefined) return { _tag: "MissingElement" };
751
802
  const innerText = Reflect.get(element, "innerText");
752
803
  const textContent = Reflect.get(element, "textContent");
804
+
753
805
  const pageText =
754
806
  typeof innerText === "string"
755
807
  ? innerText
756
808
  : typeof textContent === "string"
757
809
  ? textContent
758
810
  : "";
811
+
759
812
  const primaryControlSelector =
760
813
  'input,select,textarea,label,button,[role="checkbox"],[role="radio"],[role="option"],[role="switch"],[role="tab"],[role="button"]';
814
+
761
815
  const optionSelector = "select option";
762
816
  const secondaryControlSelector = "a[href]";
763
817
  const controlSelector = `${primaryControlSelector},${optionSelector},${secondaryControlSelector}`;
818
+
764
819
  const selectorFor = (candidate: object) => {
765
820
  const parts: Array<string> = [];
766
821
  let current: object | null = candidate;
822
+
767
823
  while (current !== null && current !== undefined) {
768
824
  const tagName = String(Reflect.get(current, "tagName") ?? "").toLowerCase();
825
+
769
826
  if (tagName === "") break;
770
827
  const parent: object | null = Reflect.get(current, "parentElement");
828
+
771
829
  if (parent === null) {
772
830
  parts.push(tagName);
773
831
  break;
774
832
  }
775
833
  let sibling = Reflect.get(current, "previousElementSibling");
776
834
  let index = 1;
835
+
777
836
  while (sibling !== null && sibling !== undefined) {
778
837
  if (String(Reflect.get(sibling, "tagName") ?? "").toLowerCase() === tagName)
779
838
  index++;
@@ -782,14 +841,19 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
782
841
  parts.push(`${tagName}:nth-of-type(${index})`);
783
842
  current = parent;
784
843
  }
844
+
785
845
  return parts.reverse().join(" > ");
786
846
  };
847
+
787
848
  const visible = (candidate: object) => {
788
849
  const hidden = Reflect.get(candidate, "hidden");
850
+
789
851
  const ariaHidden = Reflect.apply(Reflect.get(candidate, "getAttribute"), candidate, [
790
852
  "aria-hidden",
791
853
  ]);
854
+
792
855
  const rects = Reflect.apply(Reflect.get(candidate, "getClientRects"), candidate, []);
856
+
793
857
  return (
794
858
  hidden !== true &&
795
859
  ariaHidden !== "true" &&
@@ -798,17 +862,22 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
798
862
  Reflect.get(rects, "length") > 0
799
863
  );
800
864
  };
865
+
801
866
  const candidates: Array<object> = [];
802
867
  let controlsTruncated = false;
868
+
803
869
  const consider = (candidate: object) => {
804
870
  const tagName = String(Reflect.get(candidate, "tagName") ?? "").toLowerCase();
871
+
805
872
  // Collapsed native options have no client rects. Their owning
806
873
  // select determines visibility; observe text/selection, never value.
807
874
  const visibilityTarget =
808
875
  tagName === "option"
809
876
  ? Reflect.apply(Reflect.get(candidate, "closest"), candidate, ["select"])
810
877
  : candidate;
878
+
811
879
  const actionable = tagName !== "label" || Reflect.get(candidate, "control") !== null;
880
+
812
881
  if (
813
882
  !actionable ||
814
883
  typeof visibilityTarget !== "object" ||
@@ -819,47 +888,60 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
819
888
  candidates.push(candidate);
820
889
  if (candidates.length > maximumControls) controlsTruncated = true;
821
890
  };
891
+
822
892
  const elementMatches = Reflect.get(element, "matches");
893
+
823
894
  if (
824
895
  typeof elementMatches === "function" &&
825
896
  Reflect.apply(elementMatches, element, [controlSelector])
826
897
  ) {
827
898
  consider(element);
828
899
  }
900
+
829
901
  const considerSelector = (candidateSelector: string) => {
830
902
  const descendants = Reflect.apply(Reflect.get(element, "querySelectorAll"), element, [
831
903
  candidateSelector,
832
904
  ]);
905
+
833
906
  if (typeof descendants !== "object" || descendants === null) return;
834
907
  const descendantCount = Reflect.get(descendants, "length");
908
+
835
909
  for (let index = 0; index < descendantCount && !controlsTruncated; index++) {
836
910
  consider(Reflect.get(descendants, index));
837
911
  }
838
912
  };
913
+
839
914
  considerSelector(primaryControlSelector);
840
915
  if (!controlsTruncated) considerSelector(optionSelector);
841
916
  if (!controlsTruncated) considerSelector(secondaryControlSelector);
917
+
842
918
  const controls = candidates.slice(0, maximumControls).map((candidate) => {
843
919
  const associated = Reflect.get(candidate, "control") ?? candidate;
844
920
  const tagName = String(Reflect.get(candidate, "tagName") ?? "").toLowerCase();
845
921
  const inputType = String(Reflect.get(associated, "type") ?? "").toLowerCase();
922
+
846
923
  const role = String(
847
924
  Reflect.apply(Reflect.get(candidate, "getAttribute"), candidate, ["role"]) ?? "",
848
925
  ).toLowerCase();
926
+
849
927
  const ariaLabel = Reflect.apply(Reflect.get(candidate, "getAttribute"), candidate, [
850
928
  "aria-label",
851
929
  ]);
930
+
852
931
  const candidateText =
853
932
  tagName === "textarea" || tagName === "input" || tagName === "select"
854
933
  ? undefined
855
934
  : Reflect.get(candidate, tagName === "option" ? "label" : "innerText");
935
+
856
936
  const associatedLabels = Reflect.get(associated, "labels");
937
+
857
938
  const associatedLabel =
858
939
  associatedLabels !== undefined &&
859
940
  associatedLabels !== null &&
860
941
  Reflect.get(associatedLabels, "length") > 0
861
942
  ? Reflect.get(Reflect.get(associatedLabels, 0), "innerText")
862
943
  : undefined;
944
+
863
945
  const label = String(
864
946
  typeof ariaLabel === "string" && ariaLabel !== ""
865
947
  ? ariaLabel
@@ -870,25 +952,32 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
870
952
  .replace(/\s+/g, " ")
871
953
  .trim()
872
954
  .slice(0, 200);
955
+
873
956
  const checked = Reflect.get(associated, "checked");
957
+
874
958
  const selected =
875
959
  tagName === "select"
876
960
  ? Reflect.get(associated, "selectedIndex") >= 0
877
961
  : Reflect.get(associated, "selected");
962
+
878
963
  const disabled = Reflect.get(associated, "disabled");
879
964
  const required = Reflect.get(associated, "required");
880
965
  const validity = Reflect.get(associated, "validity");
881
966
  const form = Reflect.get(associated, "form");
967
+
882
968
  const formMatches =
883
969
  form === null || form === undefined ? undefined : Reflect.get(form, "matches");
970
+
884
971
  const ariaChecked = Reflect.apply(Reflect.get(candidate, "getAttribute"), candidate, [
885
972
  "aria-checked",
886
973
  ]);
974
+
887
975
  const ariaSelected = Reflect.apply(
888
976
  Reflect.get(candidate, "getAttribute"),
889
977
  candidate,
890
978
  ["aria-selected"],
891
979
  );
980
+
892
981
  const ariaDisabled = Reflect.apply(
893
982
  Reflect.get(candidate, "getAttribute"),
894
983
  candidate,
@@ -930,6 +1019,7 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
930
1019
  : {}),
931
1020
  };
932
1021
  });
1022
+
933
1023
  // JSON is the bounded wire representation of this existing text result.
934
1024
  // eslint-disable-next-line no-restricted-properties
935
1025
  const text = JSON.stringify({
@@ -938,7 +1028,9 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
938
1028
  controls,
939
1029
  controlsTruncated,
940
1030
  });
1031
+
941
1032
  const observed = new TextEncoder().encode(text).byteLength;
1033
+
942
1034
  return observed > maximum ? { _tag: "OverLimit", observed } : { _tag: "Text", text };
943
1035
  },
944
1036
  selector,
@@ -946,7 +1038,9 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
946
1038
  MAX_OBSERVED_CONTROLS,
947
1039
  ),
948
1040
  );
1041
+
949
1042
  if (observation._tag === "Text") Schema.decodeUnknownSync(PageObservation)(observation.text);
1043
+
950
1044
  return observation;
951
1045
  },
952
1046
  fill: (selector, value, signal, onDispatch) =>
@@ -955,8 +1049,10 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
955
1049
  // Bypass instance setters so React can detect the change when events fire.
956
1050
  let prototype = Reflect.getPrototypeOf(element);
957
1051
  let setValue: ((value: string) => void) | undefined;
1052
+
958
1053
  while (prototype !== null) {
959
1054
  const setter = Reflect.getOwnPropertyDescriptor(prototype, "value")?.set;
1055
+
960
1056
  if (typeof setter === "function") {
961
1057
  setValue = setter;
962
1058
  break;
@@ -967,9 +1063,11 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
967
1063
  throw new Error("The selector did not resolve to a fillable field");
968
1064
  }
969
1065
  const focus = Reflect.get(element, "focus");
1066
+
970
1067
  if (typeof focus === "function") Reflect.apply(focus, element, []);
971
1068
  Reflect.apply(setValue, element, [nextValue]);
972
1069
  const dispatchEvent = Reflect.get(element, "dispatchEvent");
1070
+
973
1071
  if (typeof dispatchEvent === "function") {
974
1072
  Reflect.apply(dispatchEvent, element, [new Event("input", { bubbles: true })]);
975
1073
  Reflect.apply(dispatchEvent, element, [new Event("change", { bubbles: true })]);
@@ -985,6 +1083,7 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
985
1083
  page.evaluate(
986
1084
  (x, y) => {
987
1085
  const scrollBy = Reflect.get(globalThis, "scrollBy");
1086
+
988
1087
  Reflect.apply(scrollBy, globalThis, [{ left: x, top: y, behavior: "instant" }]);
989
1088
  },
990
1089
  deltaX,
@@ -1056,6 +1155,7 @@ const expiredError = (): InteractiveBrowserExpiredError =>
1056
1155
 
1057
1156
  const causeText = (cause: unknown): string => {
1058
1157
  if (cause instanceof Error) return cause.message.slice(0, 8_000);
1158
+
1059
1159
  return String(cause).slice(0, 8_000);
1060
1160
  };
1061
1161
 
@@ -1076,6 +1176,7 @@ const snapshotPolicy = Effect.fn("BrowserRunInteractive.snapshotPolicy")(functio
1076
1176
  const decoded = yield* Schema.decodeUnknownEffect(InteractiveBrowserPolicy)(input).pipe(
1077
1177
  Effect.mapError(() => policyError("The interactive browser policy is malformed")),
1078
1178
  );
1179
+
1079
1180
  if (decoded.network._tag === "PublicWeb") {
1080
1181
  return yield* InteractiveBrowserUnsupportedError.make({
1081
1182
  implementation: browserRunInteractiveImplementation,
@@ -1084,6 +1185,7 @@ const snapshotPolicy = Effect.fn("BrowserRunInteractive.snapshotPolicy")(functio
1084
1185
  "Cloudflare Browser Run cannot enforce the PublicWeb network policy for all session traffic",
1085
1186
  });
1086
1187
  }
1188
+
1087
1189
  return Object.freeze({
1088
1190
  network:
1089
1191
  decoded.network._tag === "ExactHosts"
@@ -1102,6 +1204,7 @@ const hostAllowed = (policy: InteractiveBrowserPolicySnapshot, value: string): b
1102
1204
  if (policy.network._tag === "Unrestricted") return true;
1103
1205
  try {
1104
1206
  const url = new URL(value);
1207
+
1105
1208
  return (
1106
1209
  url.protocol === "https:" &&
1107
1210
  url.username === "" &&
@@ -1122,6 +1225,7 @@ const closeLateAcquisition = async <A>(
1122
1225
  close: (acquired: A) => Promise<void>,
1123
1226
  ): Promise<A> => {
1124
1227
  const acquired = await acquire();
1228
+
1125
1229
  if (!signal.aborted) return acquired;
1126
1230
 
1127
1231
  // The SDK does not accept AbortSignal. If an acquisition settles after Effect
@@ -1152,6 +1256,7 @@ const deadlineError = Effect.fn("BrowserRunInteractive.deadlineError")(function*
1152
1256
  startedAt: number,
1153
1257
  ) {
1154
1258
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
1259
+
1155
1260
  return yield* InteractiveBrowserLimitError.make({
1156
1261
  implementation: browserRunInteractiveImplementation,
1157
1262
  limit: "elapsed",
@@ -1170,7 +1275,9 @@ const withinDeadline = Effect.fn("BrowserRunInteractive.withinDeadline")(functio
1170
1275
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
1171
1276
  const elapsed = Math.max(0, now - startedAt);
1172
1277
  const remaining = policy.maxElapsedMillis - elapsed;
1278
+
1173
1279
  if (remaining <= 0) return yield* deadlineError(policy, startedAt);
1280
+
1174
1281
  return yield* effect.pipe(
1175
1282
  Effect.timeoutOrElse({
1176
1283
  duration: Duration.millis(remaining),
@@ -1193,6 +1300,7 @@ const stateFailure = (state: HandleState): BrowserFailure | undefined => {
1193
1300
  if (state.closed.value || state.disconnected.value || state.uncertain.value) {
1194
1301
  return expiredError();
1195
1302
  }
1303
+
1196
1304
  return undefined;
1197
1305
  };
1198
1306
 
@@ -1226,6 +1334,7 @@ class BrowserRunRemoteFailure {
1226
1334
  const awaitPendingRequests = (state: HandleState): Effect.Effect<void> =>
1227
1335
  Effect.suspend(() => {
1228
1336
  const pending = [...state.pendingRequests];
1337
+
1229
1338
  return pending.length === 0
1230
1339
  ? Effect.void
1231
1340
  : Effect.promise(() => Promise.all(pending)).pipe(
@@ -1242,6 +1351,7 @@ const decodeNavigationResult = Effect.fn("BrowserRunInteractive.decodeNavigation
1242
1351
  try: page.url,
1243
1352
  catch: (cause) => protocolError("Reading the browser navigation URL failed", cause),
1244
1353
  });
1354
+
1245
1355
  const result = yield* Schema.decodeUnknownEffect(BrowserNavigationResult)({
1246
1356
  url,
1247
1357
  }).pipe(
@@ -1249,9 +1359,11 @@ const decodeNavigationResult = Effect.fn("BrowserRunInteractive.decodeNavigation
1249
1359
  protocolError("The browser returned a malformed navigation URL", cause),
1250
1360
  ),
1251
1361
  );
1362
+
1252
1363
  if (!hostAllowed(policy, result.url)) {
1253
1364
  return yield* policyError("The browser returned an off-policy page URL");
1254
1365
  }
1366
+
1255
1367
  return result;
1256
1368
  });
1257
1369
 
@@ -1263,12 +1375,15 @@ const decodeActionResult = Effect.fn("BrowserRunInteractive.decodeActionResult")
1263
1375
  try: page.url,
1264
1376
  catch: (cause) => protocolError("Reading the browser page URL failed", cause),
1265
1377
  });
1378
+
1266
1379
  const result = yield* Schema.decodeUnknownEffect(BrowserActionResult)({ url }).pipe(
1267
1380
  Effect.mapError((cause) => protocolError("The browser returned a malformed page URL", cause)),
1268
1381
  );
1382
+
1269
1383
  if (!hostAllowed(policy, result.url)) {
1270
1384
  return yield* policyError("The browser returned an off-policy page URL");
1271
1385
  }
1386
+
1272
1387
  return result;
1273
1388
  });
1274
1389
 
@@ -1279,6 +1394,7 @@ const makeRequestListener =
1279
1394
  ): BrowserRunInteractiveRequestListener =>
1280
1395
  (request) => {
1281
1396
  let allowed = false;
1397
+
1282
1398
  try {
1283
1399
  allowed = hostAllowed(policy, request.url());
1284
1400
  } catch {
@@ -1289,12 +1405,15 @@ const makeRequestListener =
1289
1405
  }
1290
1406
 
1291
1407
  let settlement: Promise<void>;
1408
+
1292
1409
  try {
1293
1410
  settlement = allowed ? request.continue() : request.abort();
1294
1411
  } catch {
1295
1412
  state.violation.value = protocolError("Resolving an intercepted browser request failed");
1413
+
1296
1414
  return;
1297
1415
  }
1416
+
1298
1417
  const observed = settlement
1299
1418
  .catch(() => {
1300
1419
  state.violation.value = protocolError("Resolving an intercepted browser request failed");
@@ -1302,6 +1421,7 @@ const makeRequestListener =
1302
1421
  .finally(() => {
1303
1422
  state.pendingRequests.delete(observed);
1304
1423
  });
1424
+
1305
1425
  state.pendingRequests.add(observed);
1306
1426
  };
1307
1427
 
@@ -1325,6 +1445,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1325
1445
  failure,
1326
1446
  ): Effect.Effect<never, InteractiveBrowserActionError | InteractiveBrowserExpiredError> => {
1327
1447
  const cause = failure.cause;
1448
+
1328
1449
  if (cause instanceof BrowserRunActionUndispatched) {
1329
1450
  return Effect.logInfo("Browser interactive action was not dispatched").pipe(
1330
1451
  Effect.annotateLogs({
@@ -1336,8 +1457,10 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1336
1457
  }
1337
1458
  if (state.disconnected.value || isRemoteClosure(cause)) {
1338
1459
  state.disconnected.value = true;
1460
+
1339
1461
  return Effect.fail(expiredError());
1340
1462
  }
1463
+
1341
1464
  return Effect.fail(actionError(operation, cause));
1342
1465
  },
1343
1466
  ),
@@ -1350,10 +1473,12 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1350
1473
  Effect.suspend(() => {
1351
1474
  let dispatched = false;
1352
1475
  let pending: Promise<unknown> | undefined;
1476
+
1353
1477
  return remote(operation, (signal) => {
1354
1478
  pending = evaluate(signal, () => {
1355
1479
  dispatched = true;
1356
1480
  });
1481
+
1357
1482
  return pending;
1358
1483
  }).pipe(
1359
1484
  Effect.onInterrupt(() =>
@@ -1369,6 +1494,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1369
1494
  }),
1370
1495
  );
1371
1496
  const completion = pending;
1497
+
1372
1498
  if (completion !== undefined) {
1373
1499
  yield* Effect.promise(() => boundedBestEffort(completion, 500));
1374
1500
  }
@@ -1455,6 +1581,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1455
1581
  .withPermitsIfAvailable(1)(
1456
1582
  Effect.gen(function* () {
1457
1583
  const unavailable = stateFailure(state);
1584
+
1458
1585
  if (unavailable !== undefined) return yield* unavailable;
1459
1586
  yield* preflight;
1460
1587
 
@@ -1464,6 +1591,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1464
1591
  ? [{ allowed: false, observed: count + 1 }, count]
1465
1592
  : [{ allowed: true, observed: count + 1 }, count + 1],
1466
1593
  );
1594
+
1467
1595
  if (!admitted.allowed) {
1468
1596
  return yield* InteractiveBrowserLimitError.make({
1469
1597
  implementation: browserRunInteractiveImplementation,
@@ -1478,17 +1606,20 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1478
1606
  const completed = effect.pipe(
1479
1607
  Effect.catch((error) => {
1480
1608
  const failure = stateFailure(state);
1609
+
1481
1610
  return Effect.fail(failure ?? error);
1482
1611
  }),
1483
1612
  Effect.flatMap((result) =>
1484
1613
  awaitPendingRequests(state).pipe(
1485
1614
  Effect.flatMap(() => {
1486
1615
  const failure = stateFailure(state);
1616
+
1487
1617
  return failure === undefined ? Effect.succeed(result) : Effect.fail(failure);
1488
1618
  }),
1489
1619
  ),
1490
1620
  ),
1491
1621
  );
1622
+
1492
1623
  return yield* withinDeadline(completed, policy, startedAt, () => {
1493
1624
  state.uncertain.value = true;
1494
1625
  }).pipe(
@@ -1502,12 +1633,14 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1502
1633
  return Effect.fail(error);
1503
1634
  }
1504
1635
  const failure = stateFailure(state);
1636
+
1505
1637
  if (
1506
1638
  Schema.is(InteractiveBrowserActionError)(error) &&
1507
1639
  !isBrowserRunUndispatchedActionError(error)
1508
1640
  ) {
1509
1641
  state.uncertain.value = true;
1510
1642
  }
1643
+
1511
1644
  return Effect.fail(failure ?? error);
1512
1645
  }),
1513
1646
  );
@@ -1531,6 +1664,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1531
1664
  run(
1532
1665
  Effect.gen(function* () {
1533
1666
  yield* remote("navigate", () => page.goto(request.url));
1667
+
1534
1668
  return yield* decodeNavigationResult(page, policy);
1535
1669
  }),
1536
1670
  Effect.suspend(() =>
@@ -1545,11 +1679,13 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1545
1679
  const raw = yield* remote("read-text", () =>
1546
1680
  page.readText(request.selector, policy.maxReturnedBytes),
1547
1681
  );
1682
+
1548
1683
  const observation = yield* Schema.decodeUnknownEffect(TextObservation)(raw).pipe(
1549
1684
  Effect.mapError((cause) =>
1550
1685
  protocolError("The browser returned a malformed text observation", cause),
1551
1686
  ),
1552
1687
  );
1688
+
1553
1689
  if (observation._tag === "MissingElement") {
1554
1690
  return yield* actionError("read-text");
1555
1691
  }
@@ -1563,6 +1699,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1563
1699
  });
1564
1700
  }
1565
1701
  const observed = new TextEncoder().encode(observation.text).byteLength;
1702
+
1566
1703
  if (observed > policy.maxReturnedBytes) {
1567
1704
  return yield* InteractiveBrowserLimitError.make({
1568
1705
  implementation: browserRunInteractiveImplementation,
@@ -1572,6 +1709,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1572
1709
  message: "The browser returned-text limit was reached",
1573
1710
  });
1574
1711
  }
1712
+
1575
1713
  return yield* Schema.decodeUnknownEffect(BrowserTextResult)({
1576
1714
  text: observation.text,
1577
1715
  }).pipe(
@@ -1587,7 +1725,9 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1587
1725
  const observation = yield* observedAction("fill", (signal, onDispatch) =>
1588
1726
  page.fill(request.selector, request.value, signal, onDispatch),
1589
1727
  ).pipe(Effect.flatMap(decodeActionObservation));
1728
+
1590
1729
  yield* logActionObservation("fill", observation);
1730
+
1591
1731
  return yield* decodeActionResult(page, policy);
1592
1732
  }),
1593
1733
  ),
@@ -1597,7 +1737,9 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1597
1737
  const observation = yield* observedAction("click", (signal, onDispatch) =>
1598
1738
  page.click(request.selector, signal, onDispatch),
1599
1739
  ).pipe(Effect.flatMap(decodeActionObservation));
1740
+
1600
1741
  yield* logActionObservation("click", observation);
1742
+
1601
1743
  return yield* decodeActionResult(page, policy);
1602
1744
  }),
1603
1745
  ),
@@ -1608,11 +1750,13 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1608
1750
  run(
1609
1751
  Effect.gen(function* () {
1610
1752
  const raw = yield* remote("screenshot", () => page.screenshot(decoded.fullPage));
1753
+
1611
1754
  const bytes = yield* Schema.decodeUnknownEffect(PngBytes)(raw).pipe(
1612
1755
  Effect.mapError(() =>
1613
1756
  protocolError("The browser returned a malformed PNG screenshot"),
1614
1757
  ),
1615
1758
  );
1759
+
1616
1760
  if (bytes.length > policy.maxReturnedBytes) {
1617
1761
  return yield* InteractiveBrowserLimitError.make({
1618
1762
  implementation: browserRunInteractiveImplementation,
@@ -1622,6 +1766,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1622
1766
  message: "The browser screenshot byte limit was reached",
1623
1767
  });
1624
1768
  }
1769
+
1625
1770
  return yield* Schema.decodeUnknownEffect(PageScreenshotResult)({
1626
1771
  implementation: browserRunInteractiveImplementation,
1627
1772
  mediaType: "image/png",
@@ -1650,6 +1795,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
1650
1795
  ),
1651
1796
  close,
1652
1797
  };
1798
+
1653
1799
  return { handle, run };
1654
1800
  });
1655
1801
 
@@ -1658,6 +1804,7 @@ const remainingMillis = Effect.fn("BrowserRunInteractive.remainingMillis")(funct
1658
1804
  startedAt: number,
1659
1805
  ) {
1660
1806
  const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
1807
+
1661
1808
  return Math.max(0, policy.maxElapsedMillis - Math.max(0, now - startedAt));
1662
1809
  });
1663
1810
 
@@ -1720,6 +1867,7 @@ const cdpCommand = <A>(
1720
1867
  ),
1721
1868
  { interruptible: true },
1722
1869
  );
1870
+
1723
1871
  const raw = yield* Effect.tryPromise({
1724
1872
  try: () => cdp.send(command, parameters),
1725
1873
  catch: (cause) =>
@@ -1727,6 +1875,7 @@ const cdpCommand = <A>(
1727
1875
  ? expiredError()
1728
1876
  : protocolError("The Cloudflare browser control command failed", cause),
1729
1877
  });
1878
+
1730
1879
  return yield* Schema.decodeUnknownEffect(output)(raw).pipe(
1731
1880
  Effect.mapError(() => protocolError(malformedMessage)),
1732
1881
  );
@@ -1737,11 +1886,13 @@ const makeHostService = (
1737
1886
  binding: BrowserRunInteractiveBinding["Service"],
1738
1887
  ): BrowserRunInteractiveHost["Service"] => {
1739
1888
  const closeSession = binding.closeSession;
1889
+
1740
1890
  const terminate = Effect.fn("BrowserRunInteractiveHost.terminate")(function* (
1741
1891
  sessionId: Redacted.Redacted<string>,
1742
1892
  entries: ReadonlyArray<CloseEntry>,
1743
1893
  ) {
1744
1894
  const deadline = (yield* Clock.currentTimeMillis) + 10_000;
1895
+
1745
1896
  yield* closeSession(sessionId).pipe(
1746
1897
  Effect.interruptible,
1747
1898
  Effect.timeoutOrElse({
@@ -1750,6 +1901,7 @@ const makeHostService = (
1750
1901
  }),
1751
1902
  );
1752
1903
  const remaining = deadline - (yield* Clock.currentTimeMillis);
1904
+
1753
1905
  if (remaining <= 0)
1754
1906
  return yield* Effect.logWarning(
1755
1907
  "Local browser teardown skipped after confirmed termination deadline",
@@ -1768,6 +1920,7 @@ const makeHostService = (
1768
1920
  ),
1769
1921
  );
1770
1922
  });
1923
+
1771
1924
  const closeAcquired = Effect.fn("BrowserRunInteractiveHost.closeAcquired")(function* (
1772
1925
  browser: BrowserRunInteractiveBrowser,
1773
1926
  ) {
@@ -1781,15 +1934,18 @@ const makeHostService = (
1781
1934
  closeWithWarning(browser.close, "Closing an unidentified browser failed"),
1782
1935
  ),
1783
1936
  );
1937
+
1784
1938
  yield* terminate(Redacted.make(sessionId), [
1785
1939
  closeEntry(browser.close, "Closing the local browser connection failed"),
1786
1940
  ]);
1787
1941
  });
1942
+
1788
1943
  const open = Effect.fn("BrowserRunInteractiveHost.open")(function* (
1789
1944
  policy: InteractiveBrowserPolicy,
1790
1945
  ): Effect.fn.Return<BrowserRunInteractiveSession, InteractiveBrowserError, Scope.Scope> {
1791
1946
  const fixedPolicy = yield* snapshotPolicy(policy);
1792
1947
  const startedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
1948
+
1793
1949
  const state: HandleState = {
1794
1950
  closed: { value: false },
1795
1951
  disconnected: { value: false },
@@ -1797,10 +1953,13 @@ const makeHostService = (
1797
1953
  violation: { value: undefined },
1798
1954
  pendingRequests: new Set(),
1799
1955
  };
1956
+
1800
1957
  const lifecycle = {
1801
1958
  managedTeardownInstalled: false,
1802
1959
  };
1960
+
1803
1961
  const closers: Array<CloseEntry> = [];
1962
+
1804
1963
  const releaseBeforeManaged = (entry: CloseEntry): Effect.Effect<void> =>
1805
1964
  Effect.suspend(() =>
1806
1965
  lifecycle.managedTeardownInstalled
@@ -1830,6 +1989,7 @@ const makeHostService = (
1830
1989
  ),
1831
1990
  (acquired) => {
1832
1991
  state.disconnected.value = true;
1992
+
1833
1993
  return lifecycle.managedTeardownInstalled
1834
1994
  ? Effect.void
1835
1995
  : closeAcquired(acquired).pipe(
@@ -1838,11 +1998,13 @@ const makeHostService = (
1838
1998
  },
1839
1999
  { interruptible: true },
1840
2000
  );
2001
+
1841
2002
  closers.push(closeEntry(browser.close, "Closing the interactive browser failed"));
1842
2003
 
1843
2004
  const disconnected = () => {
1844
2005
  state.disconnected.value = true;
1845
2006
  };
2007
+
1846
2008
  yield* Effect.acquireRelease(
1847
2009
  Effect.try({
1848
2010
  try: () => browser.onDisconnected(disconnected),
@@ -1862,12 +2024,15 @@ const makeHostService = (
1862
2024
  "Removing the browser disconnect listener failed",
1863
2025
  ),
1864
2026
  );
2027
+
1865
2028
  const connected = yield* Effect.try({
1866
2029
  try: browser.isConnected,
1867
2030
  catch: (cause) => protocolError("Reading the Browser Run connection state failed", cause),
1868
2031
  });
2032
+
1869
2033
  if (!connected) {
1870
2034
  state.disconnected.value = true;
2035
+
1871
2036
  return yield* expiredError();
1872
2037
  }
1873
2038
 
@@ -1901,7 +2066,9 @@ const makeHostService = (
1901
2066
  ),
1902
2067
  { interruptible: true },
1903
2068
  );
2069
+
1904
2070
  closers.push(closeEntry(context.close, "Closing the interactive browser context failed"));
2071
+
1905
2072
  const page = yield* Effect.acquireRelease(
1906
2073
  withinDeadline(
1907
2074
  Effect.tryPromise({
@@ -1921,6 +2088,7 @@ const makeHostService = (
1921
2088
  ),
1922
2089
  { interruptible: true },
1923
2090
  );
2091
+
1924
2092
  closers.push(closeEntry(page.close, "Closing the interactive browser page failed"));
1925
2093
 
1926
2094
  yield* withinDeadline(
@@ -1933,6 +2101,7 @@ const makeHostService = (
1933
2101
  );
1934
2102
 
1935
2103
  const requestListener = makeRequestListener(fixedPolicy, state);
2104
+
1936
2105
  yield* Effect.acquireRelease(
1937
2106
  Effect.try({
1938
2107
  try: () => page.onRequest(requestListener),
@@ -1964,11 +2133,13 @@ const makeHostService = (
1964
2133
 
1965
2134
  yield* withinDeadline(awaitPendingRequests(state), fixedPolicy, startedAt);
1966
2135
  const setupFailure = stateFailure(state);
2136
+
1967
2137
  if (setupFailure !== undefined) return yield* setupFailure;
1968
2138
 
1969
2139
  const teardown = yield* Effect.uninterruptible(
1970
2140
  Effect.gen(function* () {
1971
2141
  const cached = yield* Effect.cached(terminate(Redacted.make(sessionIdValue), closers));
2142
+
1972
2143
  lifecycle.managedTeardownInstalled = true;
1973
2144
  yield* Effect.addFinalizer(() =>
1974
2145
  Effect.uninterruptible(
@@ -1981,6 +2152,7 @@ const makeHostService = (
1981
2152
  ),
1982
2153
  ),
1983
2154
  );
2155
+
1984
2156
  return cached;
1985
2157
  }),
1986
2158
  );
@@ -1994,6 +2166,7 @@ const makeHostService = (
1994
2166
 
1995
2167
  const runtime = yield* makeHandle(page, fixedPolicy, startedAt, state, close);
1996
2168
  const currentPagePreflight = decodeActionResult(page, fixedPolicy).pipe(Effect.asVoid);
2169
+
1997
2170
  const requestFitsSession = (requestedMillis: number): Effect.Effect<void, BrowserFailure> =>
1998
2171
  remainingMillis(fixedPolicy, startedAt).pipe(
1999
2172
  Effect.flatMap((remaining) =>
@@ -2017,8 +2190,10 @@ const makeHostService = (
2017
2190
  catch: (cause) => {
2018
2191
  if (state.disconnected.value || isRemoteClosure(cause)) {
2019
2192
  state.disconnected.value = true;
2193
+
2020
2194
  return expiredError();
2021
2195
  }
2196
+
2022
2197
  return protocolError("Resizing the browser viewport failed", cause);
2023
2198
  },
2024
2199
  }),
@@ -2143,6 +2318,7 @@ export const browserRunInteractiveLayer = (): Layer.Layer<
2143
2318
  Effect.gen(function* () {
2144
2319
  const binding = yield* BrowserRunInteractiveBinding;
2145
2320
  const host = makeHostService(binding);
2321
+
2146
2322
  return InteractiveBrowser.of({
2147
2323
  open: (policy) => host.open(policy).pipe(Effect.map((session) => session.handle)),
2148
2324
  });