effect-browser 0.2.0-beta.8 → 0.3.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +705 -1166
- package/dist/Browser-CM65Uo7f.mjs +437 -0
- package/dist/Browser-CM65Uo7f.mjs.map +1 -0
- package/dist/Browser.d.mts +115 -3
- package/dist/Browser.mjs +2 -22
- package/dist/BrowserError-DXXIA6Rk.d.mts +135 -0
- package/dist/BrowserError.d.mts +2 -0
- package/dist/BrowserError.mjs +202 -0
- package/dist/BrowserError.mjs.map +1 -0
- package/dist/BrowserEvent-DzdNdvoh.d.mts +277 -0
- package/dist/BrowserEvent.d.mts +2 -0
- package/dist/BrowserEvent.mjs +295 -0
- package/dist/BrowserEvent.mjs.map +1 -0
- package/dist/Cdp.d.mts +32 -0
- package/dist/Cdp.mjs +61 -0
- package/dist/Cdp.mjs.map +1 -0
- package/dist/Change-DNvztNLu.mjs +138 -0
- package/dist/Change-DNvztNLu.mjs.map +1 -0
- package/dist/Change-DVP2IJWm.d.mts +71 -0
- package/dist/Change.d.mts +2 -0
- package/dist/Change.mjs +2 -0
- package/dist/Chromium.d.mts +29 -81
- package/dist/Chromium.mjs +57 -392
- package/dist/Chromium.mjs.map +1 -1
- package/dist/Expect.d.mts +62 -0
- package/dist/Expect.mjs +114 -0
- package/dist/Expect.mjs.map +1 -0
- package/dist/Frame-BERmf_gM.d.mts +117 -0
- package/dist/Frame.d.mts +2 -0
- package/dist/Frame.mjs +123 -0
- package/dist/Frame.mjs.map +1 -0
- package/dist/Moment.d.mts +2 -0
- package/dist/Moment.mjs +288 -0
- package/dist/Moment.mjs.map +1 -0
- package/dist/Motion.d.mts +40 -0
- package/dist/Motion.mjs +122 -0
- package/dist/Motion.mjs.map +1 -0
- package/dist/Page-D4281dIi.mjs +618 -0
- package/dist/Page-D4281dIi.mjs.map +1 -0
- package/dist/Page-DYTdVhYa.d.mts +593 -0
- package/dist/Page.d.mts +2 -0
- package/dist/Page.mjs +2 -0
- package/dist/Plan-CNNOU1Tx.d.mts +112 -0
- package/dist/Plan-Dtrb-8im.mjs +311 -0
- package/dist/Plan-Dtrb-8im.mjs.map +1 -0
- package/dist/Plan.d.mts +2 -3
- package/dist/Plan.mjs +2 -2
- package/dist/Policy.d.mts +76 -0
- package/dist/Policy.mjs +237 -0
- package/dist/Policy.mjs.map +1 -0
- package/dist/Presentation.d.mts +48 -0
- package/dist/Presentation.mjs +117 -0
- package/dist/Presentation.mjs.map +1 -0
- package/dist/Snapshot-B34hZ4Jl.d.mts +68 -0
- package/dist/Snapshot.d.mts +2 -0
- package/dist/Snapshot.mjs +79 -0
- package/dist/Snapshot.mjs.map +1 -0
- package/dist/Stage.d.mts +54 -0
- package/dist/Stage.mjs +133 -0
- package/dist/Stage.mjs.map +1 -0
- package/dist/Supervisor-BtEG93gR.mjs +420 -0
- package/dist/Supervisor-BtEG93gR.mjs.map +1 -0
- package/dist/Supervisor-Im6-wbg5.d.mts +230 -0
- package/dist/Supervisor.d.mts +2 -0
- package/dist/Supervisor.mjs +2 -0
- package/dist/context-DAYjATgC.mjs +137 -0
- package/dist/context-DAYjATgC.mjs.map +1 -0
- package/dist/index.d.mts +17 -14
- package/dist/index.mjs +18 -13
- package/dist/match.inpage-xOe9LNO0.mjs +48 -0
- package/dist/match.inpage-xOe9LNO0.mjs.map +1 -0
- package/dist/page-Banfm_zc.mjs +5097 -0
- package/dist/page-Banfm_zc.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/url-CcL0K7S0.mjs +262 -0
- package/dist/url-CcL0K7S0.mjs.map +1 -0
- package/package.json +96 -61
- package/src/Browser.ts +625 -0
- package/src/BrowserError.ts +225 -0
- package/src/BrowserEvent.ts +294 -0
- package/src/Cdp.ts +101 -0
- package/src/Change.ts +88 -0
- package/src/Chromium.ts +102 -0
- package/src/Expect.ts +180 -0
- package/src/Frame.ts +130 -0
- package/src/Moment.ts +479 -0
- package/src/Motion.ts +166 -0
- package/src/Page.ts +520 -0
- package/src/Plan.ts +350 -0
- package/src/Policy.ts +325 -0
- package/src/Presentation.ts +195 -0
- package/src/Snapshot.ts +89 -0
- package/src/Stage.ts +223 -0
- package/src/Supervisor.ts +353 -0
- package/src/index.ts +20 -0
- package/src/internal/input/actions.ts +607 -0
- package/src/internal/input/dispatch.ts +161 -0
- package/src/internal/input/edit.inpage.ts +162 -0
- package/src/internal/input/evidence.inpage.ts +164 -0
- package/src/internal/input/guard.inpage.ts +605 -0
- package/src/internal/input/guard.ts +183 -0
- package/src/internal/input/keyboard.ts +112 -0
- package/src/internal/input/keys.ts +127 -0
- package/src/internal/input/perform.ts +322 -0
- package/src/internal/input/pointer.ts +153 -0
- package/src/internal/input/replies.ts +159 -0
- package/src/internal/input/settle.ts +50 -0
- package/src/internal/input/style.ts +186 -0
- package/src/internal/input/targets.inpage.ts +293 -0
- package/src/internal/input/targets.ts +148 -0
- package/src/internal/page/bridge.ts +386 -0
- package/src/internal/page/context.ts +302 -0
- package/src/internal/page/lane.ts +301 -0
- package/src/internal/page/navigation.ts +183 -0
- package/src/internal/page/openings.ts +87 -0
- package/src/internal/page/page.ts +137 -0
- package/src/internal/page/url.ts +100 -0
- package/src/internal/page/viewport.ts +43 -0
- package/src/internal/pictures/capture.ts +778 -0
- package/src/internal/pictures/clock.ts +141 -0
- package/src/internal/pictures/pictures.ts +520 -0
- package/src/internal/pictures/transport.ts +242 -0
- package/src/internal/reading/choose.ts +100 -0
- package/src/internal/reading/context.inpage.ts +201 -0
- package/src/internal/reading/controls.inpage.ts +158 -0
- package/src/internal/reading/expect.inpage.ts +70 -0
- package/src/internal/reading/match.inpage.ts +92 -0
- package/src/internal/reading/names.inpage.ts +306 -0
- package/src/internal/reading/outline.inpage.ts +260 -0
- package/src/internal/reading/reading.ts +223 -0
- package/src/internal/reading/ready.inpage.ts +154 -0
- package/src/internal/reading/ready.ts +105 -0
- package/src/internal/reading/subjects.inpage.ts +188 -0
- package/src/internal/reading/text.inpage.ts +135 -0
- package/src/internal/reading/walk.inpage.ts +220 -0
- package/src/internal/supervisor/lifecycle.ts +265 -0
- package/src/internal/timeline/changes.inpage.ts +216 -0
- package/src/internal/timeline/changes.ts +100 -0
- package/src/internal/timeline/events.ts +93 -0
- package/src/internal/timeline/fold.inpage.ts +123 -0
- package/src/internal/timeline/history.inpage.ts +229 -0
- package/src/internal/timeline/marks.inpage.ts +207 -0
- package/src/internal/timeline/record.inpage.ts +133 -0
- package/src/internal/timeline/sight.inpage.ts +122 -0
- package/src/internal/timeline/window.ts +96 -0
- package/src/internal/timeline/words.ts +65 -0
- package/dist/Bootstrap-B9UUXsvF.d.mts +0 -183
- package/dist/Bootstrap-CeQOWBy6.mjs +0 -189
- package/dist/Bootstrap-CeQOWBy6.mjs.map +0 -1
- package/dist/Bootstrap.d.mts +0 -3
- package/dist/Bootstrap.mjs +0 -2
- package/dist/Browser-n-bKgBNN.d.mts +0 -372
- package/dist/Browser.mjs.map +0 -1
- package/dist/BrowserData-CPIIIHol.d.mts +0 -585
- package/dist/BrowserData.d.mts +0 -3
- package/dist/BrowserData.mjs +0 -695
- package/dist/BrowserData.mjs.map +0 -1
- package/dist/BrowserRuntime-CFe9uUTv.d.mts +0 -148
- package/dist/BrowserRuntime-HJ1yYlFS.mjs +0 -6741
- package/dist/BrowserRuntime-HJ1yYlFS.mjs.map +0 -1
- package/dist/BrowserRuntime.d.mts +0 -3
- package/dist/BrowserRuntime.mjs +0 -3
- package/dist/Capture.d.mts +0 -56
- package/dist/Capture.mjs +0 -678
- package/dist/Capture.mjs.map +0 -1
- package/dist/CaptureData-CYriZHX1.mjs +0 -201
- package/dist/CaptureData-CYriZHX1.mjs.map +0 -1
- package/dist/CaptureData.d.mts +0 -219
- package/dist/CaptureData.mjs +0 -2
- package/dist/Errors.d.mts +0 -158
- package/dist/Errors.mjs +0 -207
- package/dist/Errors.mjs.map +0 -1
- package/dist/Images-CaaPNeed.mjs +0 -76
- package/dist/Images-CaaPNeed.mjs.map +0 -1
- package/dist/OperationOptions-DAeRS6O9.mjs +0 -55
- package/dist/OperationOptions-DAeRS6O9.mjs.map +0 -1
- package/dist/PageControl.d.mts +0 -15
- package/dist/PageControl.mjs +0 -34
- package/dist/PageControl.mjs.map +0 -1
- package/dist/PageRegistry-B3q9DS3g.mjs +0 -164
- package/dist/PageRegistry-B3q9DS3g.mjs.map +0 -1
- package/dist/Plan-D8qQ48Ic.mjs +0 -512
- package/dist/Plan-D8qQ48Ic.mjs.map +0 -1
- package/dist/PlanData-B668RxbQ.d.mts +0 -4831
- package/dist/PlanData.d.mts +0 -3
- package/dist/PlanData.mjs +0 -268
- package/dist/PlanData.mjs.map +0 -1
- package/dist/PlanInput-BJcPPncs.mjs +0 -75
- package/dist/PlanInput-BJcPPncs.mjs.map +0 -1
- package/dist/PublicSession-Bs61Eal8.mjs +0 -5601
- package/dist/PublicSession-Bs61Eal8.mjs.map +0 -1
- package/dist/Testing.d.mts +0 -479
- package/dist/Testing.mjs +0 -2147
- package/dist/Testing.mjs.map +0 -1
- package/dist/Timeline-fV22Xk-S.d.mts +0 -29
- package/dist/Timeline.d.mts +0 -4
- package/dist/Timeline.mjs +0 -20
- package/dist/Timeline.mjs.map +0 -1
- package/dist/TimelineData-BsOn-YGZ.d.mts +0 -3818
- package/dist/TimelineData.d.mts +0 -3
- package/dist/TimelineData.mjs +0 -482
- package/dist/TimelineData.mjs.map +0 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { t as BrowserError } from "./BrowserError-DXXIA6Rk.mjs";
|
|
2
|
+
import { b as Subject, i as BrowserEvent } from "./BrowserEvent-DzdNdvoh.mjs";
|
|
3
|
+
import { m as ReadyOptions, o as Found, u as Page } from "./Page-DYTdVhYa.mjs";
|
|
4
|
+
import { Effect, Schema } from "effect";
|
|
5
|
+
//#region src/internal/reading/choose.d.ts
|
|
6
|
+
declare const Missing_base: Schema.Class<Missing, Schema.TaggedStruct<"Missing", {}>, import("effect/Cause").YieldableError>;
|
|
7
|
+
/** Nothing on the page has the step's role and name. */
|
|
8
|
+
declare class Missing extends Missing_base {
|
|
9
|
+
get message(): string;
|
|
10
|
+
}
|
|
11
|
+
declare const Ambiguous_base: Schema.Class<Ambiguous, Schema.TaggedStruct<"Ambiguous", {
|
|
12
|
+
readonly count: Schema.Int;
|
|
13
|
+
}>, import("effect/Cause").YieldableError>;
|
|
14
|
+
/** Several elements match the step's subject equally well, and nothing tells them apart. */
|
|
15
|
+
declare class Ambiguous extends Ambiguous_base {
|
|
16
|
+
get message(): string;
|
|
17
|
+
}
|
|
18
|
+
declare const Drifted_base: Schema.Class<Drifted, Schema.TaggedStruct<"Drifted", {
|
|
19
|
+
readonly detail: Schema.String;
|
|
20
|
+
}>, import("effect/Cause").YieldableError>;
|
|
21
|
+
/** The page is not where the recording was: another site, or no element with its context. */
|
|
22
|
+
declare class Drifted extends Drifted_base {
|
|
23
|
+
get message(): string;
|
|
24
|
+
}
|
|
25
|
+
declare namespace Plan_d_exports {
|
|
26
|
+
export { Ambiguous, Drifted, Missing, Plan, ReplayError, ReplayOptions, Step, StepAction, fromEvents, locate, replay };
|
|
27
|
+
}
|
|
28
|
+
declare const StepAction: Schema.Literals<readonly ["navigate", "back", "reload", "click", "hover", "drag", "type", "press", "scroll", "select"]>;
|
|
29
|
+
type StepAction = typeof StepAction.Type;
|
|
30
|
+
declare const Step_base: Schema.Class<Step, Schema.Struct<{
|
|
31
|
+
readonly action: Schema.Literals<readonly ["navigate", "back", "reload", "click", "hover", "drag", "type", "press", "scroll", "select"]>;
|
|
32
|
+
/** Where the page was once the step was done, as recorded; empty when unknown. */
|
|
33
|
+
readonly url: Schema.String;
|
|
34
|
+
/** A navigation's requested address, or the keys a press pressed. */
|
|
35
|
+
readonly target: Schema.optional<Schema.String>;
|
|
36
|
+
readonly subject: Schema.optional<typeof Subject>;
|
|
37
|
+
/** Where a drag ended. */
|
|
38
|
+
readonly to: Schema.optional<typeof Subject>;
|
|
39
|
+
/** The viewport point a step aimed at, when it aimed at a point, and a drag's end point. */
|
|
40
|
+
readonly point: Schema.optional<Schema.Struct<{
|
|
41
|
+
readonly x: Schema.Finite;
|
|
42
|
+
readonly y: Schema.Finite;
|
|
43
|
+
}>>;
|
|
44
|
+
readonly toPoint: Schema.optional<Schema.Struct<{
|
|
45
|
+
readonly x: Schema.Finite;
|
|
46
|
+
readonly y: Schema.Finite;
|
|
47
|
+
}>>;
|
|
48
|
+
/** The box of what a step aimed at by point found there, so replay aims at the same place in it. */
|
|
49
|
+
readonly box: Schema.optional<Schema.Struct<{
|
|
50
|
+
readonly x: Schema.Finite;
|
|
51
|
+
readonly y: Schema.Finite;
|
|
52
|
+
readonly width: Schema.Finite;
|
|
53
|
+
readonly height: Schema.Finite;
|
|
54
|
+
}>>;
|
|
55
|
+
/**
|
|
56
|
+
* The input slot whose text a `type` step types, or whose address a `navigate` step goes to, as
|
|
57
|
+
* its recorded address had a credential withheld.
|
|
58
|
+
*/
|
|
59
|
+
readonly input: Schema.optional<Schema.String>;
|
|
60
|
+
/** The slot's text went into a secret field, so replay types it into nothing else. */
|
|
61
|
+
readonly secret: Schema.optional<Schema.Literal<true>>;
|
|
62
|
+
readonly options: Schema.optional<Schema.Struct<{
|
|
63
|
+
readonly button: Schema.optional<Schema.Literals<readonly ["left", "right", "middle"]>>;
|
|
64
|
+
readonly clickCount: Schema.optional<Schema.Finite>;
|
|
65
|
+
readonly holdMillis: Schema.optional<Schema.Finite>;
|
|
66
|
+
readonly replace: Schema.optional<Schema.Boolean>;
|
|
67
|
+
readonly submit: Schema.optional<Schema.Boolean>;
|
|
68
|
+
readonly times: Schema.optional<Schema.Finite>;
|
|
69
|
+
readonly dx: Schema.optional<Schema.Finite>;
|
|
70
|
+
readonly dy: Schema.optional<Schema.Finite>;
|
|
71
|
+
readonly values: Schema.optional<Schema.$Array<Schema.String>>;
|
|
72
|
+
}>>;
|
|
73
|
+
}>, {}>;
|
|
74
|
+
/** One recorded step: the action, what it acted on, and what else it was asked. */
|
|
75
|
+
declare class Step extends Step_base {}
|
|
76
|
+
declare const Plan_base: Schema.Class<Plan, Schema.Struct<{
|
|
77
|
+
readonly version: Schema.Literal<1>;
|
|
78
|
+
readonly steps: Schema.$Array<typeof Step>;
|
|
79
|
+
}>, {}>;
|
|
80
|
+
declare class Plan extends Plan_base {
|
|
81
|
+
/** The input slots its steps type, in order; replay needs text for each. */
|
|
82
|
+
get inputs(): ReadonlyArray<string>;
|
|
83
|
+
}
|
|
84
|
+
declare const ReplayError_base: Schema.Class<ReplayError, Schema.TaggedStruct<"ReplayError", {
|
|
85
|
+
readonly step: Schema.Int;
|
|
86
|
+
readonly reason: Schema.Union<readonly [typeof Missing, typeof Ambiguous, typeof Drifted, typeof BrowserError]>;
|
|
87
|
+
}>, import("effect/Cause").YieldableError>;
|
|
88
|
+
/** Where replay stopped, counting steps from 0, and why. */
|
|
89
|
+
declare class ReplayError extends ReplayError_base {
|
|
90
|
+
get message(): string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The walk one page's events record: the completed actions of the page that acted first, in
|
|
94
|
+
* order. `page.recentEvents` gives them, or `browser.events()` read until the walk ends.
|
|
95
|
+
*/
|
|
96
|
+
declare const fromEvents: (events: Iterable<BrowserEvent>) => Plan;
|
|
97
|
+
/**
|
|
98
|
+
* The one element on the page that a subject names, by role, name and context (see `Plan`),
|
|
99
|
+
* looking again for two seconds while the page may still be drawing it.
|
|
100
|
+
*/
|
|
101
|
+
declare const locate: (page: Page, subject: Subject) => Effect.Effect<Found, Ambiguous | BrowserError | Drifted | Missing, never>;
|
|
102
|
+
interface ReplayOptions {
|
|
103
|
+
/** How each step waits for the page first, as `Page.ready` takes it, or false not to wait. */
|
|
104
|
+
readonly settle?: ReadyOptions | false | undefined;
|
|
105
|
+
/** The text for each of the plan's input slots. */
|
|
106
|
+
readonly inputs?: Readonly<Record<string, string>> | undefined;
|
|
107
|
+
}
|
|
108
|
+
/** Take a plan's steps on a page, in order, stopping at the first that cannot be taken. */
|
|
109
|
+
declare const replay: (page: Page, plan: Plan, options?: ReplayOptions) => Effect.Effect<undefined, ReplayError, never>;
|
|
110
|
+
//#endregion
|
|
111
|
+
export { Step as a, locate as c, Drifted as d, Missing as f, ReplayOptions as i, replay as l, Plan_d_exports as n, StepAction as o, ReplayError as r, fromEvents as s, Plan as t, Ambiguous as u };
|
|
112
|
+
//# sourceMappingURL=Plan-CNNOU1Tx.d.mts.map
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { ActionOptions, Box, Subject } from "./BrowserEvent.mjs";
|
|
3
|
+
import { BrowserError, InvalidRequest } from "./BrowserError.mjs";
|
|
4
|
+
import { s as undispatched } from "./context-DAYjATgC.mjs";
|
|
5
|
+
import { p as redacted } from "./Page-D4281dIi.mjs";
|
|
6
|
+
import { t as match } from "./match.inpage-xOe9LNO0.mjs";
|
|
7
|
+
import { Duration, Effect, Schedule, Schema } from "effect";
|
|
8
|
+
//#region src/internal/reading/choose.ts
|
|
9
|
+
/**
|
|
10
|
+
* Choosing the one element a recorded subject names, among those `find` gives for its role and
|
|
11
|
+
* name. Context tells them apart, and an element that does not repeat every part of the recorded
|
|
12
|
+
* context is another subject, so a sole element left once its own has gone, such as Alice's
|
|
13
|
+
* "Remove" after Bob has left, drifts rather than stands in:
|
|
14
|
+
*
|
|
15
|
+
* - a row is read whole: a row named "Wrapped BTC" is not the row "BTC". Laid out again without
|
|
16
|
+
* rows, as cards, one of the element's fields must start with the row's words;
|
|
17
|
+
* - a column counts where the element is under one;
|
|
18
|
+
* - a label or heading must read the same, or its words appear, whole and in order, in one of the
|
|
19
|
+
* element's fields, as `near` reads them.
|
|
20
|
+
*
|
|
21
|
+
* The rest are ranked by how much of the recorded context they read the same. There is no
|
|
22
|
+
* ordinal, so a tie stays a tie.
|
|
23
|
+
*/
|
|
24
|
+
/** Nothing on the page has the step's role and name. */
|
|
25
|
+
var Missing = class extends Schema.TaggedError()("Missing", {}) {
|
|
26
|
+
get message() {
|
|
27
|
+
return "nothing on the page has the recorded role and name";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/** Several elements match the step's subject equally well, and nothing tells them apart. */
|
|
31
|
+
var Ambiguous = class extends Schema.TaggedError()("Ambiguous", { count: Schema.Int }) {
|
|
32
|
+
get message() {
|
|
33
|
+
return `${this.count} elements match the recorded subject equally well`;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/** The page is not where the recording was: another site, or no element with its context. */
|
|
37
|
+
var Drifted = class extends Schema.TaggedError()("Drifted", { detail: Schema.String }) {
|
|
38
|
+
get message() {
|
|
39
|
+
return this.detail;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const { holds, normalize } = match();
|
|
43
|
+
const fields = [
|
|
44
|
+
"row",
|
|
45
|
+
"column",
|
|
46
|
+
"label",
|
|
47
|
+
"heading"
|
|
48
|
+
];
|
|
49
|
+
const said = (context) => Object.values(context).filter((value) => typeof value === "string");
|
|
50
|
+
/** How an element repeats one recorded field: 2 read the same, 1 found as words, 0 not at all. */
|
|
51
|
+
const repeats = (field, value, context) => {
|
|
52
|
+
const own = context[field];
|
|
53
|
+
if (own !== void 0 && normalize(own) === normalize(value)) return 2;
|
|
54
|
+
if (field === "column") return own === void 0 ? 1 : 0;
|
|
55
|
+
if (field === "row") return own === void 0 && said(context).some((other) => `${normalize(other)} `.startsWith(`${normalize(value)} `)) ? 1 : 0;
|
|
56
|
+
return said(context).some((other) => holds(other, value)) ? 1 : 0;
|
|
57
|
+
};
|
|
58
|
+
const choose = (subject, found) => {
|
|
59
|
+
const named = found.filter((one) => subject.role !== null || one.subject.role === null);
|
|
60
|
+
const kept = named.flatMap((one) => {
|
|
61
|
+
let score = 0;
|
|
62
|
+
for (const field of fields) {
|
|
63
|
+
const value = subject.context[field];
|
|
64
|
+
const repeated = value === void 0 ? 0 : repeats(field, value, one.subject.context);
|
|
65
|
+
if (value !== void 0 && repeated === 0) return [];
|
|
66
|
+
score += repeated;
|
|
67
|
+
}
|
|
68
|
+
return [{
|
|
69
|
+
one,
|
|
70
|
+
score
|
|
71
|
+
}];
|
|
72
|
+
});
|
|
73
|
+
const best = Math.max(...kept.map(({ score }) => score));
|
|
74
|
+
const top = kept.filter(({ score }) => score === best);
|
|
75
|
+
const [only] = top;
|
|
76
|
+
if (named.length === 0) return Effect.fail(new Missing());
|
|
77
|
+
if (kept.length === 0) return Effect.fail(new Drifted({ detail: "no element named so repeats the recorded context" }));
|
|
78
|
+
return top.length === 1 && only !== void 0 ? Effect.succeed(only.one) : Effect.fail(new Ambiguous({ count: top.length }));
|
|
79
|
+
};
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/Plan.ts
|
|
82
|
+
/**
|
|
83
|
+
* A walk recorded once from a page's events and replayed later, on a fresh page, with no model
|
|
84
|
+
* call.
|
|
85
|
+
*
|
|
86
|
+
* `fromEvents` keeps one page's completed actions with their subjects and the options they were
|
|
87
|
+
* given, its navigations with both the address asked for and the one reached, and what was typed
|
|
88
|
+
* as a named input slot, never the text itself. A navigation to an address whose credentials the
|
|
89
|
+
* events withheld is an input slot too, so replay goes where the caller says, or nowhere, and
|
|
90
|
+
* never to the address without them. `replay` takes the steps in turn. Before a step
|
|
91
|
+
* that acts on the page, it waits for `Page.ready`; then it finds the one element the step's
|
|
92
|
+
* subject names, by role and name among those that repeat all its recorded context, and acts on
|
|
93
|
+
* it. Text typed into focus was typed into the focused field, which replay finds the same way, and
|
|
94
|
+
* text typed into a secret field goes into nothing else. An element step never falls back to
|
|
95
|
+
* coordinates, and a step recorded at a point acts there only while the element found for it still
|
|
96
|
+
* covers that point. Replay stops at the first step it cannot take and says why: `Missing`,
|
|
97
|
+
* `Ambiguous`, `Drifted`, or the step's own `BrowserError`. Nothing is replayed automatically, and
|
|
98
|
+
* a plan of another version, such as one 0.2 stored, does not decode.
|
|
99
|
+
*
|
|
100
|
+
* @since 0.3.0
|
|
101
|
+
*/
|
|
102
|
+
var Plan_exports = /* @__PURE__ */ __exportAll({
|
|
103
|
+
Ambiguous: () => Ambiguous,
|
|
104
|
+
Drifted: () => Drifted,
|
|
105
|
+
Missing: () => Missing,
|
|
106
|
+
Plan: () => Plan,
|
|
107
|
+
ReplayError: () => ReplayError,
|
|
108
|
+
Step: () => Step,
|
|
109
|
+
StepAction: () => StepAction,
|
|
110
|
+
fromEvents: () => fromEvents,
|
|
111
|
+
locate: () => locate,
|
|
112
|
+
replay: () => replay
|
|
113
|
+
});
|
|
114
|
+
const ViewportPoint = Schema.Struct({
|
|
115
|
+
x: Schema.Finite,
|
|
116
|
+
y: Schema.Finite
|
|
117
|
+
});
|
|
118
|
+
const StepAction = Schema.Literals([
|
|
119
|
+
"navigate",
|
|
120
|
+
"back",
|
|
121
|
+
"reload",
|
|
122
|
+
"click",
|
|
123
|
+
"hover",
|
|
124
|
+
"drag",
|
|
125
|
+
"type",
|
|
126
|
+
"press",
|
|
127
|
+
"scroll",
|
|
128
|
+
"select"
|
|
129
|
+
]);
|
|
130
|
+
/** One recorded step: the action, what it acted on, and what else it was asked. */
|
|
131
|
+
var Step = class extends Schema.Class("effect-browser/Plan/Step")({
|
|
132
|
+
action: StepAction,
|
|
133
|
+
/** Where the page was once the step was done, as recorded; empty when unknown. */
|
|
134
|
+
url: Schema.String,
|
|
135
|
+
/** A navigation's requested address, or the keys a press pressed. */
|
|
136
|
+
target: Schema.optional(Schema.String),
|
|
137
|
+
subject: Schema.optional(Subject),
|
|
138
|
+
/** Where a drag ended. */
|
|
139
|
+
to: Schema.optional(Subject),
|
|
140
|
+
/** The viewport point a step aimed at, when it aimed at a point, and a drag's end point. */
|
|
141
|
+
point: Schema.optional(ViewportPoint),
|
|
142
|
+
toPoint: Schema.optional(ViewportPoint),
|
|
143
|
+
/** The box of what a step aimed at by point found there, so replay aims at the same place in it. */
|
|
144
|
+
box: Schema.optional(Box),
|
|
145
|
+
/**
|
|
146
|
+
* The input slot whose text a `type` step types, or whose address a `navigate` step goes to, as
|
|
147
|
+
* its recorded address had a credential withheld.
|
|
148
|
+
*/
|
|
149
|
+
input: Schema.optional(Schema.String),
|
|
150
|
+
/** The slot's text went into a secret field, so replay types it into nothing else. */
|
|
151
|
+
secret: Schema.optional(Schema.Literal(true)),
|
|
152
|
+
options: Schema.optional(ActionOptions)
|
|
153
|
+
}) {};
|
|
154
|
+
var Plan = class extends Schema.Class("effect-browser/Plan")({
|
|
155
|
+
version: Schema.Literal(1),
|
|
156
|
+
steps: Schema.Array(Step)
|
|
157
|
+
}) {
|
|
158
|
+
/** The input slots its steps type, in order; replay needs text for each. */
|
|
159
|
+
get inputs() {
|
|
160
|
+
return this.steps.flatMap((step) => step.input === void 0 ? [] : [step.input]);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
/** Where replay stopped, counting steps from 0, and why. */
|
|
164
|
+
var ReplayError = class extends Schema.TaggedError()("ReplayError", {
|
|
165
|
+
step: Schema.Int,
|
|
166
|
+
reason: Schema.Union([
|
|
167
|
+
Missing,
|
|
168
|
+
Ambiguous,
|
|
169
|
+
Drifted,
|
|
170
|
+
BrowserError
|
|
171
|
+
])
|
|
172
|
+
}) {
|
|
173
|
+
get message() {
|
|
174
|
+
return `step ${this.step} failed: ${this.reason.message}`;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const isStep = (event) => event._tag === "Action" && event.ok && StepAction.literals.includes(event.name);
|
|
178
|
+
const pointIn = (target) => {
|
|
179
|
+
const parsed = /^\{?(?:"x":)?(-?[\d.]+),(?:"y":)?(-?[\d.]+)\}?$/.exec(target ?? "");
|
|
180
|
+
return parsed === null ? void 0 : {
|
|
181
|
+
x: Number(parsed[1]),
|
|
182
|
+
y: Number(parsed[2])
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
/** A slot named for the field it fills, such as `email`, unique within the plan. */
|
|
186
|
+
const slotFor = (taken, field) => {
|
|
187
|
+
const base = field?.toLowerCase().match(/[\p{L}\p{N}]+/gu)?.join("-") ?? "input";
|
|
188
|
+
let slot = base;
|
|
189
|
+
for (let count = 2; taken.has(slot); count++) slot = `${base}-${count}`;
|
|
190
|
+
taken.add(slot);
|
|
191
|
+
return slot;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* The walk one page's events record: the completed actions of the page that acted first, in
|
|
195
|
+
* order. `page.recentEvents` gives them, or `browser.events()` read until the walk ends.
|
|
196
|
+
*/
|
|
197
|
+
const fromEvents = (events) => {
|
|
198
|
+
const all = Array.from(events);
|
|
199
|
+
const page = all.find((event) => event._tag === "Action")?.page;
|
|
200
|
+
const mine = all.filter((event) => "page" in event && event.page === page);
|
|
201
|
+
const steps = mine.filter(isStep);
|
|
202
|
+
const opened = mine.find((event) => event._tag === "PageOpened")?.url ?? "";
|
|
203
|
+
const moves = mine.filter((event) => event._tag === "Navigated");
|
|
204
|
+
const taken = /* @__PURE__ */ new Set();
|
|
205
|
+
const before = (time) => moves.findLast((move) => move.at <= time)?.url ?? opened;
|
|
206
|
+
return new Plan({
|
|
207
|
+
version: 1,
|
|
208
|
+
steps: steps.map((action, index) => {
|
|
209
|
+
const drag = action.name === "drag" ? (action.target ?? "").split(" -> ") : void 0;
|
|
210
|
+
const point = pointIn(drag === void 0 ? action.target : drag[0]);
|
|
211
|
+
return new Step({
|
|
212
|
+
action: action.name,
|
|
213
|
+
url: before(steps[index + 1]?.startedAt ?? Number.POSITIVE_INFINITY),
|
|
214
|
+
target: action.name === "navigate" || action.name === "press" ? action.target : void 0,
|
|
215
|
+
subject: action.subject,
|
|
216
|
+
to: action.to,
|
|
217
|
+
point,
|
|
218
|
+
toPoint: pointIn(drag?.[1]),
|
|
219
|
+
box: point === void 0 ? void 0 : action.box,
|
|
220
|
+
input: action.name === "type" ? slotFor(taken, action.subject?.name) : action.name === "navigate" && action.target?.includes(redacted) === true ? slotFor(taken, "address") : void 0,
|
|
221
|
+
secret: action.name === "type" && action.text === redacted ? true : void 0,
|
|
222
|
+
options: action.options
|
|
223
|
+
});
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* The one element on the page that a subject names, by role, name and context (see `Plan`),
|
|
229
|
+
* looking again for two seconds while the page may still be drawing it.
|
|
230
|
+
*/
|
|
231
|
+
const locate = (page, subject) => page.find({
|
|
232
|
+
name: subject.name,
|
|
233
|
+
scope: "document",
|
|
234
|
+
...subject.role === null ? {} : { role: subject.role }
|
|
235
|
+
}).pipe(Effect.flatMap((found) => choose(subject, found)), Effect.retry({
|
|
236
|
+
schedule: Schedule.spaced(Duration.millis(250)),
|
|
237
|
+
times: 8,
|
|
238
|
+
while: (error) => error._tag !== "BrowserError"
|
|
239
|
+
}), Effect.withSpan("Plan.locate", { attributes: { role: subject.role ?? "" } }));
|
|
240
|
+
const invalid = (detail) => undispatched("replay", new InvalidRequest({ detail }));
|
|
241
|
+
/**
|
|
242
|
+
* Where the step acts: the element its subject names, or, for a step recorded at a point, the same
|
|
243
|
+
* place within that element, as long as a press there reaches that element and not one over it.
|
|
244
|
+
* An element out of view is first brought into it, by hovering it, as a press by ref would be.
|
|
245
|
+
*/
|
|
246
|
+
const aim = (page, subject, point, recorded) => {
|
|
247
|
+
if (subject === void 0) return Effect.fail(invalid("the step names no element"));
|
|
248
|
+
if (point === void 0) return Effect.map(locate(page, subject), ({ ref }) => ref);
|
|
249
|
+
const reach = ({ ref, box }) => {
|
|
250
|
+
const at = recorded === void 0 || recorded.width <= 0 || recorded.height <= 0 ? point : {
|
|
251
|
+
x: box.x + (point.x - recorded.x) / recorded.width * box.width,
|
|
252
|
+
y: box.y + (point.y - recorded.y) / recorded.height * box.height
|
|
253
|
+
};
|
|
254
|
+
return Effect.map(page.find({ at }), ([reached]) => reached?.ref === ref ? at : void 0);
|
|
255
|
+
};
|
|
256
|
+
const missed = new Drifted({ detail: "a press at the subject's point reaches another element" });
|
|
257
|
+
return locate(page, subject).pipe(Effect.flatMap((found) => Effect.flatMap(reach(found), (at) => at !== void 0 ? Effect.succeed(at) : page.hover(found.ref).pipe(Effect.andThen(locate(page, subject)), Effect.flatMap(reach), Effect.flatMap((again) => again === void 0 ? Effect.fail(missed) : Effect.succeed(again))))));
|
|
258
|
+
};
|
|
259
|
+
const take = (page, step, text) => {
|
|
260
|
+
const options = step.options ?? {};
|
|
261
|
+
switch (step.action) {
|
|
262
|
+
case "navigate": return page.goto(step.input === void 0 ? step.target ?? step.url : text);
|
|
263
|
+
case "back": return page.back;
|
|
264
|
+
case "reload": return page.reload;
|
|
265
|
+
case "press": return page.press(step.target ?? "", options);
|
|
266
|
+
case "click": return aim(page, step.subject, step.point, step.box).pipe(Effect.flatMap((at) => page.click(at, options)), Effect.asVoid);
|
|
267
|
+
case "hover": return Effect.flatMap(aim(page, step.subject, step.point, step.box), page.hover);
|
|
268
|
+
case "drag": return Effect.all([aim(page, step.subject, step.point), aim(page, step.to, step.toPoint)]).pipe(Effect.flatMap(([from, to]) => page.drag(from, to)));
|
|
269
|
+
case "select": return aim(page, step.subject).pipe(Effect.flatMap((at) => page.select(typeof at === "string" ? at : "", options.values ?? [])), Effect.asVoid);
|
|
270
|
+
case "scroll": return step.subject === void 0 ? page.scroll(options) : Effect.flatMap(aim(page, step.subject, step.point, step.box), (at) => page.scroll({
|
|
271
|
+
...options,
|
|
272
|
+
at
|
|
273
|
+
}));
|
|
274
|
+
case "type": return step.subject === void 0 ? page.type(text, {
|
|
275
|
+
...options,
|
|
276
|
+
secret: step.secret
|
|
277
|
+
}) : Effect.flatMap(aim(page, step.subject), (into) => page.type(text, {
|
|
278
|
+
...options,
|
|
279
|
+
into: typeof into === "string" ? into : void 0,
|
|
280
|
+
secret: step.secret
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
/** Take a plan's steps on a page, in order, stopping at the first that cannot be taken. */
|
|
285
|
+
const replay = (page, plan, options = {}) => Effect.gen(function* () {
|
|
286
|
+
const inputs = options.inputs ?? {};
|
|
287
|
+
const settle = options.settle === false ? Effect.void : page.ready(options.settle);
|
|
288
|
+
const at = (step) => (effect) => effect.pipe(Effect.mapError((reason) => new ReplayError({
|
|
289
|
+
step,
|
|
290
|
+
reason
|
|
291
|
+
})));
|
|
292
|
+
const arrived = (index) => {
|
|
293
|
+
const recorded = plan.steps[index]?.url ?? "";
|
|
294
|
+
return page.url.pipe(Effect.flatMap((url) => recorded === "" || URL.parse(url)?.origin === URL.parse(recorded)?.origin ? Effect.void : Effect.fail(new Drifted({ detail: `the page is at ${url}, not on ${recorded}'s site` }))), at(index));
|
|
295
|
+
};
|
|
296
|
+
const unfilled = plan.steps.findIndex((step) => step.input !== void 0 && inputs[step.input] === void 0);
|
|
297
|
+
if (unfilled !== -1) return yield* at(unfilled)(Effect.fail(invalid(`no text for the input "${plan.steps[unfilled]?.input ?? ""}"`)));
|
|
298
|
+
for (const [index, step] of plan.steps.entries()) {
|
|
299
|
+
if (step.action !== "navigate" && step.action !== "back" && step.action !== "reload") yield* at(index)(settle);
|
|
300
|
+
if (index > 0) yield* arrived(index - 1);
|
|
301
|
+
yield* at(index)(take(page, step, step.input === void 0 ? "" : inputs[step.input] ?? ""));
|
|
302
|
+
}
|
|
303
|
+
if (plan.steps.length > 0) {
|
|
304
|
+
yield* at(plan.steps.length - 1)(settle);
|
|
305
|
+
yield* arrived(plan.steps.length - 1);
|
|
306
|
+
}
|
|
307
|
+
}).pipe(Effect.withSpan("Plan.replay", { attributes: { steps: plan.steps.length } }));
|
|
308
|
+
//#endregion
|
|
309
|
+
export { StepAction as a, replay as c, Missing as d, Step as i, Ambiguous as l, Plan_exports as n, fromEvents as o, ReplayError as r, locate as s, Plan as t, Drifted as u };
|
|
310
|
+
|
|
311
|
+
//# sourceMappingURL=Plan-Dtrb-8im.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Plan-Dtrb-8im.mjs","names":[],"sources":["../src/internal/reading/choose.ts","../src/Plan.ts"],"sourcesContent":["/**\n * Choosing the one element a recorded subject names, among those `find` gives for its role and\n * name. Context tells them apart, and an element that does not repeat every part of the recorded\n * context is another subject, so a sole element left once its own has gone, such as Alice's\n * \"Remove\" after Bob has left, drifts rather than stands in:\n *\n * - a row is read whole: a row named \"Wrapped BTC\" is not the row \"BTC\". Laid out again without\n * rows, as cards, one of the element's fields must start with the row's words;\n * - a column counts where the element is under one;\n * - a label or heading must read the same, or its words appear, whole and in order, in one of the\n * element's fields, as `near` reads them.\n *\n * The rest are ranked by how much of the recorded context they read the same. There is no\n * ordinal, so a tie stays a tie.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport type { Subject, SubjectContext } from \"../../BrowserEvent.ts\";\nimport type { Found } from \"../../Page.ts\";\nimport { match } from \"./match.inpage.ts\";\n\n/** Nothing on the page has the step's role and name. */\nexport class Missing extends Schema.TaggedError<Missing>()(\"Missing\", {}) {\n override get message() {\n return \"nothing on the page has the recorded role and name\";\n }\n}\n\n/** Several elements match the step's subject equally well, and nothing tells them apart. */\nexport class Ambiguous extends Schema.TaggedError<Ambiguous>()(\"Ambiguous\", {\n count: Schema.Int,\n}) {\n override get message() {\n return `${this.count} elements match the recorded subject equally well`;\n }\n}\n\n/** The page is not where the recording was: another site, or no element with its context. */\nexport class Drifted extends Schema.TaggedError<Drifted>()(\"Drifted\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\nconst { holds, normalize } = match();\nconst fields = [\"row\", \"column\", \"label\", \"heading\"] as const;\n\nconst said = (context: SubjectContext) =>\n Object.values(context).filter((value) => typeof value === \"string\");\n\n/** How an element repeats one recorded field: 2 read the same, 1 found as words, 0 not at all. */\nconst repeats = (field: (typeof fields)[number], value: string, context: SubjectContext) => {\n const own = context[field];\n\n if (own !== undefined && normalize(own) === normalize(value)) return 2;\n if (field === \"column\") return own === undefined ? 1 : 0;\n if (field === \"row\")\n return own === undefined &&\n said(context).some((other) => `${normalize(other)} `.startsWith(`${normalize(value)} `))\n ? 1\n : 0;\n\n return said(context).some((other) => holds(other, value)) ? 1 : 0;\n};\n\nexport const choose = (\n subject: Subject,\n found: ReadonlyArray<Found>,\n): Effect.Effect<Found, Missing | Ambiguous | Drifted> => {\n // `find` matched the role; a subject without one only matches elements without one.\n const named = found.filter((one) => subject.role !== null || one.subject.role === null);\n\n const kept = named.flatMap((one) => {\n let score = 0;\n\n for (const field of fields) {\n const value = subject.context[field];\n const repeated = value === undefined ? 0 : repeats(field, value, one.subject.context);\n\n if (value !== undefined && repeated === 0) return [];\n score += repeated;\n }\n\n return [{ one, score }];\n });\n\n const best = Math.max(...kept.map(({ score }) => score));\n const top = kept.filter(({ score }) => score === best);\n const [only] = top;\n\n if (named.length === 0) return Effect.fail(new Missing());\n if (kept.length === 0)\n return Effect.fail(new Drifted({ detail: \"no element named so repeats the recorded context\" }));\n\n return top.length === 1 && only !== undefined\n ? Effect.succeed(only.one)\n : Effect.fail(new Ambiguous({ count: top.length }));\n};\n","/**\n * A walk recorded once from a page's events and replayed later, on a fresh page, with no model\n * call.\n *\n * `fromEvents` keeps one page's completed actions with their subjects and the options they were\n * given, its navigations with both the address asked for and the one reached, and what was typed\n * as a named input slot, never the text itself. A navigation to an address whose credentials the\n * events withheld is an input slot too, so replay goes where the caller says, or nowhere, and\n * never to the address without them. `replay` takes the steps in turn. Before a step\n * that acts on the page, it waits for `Page.ready`; then it finds the one element the step's\n * subject names, by role and name among those that repeat all its recorded context, and acts on\n * it. Text typed into focus was typed into the focused field, which replay finds the same way, and\n * text typed into a secret field goes into nothing else. An element step never falls back to\n * coordinates, and a step recorded at a point acts there only while the element found for it still\n * covers that point. Replay stops at the first step it cannot take and says why: `Missing`,\n * `Ambiguous`, `Drifted`, or the step's own `BrowserError`. Nothing is replayed automatically, and\n * a plan of another version, such as one 0.2 stored, does not decode.\n *\n * @since 0.3.0\n */\nimport { Duration, Effect, Schedule, Schema } from \"effect\";\n\nimport { BrowserError, InvalidRequest } from \"./BrowserError.ts\";\nimport {\n type Action,\n ActionOptions,\n Box,\n type BrowserEvent,\n type Navigated,\n Subject,\n} from \"./BrowserEvent.ts\";\nimport { undispatched } from \"./internal/page/context.ts\";\nimport { Ambiguous, choose, Drifted, Missing } from \"./internal/reading/choose.ts\";\nimport {\n type Found,\n type Page,\n type Point,\n type ReadyOptions,\n redacted,\n type Target,\n} from \"./Page.ts\";\n\nexport { Ambiguous, Drifted, Missing } from \"./internal/reading/choose.ts\";\n\nconst ViewportPoint = Schema.Struct({ x: Schema.Finite, y: Schema.Finite });\n\nexport const StepAction = Schema.Literals([\n \"navigate\",\n \"back\",\n \"reload\",\n \"click\",\n \"hover\",\n \"drag\",\n \"type\",\n \"press\",\n \"scroll\",\n \"select\",\n]);\n\nexport type StepAction = typeof StepAction.Type;\n\n/** One recorded step: the action, what it acted on, and what else it was asked. */\nexport class Step extends Schema.Class<Step>(\"effect-browser/Plan/Step\")({\n action: StepAction,\n /** Where the page was once the step was done, as recorded; empty when unknown. */\n url: Schema.String,\n /** A navigation's requested address, or the keys a press pressed. */\n target: Schema.optional(Schema.String),\n subject: Schema.optional(Subject),\n /** Where a drag ended. */\n to: Schema.optional(Subject),\n /** The viewport point a step aimed at, when it aimed at a point, and a drag's end point. */\n point: Schema.optional(ViewportPoint),\n toPoint: Schema.optional(ViewportPoint),\n /** The box of what a step aimed at by point found there, so replay aims at the same place in it. */\n box: Schema.optional(Box),\n /**\n * The input slot whose text a `type` step types, or whose address a `navigate` step goes to, as\n * its recorded address had a credential withheld.\n */\n input: Schema.optional(Schema.String),\n /** The slot's text went into a secret field, so replay types it into nothing else. */\n secret: Schema.optional(Schema.Literal(true)),\n options: Schema.optional(ActionOptions),\n}) {}\n\nexport class Plan extends Schema.Class<Plan>(\"effect-browser/Plan\")({\n version: Schema.Literal(1),\n steps: Schema.Array(Step),\n}) {\n /** The input slots its steps type, in order; replay needs text for each. */\n get inputs(): ReadonlyArray<string> {\n return this.steps.flatMap((step) => (step.input === undefined ? [] : [step.input]));\n }\n}\n\n/** Where replay stopped, counting steps from 0, and why. */\nexport class ReplayError extends Schema.TaggedError<ReplayError>()(\"ReplayError\", {\n step: Schema.Int,\n reason: Schema.Union([Missing, Ambiguous, Drifted, BrowserError]),\n}) {\n override get message() {\n return `step ${this.step} failed: ${this.reason.message}`;\n }\n}\n\nconst isStep = (event: BrowserEvent): event is Action & { readonly name: StepAction } =>\n event._tag === \"Action\" &&\n event.ok &&\n (StepAction.literals as ReadonlyArray<string>).includes(event.name);\n\n// A point target reads `x,y`, as `{\"x\":…,\"y\":…}` inside a drag's; a ref reads like e12.\nconst pointIn = (target: string | undefined): Point | undefined => {\n const parsed = /^\\{?(?:\"x\":)?(-?[\\d.]+),(?:\"y\":)?(-?[\\d.]+)\\}?$/.exec(target ?? \"\");\n\n return parsed === null ? undefined : { x: Number(parsed[1]), y: Number(parsed[2]) };\n};\n\n/** A slot named for the field it fills, such as `email`, unique within the plan. */\nconst slotFor = (taken: Set<string>, field: string | undefined) => {\n const base =\n field\n ?.toLowerCase()\n .match(/[\\p{L}\\p{N}]+/gu)\n ?.join(\"-\") ?? \"input\";\n\n let slot = base;\n\n for (let count = 2; taken.has(slot); count++) slot = `${base}-${count}`;\n taken.add(slot);\n\n return slot;\n};\n\n/**\n * The walk one page's events record: the completed actions of the page that acted first, in\n * order. `page.recentEvents` gives them, or `browser.events()` read until the walk ends.\n */\nexport const fromEvents = (events: Iterable<BrowserEvent>): Plan => {\n const all = Array.from(events);\n const page = all.find((event) => event._tag === \"Action\")?.page;\n const mine = all.filter((event) => \"page\" in event && event.page === page);\n const steps = mine.filter(isStep);\n const opened = mine.find((event) => event._tag === \"PageOpened\")?.url ?? \"\";\n const moves = mine.filter((event): event is Navigated => event._tag === \"Navigated\");\n const taken = new Set<string>();\n\n // Where the page was just before a time: the last address it reached by then.\n const before = (time: number) => moves.findLast((move) => move.at <= time)?.url ?? opened;\n\n return new Plan({\n version: 1,\n steps: steps.map((action, index) => {\n const drag = action.name === \"drag\" ? (action.target ?? \"\").split(\" -> \") : undefined;\n const point = pointIn(drag === undefined ? action.target : drag[0]);\n\n // Only a navigation and a press keep their target: an address, or keys.\n return new Step({\n action: action.name,\n url: before(steps[index + 1]?.startedAt ?? Number.POSITIVE_INFINITY),\n target: action.name === \"navigate\" || action.name === \"press\" ? action.target : undefined,\n subject: action.subject,\n to: action.to,\n point,\n toPoint: pointIn(drag?.[1]),\n box: point === undefined ? undefined : action.box,\n input:\n action.name === \"type\"\n ? slotFor(taken, action.subject?.name)\n : action.name === \"navigate\" && action.target?.includes(redacted) === true\n ? slotFor(taken, \"address\")\n : undefined,\n secret: action.name === \"type\" && action.text === redacted ? true : undefined,\n options: action.options,\n });\n }),\n });\n};\n\n/**\n * The one element on the page that a subject names, by role, name and context (see `Plan`),\n * looking again for two seconds while the page may still be drawing it.\n */\nexport const locate = (page: Page, subject: Subject) =>\n page\n .find({\n name: subject.name,\n scope: \"document\",\n ...(subject.role === null ? {} : { role: subject.role }),\n })\n .pipe(\n Effect.flatMap((found) => choose(subject, found)),\n Effect.retry({\n schedule: Schedule.spaced(Duration.millis(250)),\n times: 8,\n while: (error) => error._tag !== \"BrowserError\",\n }),\n Effect.withSpan(\"Plan.locate\", { attributes: { role: subject.role ?? \"\" } }),\n );\n\nconst invalid = (detail: string) => undispatched(\"replay\", new InvalidRequest({ detail }));\n\n/**\n * Where the step acts: the element its subject names, or, for a step recorded at a point, the same\n * place within that element, as long as a press there reaches that element and not one over it.\n * An element out of view is first brought into it, by hovering it, as a press by ref would be.\n */\nconst aim = (page: Page, subject: Subject | undefined, point?: Point, recorded?: Box) => {\n if (subject === undefined) return Effect.fail(invalid(\"the step names no element\"));\n if (point === undefined) return Effect.map(locate(page, subject), ({ ref }): Target => ref);\n\n const reach = ({ ref, box }: Found) => {\n const at =\n recorded === undefined || recorded.width <= 0 || recorded.height <= 0\n ? point\n : {\n x: box.x + ((point.x - recorded.x) / recorded.width) * box.width,\n y: box.y + ((point.y - recorded.y) / recorded.height) * box.height,\n };\n\n return Effect.map(page.find({ at }), ([reached]) => (reached?.ref === ref ? at : undefined));\n };\n\n const missed = new Drifted({ detail: \"a press at the subject's point reaches another element\" });\n\n return locate(page, subject).pipe(\n Effect.flatMap((found) =>\n Effect.flatMap(reach(found), (at) =>\n at !== undefined\n ? Effect.succeed<Target>(at)\n : page.hover(found.ref).pipe(\n Effect.andThen(locate(page, subject)),\n Effect.flatMap(reach),\n Effect.flatMap((again) =>\n again === undefined ? Effect.fail(missed) : Effect.succeed<Target>(again),\n ),\n ),\n ),\n ),\n );\n};\n\nconst take = (\n page: Page,\n step: Step,\n text: string,\n): Effect.Effect<void, Missing | Ambiguous | Drifted | BrowserError> => {\n const options = step.options ?? {};\n\n switch (step.action) {\n case \"navigate\":\n return page.goto(step.input === undefined ? (step.target ?? step.url) : text);\n case \"back\":\n return page.back;\n case \"reload\":\n return page.reload;\n case \"press\":\n return page.press(step.target ?? \"\", options);\n case \"click\":\n return aim(page, step.subject, step.point, step.box).pipe(\n Effect.flatMap((at) => page.click(at, options)),\n Effect.asVoid,\n );\n case \"hover\":\n return Effect.flatMap(aim(page, step.subject, step.point, step.box), page.hover);\n case \"drag\":\n return Effect.all([\n aim(page, step.subject, step.point),\n aim(page, step.to, step.toPoint),\n ]).pipe(Effect.flatMap(([from, to]) => page.drag(from, to)));\n case \"select\":\n return aim(page, step.subject).pipe(\n Effect.flatMap((at) => page.select(typeof at === \"string\" ? at : \"\", options.values ?? [])),\n Effect.asVoid,\n );\n // A scroll or a typing step may name no element: the page, or whatever has focus.\n case \"scroll\":\n return step.subject === undefined\n ? page.scroll(options)\n : Effect.flatMap(aim(page, step.subject, step.point, step.box), (at) =>\n page.scroll({ ...options, at }),\n );\n case \"type\":\n return step.subject === undefined\n ? page.type(text, { ...options, secret: step.secret })\n : Effect.flatMap(aim(page, step.subject), (into) =>\n page.type(text, {\n ...options,\n into: typeof into === \"string\" ? into : undefined,\n secret: step.secret,\n }),\n );\n }\n};\n\nexport interface ReplayOptions {\n /** How each step waits for the page first, as `Page.ready` takes it, or false not to wait. */\n readonly settle?: ReadyOptions | false | undefined;\n /** The text for each of the plan's input slots. */\n readonly inputs?: Readonly<Record<string, string>> | undefined;\n}\n\n/** Take a plan's steps on a page, in order, stopping at the first that cannot be taken. */\nexport const replay = (page: Page, plan: Plan, options: ReplayOptions = {}) =>\n Effect.gen(function* () {\n const inputs = options.inputs ?? {};\n const settle = options.settle === false ? Effect.void : page.ready(options.settle);\n\n const at =\n (step: number) =>\n <A>(effect: Effect.Effect<A, Missing | Ambiguous | Drifted | BrowserError>) =>\n effect.pipe(Effect.mapError((reason) => new ReplayError({ step, reason })));\n\n // A step arrived where the recording did, on the same site; a moved path is not drift.\n const arrived = (index: number) => {\n const recorded = plan.steps[index]?.url ?? \"\";\n\n return page.url.pipe(\n Effect.flatMap((url) =>\n recorded === \"\" || URL.parse(url)?.origin === URL.parse(recorded)?.origin\n ? Effect.void\n : Effect.fail(\n new Drifted({ detail: `the page is at ${url}, not on ${recorded}'s site` }),\n ),\n ),\n at(index),\n );\n };\n\n const unfilled = plan.steps.findIndex(\n (step) => step.input !== undefined && inputs[step.input] === undefined,\n );\n\n if (unfilled !== -1)\n return yield* at(unfilled)(\n Effect.fail(invalid(`no text for the input \"${plan.steps[unfilled]?.input ?? \"\"}\"`)),\n );\n for (const [index, step] of plan.steps.entries()) {\n if (step.action !== \"navigate\" && step.action !== \"back\" && step.action !== \"reload\")\n yield* at(index)(settle);\n if (index > 0) yield* arrived(index - 1);\n yield* at(index)(\n take(page, step, step.input === undefined ? \"\" : (inputs[step.input] ?? \"\")),\n );\n }\n if (plan.steps.length > 0) {\n yield* at(plan.steps.length - 1)(settle);\n yield* arrived(plan.steps.length - 1);\n }\n }).pipe(Effect.withSpan(\"Plan.replay\", { attributes: { steps: plan.steps.length } }));\n"],"mappings":"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"}
|
package/dist/Plan.d.mts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
export {};
|
|
1
|
+
import { a as Step, c as locate, d as Drifted, f as Missing, i as ReplayOptions, l as replay, o as StepAction, r as ReplayError, s as fromEvents, t as Plan, u as Ambiguous } from "./Plan-CNNOU1Tx.mjs";
|
|
2
|
+
export { Ambiguous, Drifted, Missing, Plan, ReplayError, ReplayOptions, Step, StepAction, fromEvents, locate, replay };
|
package/dist/Plan.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export {
|
|
1
|
+
import { a as StepAction, c as replay, d as Missing, i as Step, l as Ambiguous, o as fromEvents, r as ReplayError, s as locate, t as Plan, u as Drifted } from "./Plan-Dtrb-8im.mjs";
|
|
2
|
+
export { Ambiguous, Drifted, Missing, Plan, ReplayError, Step, StepAction, fromEvents, locate, replay };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { c as InputGuard, i as Fact, l as InputRequest } from "./Page-DYTdVhYa.mjs";
|
|
2
|
+
import { Context, Duration, Effect, Schema } from "effect";
|
|
3
|
+
import { AiError, DecisionModel, LanguageModel } from "effect/ai";
|
|
4
|
+
declare namespace Policy_d_exports {
|
|
5
|
+
export { Judge, Judgement, Options, Risk, Task, decider, escalate, make, reviewer };
|
|
6
|
+
}
|
|
7
|
+
/** What an input means. Only a judge reading the evidence can recognise these. */
|
|
8
|
+
export declare const Risk: Schema.Literals<readonly ["financial", "account", "access", "deletion", "communication", "secret"]>;
|
|
9
|
+
export type Risk = typeof Risk.Type;
|
|
10
|
+
declare const Judgement_base: Schema.Class<Judgement, Schema.Struct<{
|
|
11
|
+
/** For each risk, the probability that the input does it. */
|
|
12
|
+
readonly risks: Schema.Struct<{
|
|
13
|
+
readonly financial: Schema.Finite;
|
|
14
|
+
readonly account: Schema.Finite;
|
|
15
|
+
readonly access: Schema.Finite;
|
|
16
|
+
readonly deletion: Schema.Finite;
|
|
17
|
+
readonly communication: Schema.Finite;
|
|
18
|
+
readonly secret: Schema.Finite;
|
|
19
|
+
}>;
|
|
20
|
+
/** The probability that the user's task asks for what the input does; absent without a task. */
|
|
21
|
+
readonly requested: Schema.optional<Schema.Finite>;
|
|
22
|
+
/** Why, in a sentence, when the judge says. */
|
|
23
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
24
|
+
}>, {}>;
|
|
25
|
+
/** A judge's reading of one input. */
|
|
26
|
+
export declare class Judgement extends Judgement_base {}
|
|
27
|
+
/**
|
|
28
|
+
* The user's task: what authorizes a risky input. `Agent.run` provides its task to every input it
|
|
29
|
+
* makes; provide one around other calls with `Effect.provideService`.
|
|
30
|
+
*/
|
|
31
|
+
export declare const Task: Context.Reference<string | undefined>;
|
|
32
|
+
/** Reads what an input means, with the user's task from `Task`. */
|
|
33
|
+
export type Judge = (request: InputRequest) => Effect.Effect<Judgement, AiError.AiError>;
|
|
34
|
+
/**
|
|
35
|
+
* A judge that asks the `LanguageModel` in context for a structured review. `rules` adds the
|
|
36
|
+
* user's own policy in plain words, such as "Purchases under $20 are fine".
|
|
37
|
+
*/
|
|
38
|
+
export declare const reviewer: (options?: {
|
|
39
|
+
readonly rules?: string | undefined;
|
|
40
|
+
} | undefined) => Effect.Effect<(request: InputRequest) => Effect.Effect<Judgement, AiError.AiError, never>, never, LanguageModel.LanguageModel>;
|
|
41
|
+
/**
|
|
42
|
+
* A judge that answers one probability per risk from the `DecisionModel` in context, all in one
|
|
43
|
+
* call. Jev is one: `TypeSafeDecisionModel.layer({ model: "jev-1.13.0" })` from
|
|
44
|
+
* `@effect/ai-typesafe` provides it.
|
|
45
|
+
*/
|
|
46
|
+
export declare const decider: Effect.Effect<Judge, never, DecisionModel.DecisionModel>;
|
|
47
|
+
/**
|
|
48
|
+
* `first`'s judgement, or `second`'s when `first` is unsure: a risk between 0.2 and 0.8, or a
|
|
49
|
+
* likely risk whose request is. A confident mistake by `first` stands.
|
|
50
|
+
*/
|
|
51
|
+
export declare const escalate: (first: Judge, second: Judge) => Judge;
|
|
52
|
+
export interface Options {
|
|
53
|
+
/** Reads what an input means. Without one, only origins and facts decide. */
|
|
54
|
+
readonly judge?: Judge | undefined;
|
|
55
|
+
/** Risks an input may carry only when the task asks for them. Defaults to all of them. */
|
|
56
|
+
readonly risks?: ReadonlyArray<Risk> | undefined;
|
|
57
|
+
/** Facts that deny an input whatever the task, such as `["upload"]`. Defaults to none. */
|
|
58
|
+
readonly deny?: ReadonlyArray<Fact> | undefined;
|
|
59
|
+
/** Origins input may act on and go to, such as `"https://shop.example"`. Defaults to any. */
|
|
60
|
+
readonly origins?: ReadonlyArray<string> | undefined;
|
|
61
|
+
/** The probability from which a judge's reading counts. Defaults to 0.5. */
|
|
62
|
+
readonly threshold?: number | undefined;
|
|
63
|
+
/** How long the judge may take before the input counts as unjudged. Defaults to 30 seconds. */
|
|
64
|
+
readonly timeout?: Duration.Input | undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A guard for a browser with nobody to ask. It allows hovering and scrolling. Otherwise it denies
|
|
68
|
+
* input on a page or towards a destination outside `origins`, input with a fact in `deny`, and,
|
|
69
|
+
* when a judge finds a risk, input the task does not ask for. An input the judge could not judge
|
|
70
|
+
* in time is denied when it has any fact, with the judge's failure as the denial's cause, and
|
|
71
|
+
* allowed when structure shows nothing.
|
|
72
|
+
*/
|
|
73
|
+
export declare const make: (options?: Options) => InputGuard;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { Policy_d_exports as t };
|
|
76
|
+
//# sourceMappingURL=Policy.d.mts.map
|