effect-browser 0.2.0-beta.9 → 0.3.0-beta.11

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 (205) hide show
  1. package/README.md +861 -1161
  2. package/dist/Browser-B4qncxe7.mjs +628 -0
  3. package/dist/Browser-B4qncxe7.mjs.map +1 -0
  4. package/dist/Browser.d.mts +129 -3
  5. package/dist/Browser.mjs +2 -22
  6. package/dist/BrowserError-Coy7EW1N.d.mts +140 -0
  7. package/dist/BrowserError.d.mts +2 -0
  8. package/dist/BrowserError.mjs +211 -0
  9. package/dist/BrowserError.mjs.map +1 -0
  10. package/dist/BrowserEvent-BRu2juEz.d.mts +279 -0
  11. package/dist/BrowserEvent.d.mts +2 -0
  12. package/dist/BrowserEvent.mjs +297 -0
  13. package/dist/BrowserEvent.mjs.map +1 -0
  14. package/dist/Cdp.d.mts +32 -0
  15. package/dist/Cdp.mjs +61 -0
  16. package/dist/Cdp.mjs.map +1 -0
  17. package/dist/Change-DNvztNLu.mjs +138 -0
  18. package/dist/Change-DNvztNLu.mjs.map +1 -0
  19. package/dist/Change-iMlcuzVD.d.mts +71 -0
  20. package/dist/Change.d.mts +2 -0
  21. package/dist/Change.mjs +2 -0
  22. package/dist/Chromium.d.mts +29 -81
  23. package/dist/Chromium.mjs +57 -392
  24. package/dist/Chromium.mjs.map +1 -1
  25. package/dist/Expect.d.mts +62 -0
  26. package/dist/Expect.mjs +114 -0
  27. package/dist/Expect.mjs.map +1 -0
  28. package/dist/Frame-Di2PxyF7.d.mts +119 -0
  29. package/dist/Frame.d.mts +2 -0
  30. package/dist/Frame.mjs +125 -0
  31. package/dist/Frame.mjs.map +1 -0
  32. package/dist/Moment.d.mts +2 -0
  33. package/dist/Moment.mjs +288 -0
  34. package/dist/Moment.mjs.map +1 -0
  35. package/dist/Motion.d.mts +40 -0
  36. package/dist/Motion.mjs +122 -0
  37. package/dist/Motion.mjs.map +1 -0
  38. package/dist/Page-B5cjaUp5.mjs +618 -0
  39. package/dist/Page-B5cjaUp5.mjs.map +1 -0
  40. package/dist/Page-B7X6DQUn.d.mts +595 -0
  41. package/dist/Page.d.mts +2 -0
  42. package/dist/Page.mjs +2 -0
  43. package/dist/Plan-Akcac63g.d.mts +209 -0
  44. package/dist/Plan-D1wrJVs8.mjs +508 -0
  45. package/dist/Plan-D1wrJVs8.mjs.map +1 -0
  46. package/dist/Plan.d.mts +2 -3
  47. package/dist/Plan.mjs +2 -2
  48. package/dist/Policy.d.mts +76 -0
  49. package/dist/Policy.mjs +237 -0
  50. package/dist/Policy.mjs.map +1 -0
  51. package/dist/Presentation.d.mts +48 -0
  52. package/dist/Presentation.mjs +117 -0
  53. package/dist/Presentation.mjs.map +1 -0
  54. package/dist/Snapshot-B34hZ4Jl.d.mts +68 -0
  55. package/dist/Snapshot.d.mts +2 -0
  56. package/dist/Snapshot.mjs +79 -0
  57. package/dist/Snapshot.mjs.map +1 -0
  58. package/dist/Stage.d.mts +64 -0
  59. package/dist/Stage.mjs +237 -0
  60. package/dist/Stage.mjs.map +1 -0
  61. package/dist/Supervisor-CYWFK3vM.d.mts +280 -0
  62. package/dist/Supervisor-TNKNOVzu.mjs +599 -0
  63. package/dist/Supervisor-TNKNOVzu.mjs.map +1 -0
  64. package/dist/Supervisor.d.mts +2 -0
  65. package/dist/Supervisor.mjs +2 -0
  66. package/dist/context-B6xr8nVi.mjs +261 -0
  67. package/dist/context-B6xr8nVi.mjs.map +1 -0
  68. package/dist/index.d.mts +17 -14
  69. package/dist/index.mjs +18 -13
  70. package/dist/match.inpage-xOe9LNO0.mjs +48 -0
  71. package/dist/match.inpage-xOe9LNO0.mjs.map +1 -0
  72. package/dist/page-BAf__WKX.mjs +5075 -0
  73. package/dist/page-BAf__WKX.mjs.map +1 -0
  74. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  75. package/dist/url-Cd04Okid.mjs +264 -0
  76. package/dist/url-Cd04Okid.mjs.map +1 -0
  77. package/package.json +96 -61
  78. package/src/Browser.ts +672 -0
  79. package/src/BrowserError.ts +234 -0
  80. package/src/BrowserEvent.ts +296 -0
  81. package/src/Cdp.ts +101 -0
  82. package/src/Change.ts +88 -0
  83. package/src/Chromium.ts +102 -0
  84. package/src/Expect.ts +180 -0
  85. package/src/Frame.ts +132 -0
  86. package/src/Moment.ts +479 -0
  87. package/src/Motion.ts +166 -0
  88. package/src/Page.ts +522 -0
  89. package/src/Plan.ts +663 -0
  90. package/src/Policy.ts +325 -0
  91. package/src/Presentation.ts +195 -0
  92. package/src/Snapshot.ts +89 -0
  93. package/src/Stage.ts +467 -0
  94. package/src/Supervisor.ts +599 -0
  95. package/src/index.ts +20 -0
  96. package/src/internal/input/actions.ts +610 -0
  97. package/src/internal/input/dispatch.ts +161 -0
  98. package/src/internal/input/edit.inpage.ts +162 -0
  99. package/src/internal/input/evidence.inpage.ts +164 -0
  100. package/src/internal/input/gate.ts +224 -0
  101. package/src/internal/input/guard.inpage.ts +605 -0
  102. package/src/internal/input/guard.ts +184 -0
  103. package/src/internal/input/keyboard.ts +112 -0
  104. package/src/internal/input/keys.ts +127 -0
  105. package/src/internal/input/perform.ts +376 -0
  106. package/src/internal/input/pointer.ts +153 -0
  107. package/src/internal/input/replies.ts +195 -0
  108. package/src/internal/input/settle.ts +50 -0
  109. package/src/internal/input/style.ts +186 -0
  110. package/src/internal/input/targets.inpage.ts +293 -0
  111. package/src/internal/input/targets.ts +148 -0
  112. package/src/internal/page/bridge.ts +391 -0
  113. package/src/internal/page/context.ts +314 -0
  114. package/src/internal/page/identity.ts +35 -0
  115. package/src/internal/page/lane.ts +311 -0
  116. package/src/internal/page/navigation.ts +183 -0
  117. package/src/internal/page/openings.ts +87 -0
  118. package/src/internal/page/page.ts +165 -0
  119. package/src/internal/page/url.ts +100 -0
  120. package/src/internal/page/viewport.ts +43 -0
  121. package/src/internal/pictures/capture.ts +776 -0
  122. package/src/internal/pictures/clock.ts +141 -0
  123. package/src/internal/pictures/counts.ts +92 -0
  124. package/src/internal/pictures/pictures.ts +521 -0
  125. package/src/internal/pictures/transport.ts +243 -0
  126. package/src/internal/reading/choose.ts +100 -0
  127. package/src/internal/reading/context.inpage.ts +201 -0
  128. package/src/internal/reading/controls.inpage.ts +158 -0
  129. package/src/internal/reading/expect.inpage.ts +70 -0
  130. package/src/internal/reading/match.inpage.ts +92 -0
  131. package/src/internal/reading/names.inpage.ts +306 -0
  132. package/src/internal/reading/outline.inpage.ts +260 -0
  133. package/src/internal/reading/reading.ts +223 -0
  134. package/src/internal/reading/ready.inpage.ts +164 -0
  135. package/src/internal/reading/ready.ts +111 -0
  136. package/src/internal/reading/subjects.inpage.ts +188 -0
  137. package/src/internal/reading/text.inpage.ts +135 -0
  138. package/src/internal/reading/walk.inpage.ts +220 -0
  139. package/src/internal/supervisor/lifecycle.ts +416 -0
  140. package/src/internal/timeline/changes.inpage.ts +216 -0
  141. package/src/internal/timeline/changes.ts +100 -0
  142. package/src/internal/timeline/events.ts +94 -0
  143. package/src/internal/timeline/fold.inpage.ts +123 -0
  144. package/src/internal/timeline/history.inpage.ts +229 -0
  145. package/src/internal/timeline/marks.inpage.ts +207 -0
  146. package/src/internal/timeline/record.inpage.ts +133 -0
  147. package/src/internal/timeline/sight.inpage.ts +122 -0
  148. package/src/internal/timeline/window.ts +96 -0
  149. package/src/internal/timeline/words.ts +65 -0
  150. package/dist/Bootstrap-B9UUXsvF.d.mts +0 -183
  151. package/dist/Bootstrap-CeQOWBy6.mjs +0 -189
  152. package/dist/Bootstrap-CeQOWBy6.mjs.map +0 -1
  153. package/dist/Bootstrap.d.mts +0 -3
  154. package/dist/Bootstrap.mjs +0 -2
  155. package/dist/Browser-BF9_sORP.d.mts +0 -388
  156. package/dist/Browser.mjs.map +0 -1
  157. package/dist/BrowserData-CPIIIHol.d.mts +0 -585
  158. package/dist/BrowserData.d.mts +0 -3
  159. package/dist/BrowserData.mjs +0 -695
  160. package/dist/BrowserData.mjs.map +0 -1
  161. package/dist/BrowserRuntime-B6nT1G4X.d.mts +0 -148
  162. package/dist/BrowserRuntime-C1r2aSRK.mjs +0 -6820
  163. package/dist/BrowserRuntime-C1r2aSRK.mjs.map +0 -1
  164. package/dist/BrowserRuntime.d.mts +0 -3
  165. package/dist/BrowserRuntime.mjs +0 -3
  166. package/dist/Capture.d.mts +0 -56
  167. package/dist/Capture.mjs +0 -678
  168. package/dist/Capture.mjs.map +0 -1
  169. package/dist/CaptureData-CYriZHX1.mjs +0 -201
  170. package/dist/CaptureData-CYriZHX1.mjs.map +0 -1
  171. package/dist/CaptureData.d.mts +0 -219
  172. package/dist/CaptureData.mjs +0 -2
  173. package/dist/Errors.d.mts +0 -158
  174. package/dist/Errors.mjs +0 -207
  175. package/dist/Errors.mjs.map +0 -1
  176. package/dist/Images-yCG9BmRU.mjs +0 -76
  177. package/dist/Images-yCG9BmRU.mjs.map +0 -1
  178. package/dist/OperationOptions-DAeRS6O9.mjs +0 -55
  179. package/dist/OperationOptions-DAeRS6O9.mjs.map +0 -1
  180. package/dist/PageControl.d.mts +0 -15
  181. package/dist/PageControl.mjs +0 -34
  182. package/dist/PageControl.mjs.map +0 -1
  183. package/dist/PageRegistry-B3q9DS3g.mjs +0 -164
  184. package/dist/PageRegistry-B3q9DS3g.mjs.map +0 -1
  185. package/dist/Plan-Dyxu2q8C.mjs +0 -543
  186. package/dist/Plan-Dyxu2q8C.mjs.map +0 -1
  187. package/dist/PlanData-B668RxbQ.d.mts +0 -4831
  188. package/dist/PlanData.d.mts +0 -3
  189. package/dist/PlanData.mjs +0 -268
  190. package/dist/PlanData.mjs.map +0 -1
  191. package/dist/PlanInput-BJcPPncs.mjs +0 -75
  192. package/dist/PlanInput-BJcPPncs.mjs.map +0 -1
  193. package/dist/PublicSession-DCSMMqOj.mjs +0 -5602
  194. package/dist/PublicSession-DCSMMqOj.mjs.map +0 -1
  195. package/dist/Testing.d.mts +0 -479
  196. package/dist/Testing.mjs +0 -2147
  197. package/dist/Testing.mjs.map +0 -1
  198. package/dist/Timeline-fV22Xk-S.d.mts +0 -29
  199. package/dist/Timeline.d.mts +0 -4
  200. package/dist/Timeline.mjs +0 -20
  201. package/dist/Timeline.mjs.map +0 -1
  202. package/dist/TimelineData-BsOn-YGZ.d.mts +0 -3818
  203. package/dist/TimelineData.d.mts +0 -3
  204. package/dist/TimelineData.mjs +0 -482
  205. package/dist/TimelineData.mjs.map +0 -1
@@ -0,0 +1,224 @@
1
+ /**
2
+ * A browser's input authority: stop admitting writes, drain admitted operations through their
3
+ * records and native replies, then retain the stop for its scopes. Reads never enter this gate.
4
+ */
5
+ import { Clock, Deferred, Duration, Effect, Exit, Option, type Scope } from "effect";
6
+
7
+ import { BrowserError, Busy, Closed, InvalidRequest, Suspended } from "../../BrowserError.ts";
8
+ import type { DisconnectCause } from "../../BrowserEvent.ts";
9
+
10
+ interface Stopped {
11
+ readonly since: number;
12
+ readonly at: number;
13
+ }
14
+
15
+ type State =
16
+ | { readonly _tag: "Open"; readonly inputs: ReadonlySet<number> }
17
+ | {
18
+ readonly _tag: "Draining";
19
+ readonly inputs: ReadonlySet<number>;
20
+ readonly claims: ReadonlySet<number>;
21
+ readonly startedAt: number;
22
+ readonly deadline: number;
23
+ }
24
+ | { readonly _tag: "Held"; readonly claims: ReadonlySet<number>; readonly stopped: Stopped };
25
+
26
+ type Input =
27
+ | { readonly _tag: "Enter" | "End" | "Leave"; readonly id: number }
28
+ | { readonly _tag: "Suspend"; readonly id: number; readonly drain: number }
29
+ | { readonly _tag: "Expire" };
30
+
31
+ const without = (set: ReadonlySet<number>, id: number) =>
32
+ new Set([...set].filter((each) => each !== id));
33
+
34
+ const step = (state: State, input: Input, stopped: Stopped): State => {
35
+ switch (input._tag) {
36
+ case "Enter":
37
+ return state._tag === "Open"
38
+ ? { ...state, inputs: new Set([...state.inputs, input.id]) }
39
+ : state;
40
+ case "End": {
41
+ if (state._tag === "Held") return state;
42
+ const inputs = without(state.inputs, input.id);
43
+
44
+ return state._tag === "Draining" && inputs.size === 0
45
+ ? { _tag: "Held", claims: state.claims, stopped }
46
+ : { ...state, inputs };
47
+ }
48
+ case "Suspend":
49
+ if (state._tag !== "Open") return { ...state, claims: new Set([...state.claims, input.id]) };
50
+
51
+ return state.inputs.size === 0
52
+ ? { _tag: "Held", claims: new Set([input.id]), stopped }
53
+ : {
54
+ _tag: "Draining",
55
+ inputs: state.inputs,
56
+ claims: new Set([input.id]),
57
+ startedAt: stopped.at,
58
+ deadline: stopped.at + input.drain,
59
+ };
60
+ case "Leave": {
61
+ if (state._tag === "Open" || !state.claims.has(input.id)) return state;
62
+ const claims = without(state.claims, input.id);
63
+
64
+ return claims.size > 0
65
+ ? { ...state, claims }
66
+ : { _tag: "Open", inputs: state._tag === "Draining" ? state.inputs : new Set() };
67
+ }
68
+ case "Expire":
69
+ return state._tag === "Draining" && stopped.at >= state.deadline
70
+ ? { _tag: "Open", inputs: state.inputs }
71
+ : state;
72
+ }
73
+ };
74
+
75
+ export const make = (options: {
76
+ readonly now: () => number;
77
+ readonly sequence: () => number;
78
+ readonly closedBy: () => DisconnectCause | undefined;
79
+ readonly lost: Effect.Effect<DisconnectCause>;
80
+ readonly clock: Clock.Clock;
81
+ readonly drain: Duration.Duration;
82
+ }) => {
83
+ let state: State = { _tag: "Open", inputs: new Set() };
84
+ let next = 0;
85
+ let refused = Deferred.makeUnsafe<void>();
86
+ let ready = Deferred.makeUnsafe<Stopped, BrowserError>();
87
+
88
+ const error = (operation: string) =>
89
+ new BrowserError({
90
+ operation,
91
+ reason:
92
+ options.closedBy() === undefined
93
+ ? new Suspended()
94
+ : new Closed({ cause: options.closedBy() ?? "connection" }),
95
+ dispatched: false,
96
+ });
97
+
98
+ const stopped = (): Stopped => ({ since: options.sequence(), at: options.now() });
99
+
100
+ // Every change to authority happens here, synchronously with its admission or scope closure.
101
+ const apply = (input: Input): State => {
102
+ if (input._tag !== "Expire" && state._tag === "Draining" && options.now() >= state.deadline)
103
+ apply({ _tag: "Expire" });
104
+ const before = state;
105
+
106
+ state = step(state, input, stopped());
107
+ if (input._tag === "Expire" && before._tag === "Draining" && state._tag === "Open")
108
+ Deferred.doneUnsafe(
109
+ ready,
110
+ Exit.fail(
111
+ new BrowserError({
112
+ operation: "suspend",
113
+ reason: new Busy({
114
+ waitedMillis: Math.round(options.now() - before.startedAt),
115
+ ahead: before.inputs.size,
116
+ }),
117
+ dispatched: false,
118
+ }),
119
+ ),
120
+ );
121
+ if (before._tag === "Open" && state._tag !== "Open") Deferred.doneUnsafe(refused, Exit.void);
122
+ if (before._tag === "Draining" && state._tag === "Held")
123
+ Deferred.doneUnsafe(ready, Exit.succeed(state.stopped));
124
+ if (before._tag !== "Open" && state._tag === "Open") {
125
+ refused = Deferred.makeUnsafe();
126
+ ready = Deferred.makeUnsafe();
127
+ }
128
+
129
+ return state;
130
+ };
131
+
132
+ const check = (operation: string) =>
133
+ Effect.suspend(() =>
134
+ state._tag === "Open" && options.closedBy() === undefined
135
+ ? Effect.void
136
+ : Effect.fail(error(operation)),
137
+ );
138
+
139
+ const suspend = (
140
+ drain: Duration.Input = options.drain,
141
+ ): Effect.Effect<Stopped, BrowserError, Scope.Scope> =>
142
+ Effect.suspend(() => {
143
+ const duration = Duration.fromInput(drain);
144
+
145
+ if (
146
+ Option.isNone(duration) ||
147
+ !Duration.isFinite(duration.value) ||
148
+ Duration.toMillis(duration.value) < 0
149
+ )
150
+ return Effect.fail(
151
+ new BrowserError({
152
+ operation: "suspend",
153
+ reason: new InvalidRequest({ detail: "drain must be a finite nonnegative duration" }),
154
+ dispatched: false,
155
+ }),
156
+ );
157
+ const id = ++next;
158
+ const leave = Effect.sync(() => apply({ _tag: "Leave", id }));
159
+
160
+ return Effect.acquireRelease(
161
+ Effect.gen(function* () {
162
+ if (options.closedBy() !== undefined) return yield* error("suspend");
163
+ const current = apply({ _tag: "Suspend", id, drain: Duration.toMillis(duration.value) });
164
+
165
+ if (current._tag === "Held") return current.stopped;
166
+ if (current._tag !== "Draining")
167
+ return yield* Effect.die("suspension did not retain authority");
168
+ const pending = ready;
169
+
170
+ // Native completion and timer callbacks both check the same monotonic deadline. A
171
+ // timer may wake a fraction early, so it sleeps the remaining time instead of hanging.
172
+ const expire: Effect.Effect<Stopped, BrowserError> = Effect.suspend(() => {
173
+ const left = current.deadline - options.now();
174
+
175
+ return left > 0
176
+ ? Effect.sleep(Duration.millis(left)).pipe(Effect.andThen(expire))
177
+ : Effect.sync(() => apply({ _tag: "Expire" })).pipe(
178
+ Effect.andThen(Deferred.await(pending)),
179
+ );
180
+ });
181
+
182
+ const wait = Effect.raceFirst(Deferred.await(pending), expire);
183
+
184
+ return yield* Effect.raceFirst(
185
+ wait,
186
+ options.lost.pipe(Effect.flatMap(() => Effect.fail(error("suspend")))),
187
+ ).pipe(
188
+ Effect.interruptible,
189
+ Effect.filterOrFail(
190
+ () => options.closedBy() === undefined,
191
+ () => error("suspend"),
192
+ ),
193
+ Effect.onError(() => leave),
194
+ );
195
+ }),
196
+ () => leave,
197
+ );
198
+ }).pipe(Effect.provideService(Clock.Clock, options.clock));
199
+
200
+ return {
201
+ check,
202
+ suspend,
203
+ refused: (operation: string): Effect.Effect<never, BrowserError> =>
204
+ Effect.suspend(() =>
205
+ state._tag === "Open"
206
+ ? Deferred.await(refused).pipe(Effect.andThen(Effect.fail(error(operation))))
207
+ : Effect.fail(error(operation)),
208
+ ),
209
+ enter: (operation: string): Effect.Effect<() => void, BrowserError> =>
210
+ Effect.suspend(() => {
211
+ if (state._tag !== "Open" || options.closedBy() !== undefined)
212
+ return Effect.fail(error(operation));
213
+ const id = ++next;
214
+
215
+ apply({ _tag: "Enter", id });
216
+
217
+ return Effect.succeed(() => {
218
+ apply({ _tag: "End", id });
219
+ });
220
+ }),
221
+ };
222
+ };
223
+
224
+ export type Gate = ReturnType<typeof make>;