@effect-uai/browser 0.9.0
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/LICENSE +21 -0
- package/dist/Connect.d.mts +31 -0
- package/dist/Connect.d.mts.map +1 -0
- package/dist/Connect.mjs +122 -0
- package/dist/Connect.mjs.map +1 -0
- package/dist/cdp-DZgualSC.d.mts +27027 -0
- package/dist/cdp-DZgualSC.d.mts.map +1 -0
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/internal/cdp.d.mts +2 -0
- package/dist/internal/cdp.mjs +123 -0
- package/dist/internal/cdp.mjs.map +1 -0
- package/dist/internal/injected.d.mts +68 -0
- package/dist/internal/injected.d.mts.map +1 -0
- package/dist/internal/injected.mjs +341 -0
- package/dist/internal/injected.mjs.map +1 -0
- package/dist/internal/session.d.mts +19 -0
- package/dist/internal/session.d.mts.map +1 -0
- package/dist/internal/session.mjs +322 -0
- package/dist/internal/session.mjs.map +1 -0
- package/package.json +61 -0
- package/src/Connect.ts +180 -0
- package/src/index.ts +1 -0
- package/src/internal/cdp.ts +203 -0
- package/src/internal/injected.ts +413 -0
- package/src/internal/session.ts +606 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Cause, Data, Deferred, Effect, HashMap, Option, PubSub, Ref, Schema } from "effect";
|
|
2
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
3
|
+
import * as JSONL from "@effect-uai/core/JSONL";
|
|
4
|
+
//#region src/internal/cdp.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal raw Chrome DevTools Protocol client over a WebSocket.
|
|
7
|
+
*
|
|
8
|
+
* CDP is JSON-RPC-shaped: commands are `{ id, method, params, sessionId? }`,
|
|
9
|
+
* replies are `{ id, result }` or `{ id, error }`, and events are `{ method,
|
|
10
|
+
* params }` with no id. Commands are correlated via a pending-`Deferred`
|
|
11
|
+
* map; events are demuxed into a `PubSub` that callers subscribe to (e.g.
|
|
12
|
+
* navigation waits on `Page.loadEventFired` instead of polling).
|
|
13
|
+
*
|
|
14
|
+
* Commands are typed against the official `devtools-protocol` schema (types
|
|
15
|
+
* only, zero runtime): `send("Page.navigate", params)` gets its params and
|
|
16
|
+
* return type from the protocol. The transport rides Effect's native
|
|
17
|
+
* `WebSocket` (via `layerWebSocketConstructorGlobal`), so no `ws` dependency is
|
|
18
|
+
* needed on modern runtimes; CDP connect uses a bare `ws://` URL with no custom
|
|
19
|
+
* headers.
|
|
20
|
+
*
|
|
21
|
+
* Failures are transport-shaped ({@link CdpError}), not the public
|
|
22
|
+
* `BrowserError`: the transport cannot know which browser verb a command was
|
|
23
|
+
* serving, so the session layer maps `CdpError` onto the typed `BrowserError`
|
|
24
|
+
* using the operation it knows.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Transport-level failure. `reply` is the browser rejecting a command,
|
|
28
|
+
* `write` is a failed socket send, `closed` is the connection going away
|
|
29
|
+
* (or never opening; the socket connects lazily) with the command still in
|
|
30
|
+
* flight. `method` names the CDP command that was in flight.
|
|
31
|
+
*/
|
|
32
|
+
var CdpError = class extends Data.TaggedError("CdpError") {};
|
|
33
|
+
const CdpMessage = Schema.Struct({
|
|
34
|
+
id: Schema.optional(Schema.Number),
|
|
35
|
+
result: Schema.optional(Schema.Unknown),
|
|
36
|
+
error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),
|
|
37
|
+
method: Schema.optional(Schema.String),
|
|
38
|
+
params: Schema.optional(Schema.Unknown),
|
|
39
|
+
sessionId: Schema.optional(Schema.String)
|
|
40
|
+
});
|
|
41
|
+
const decodeMessage = Schema.decodeUnknownEffect(CdpMessage);
|
|
42
|
+
const frame = (id, method, params, sessionId) => JSON.stringify(sessionId === void 0 ? {
|
|
43
|
+
id,
|
|
44
|
+
method,
|
|
45
|
+
params
|
|
46
|
+
} : {
|
|
47
|
+
id,
|
|
48
|
+
method,
|
|
49
|
+
params,
|
|
50
|
+
sessionId
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Open a scoped CDP connection to a browser-level WebSocket endpoint. The
|
|
54
|
+
* socket and its reader fiber are torn down on scope close. The underlying
|
|
55
|
+
* socket connects lazily, so a bad endpoint surfaces on the first `send` as
|
|
56
|
+
* a `CdpError` of kind `closed` (carrying the socket failure on `raw`), not
|
|
57
|
+
* here.
|
|
58
|
+
*/
|
|
59
|
+
const openCdp = (endpoint) => Effect.gen(function* () {
|
|
60
|
+
const socket = yield* Socket.makeWebSocket(endpoint, { closeCodeIsError: (code) => code !== 1e3 && code !== 1001 && code !== 1005 }).pipe(Effect.provide(Socket.layerWebSocketConstructorGlobal));
|
|
61
|
+
const pending = yield* Ref.make(HashMap.empty());
|
|
62
|
+
const counter = yield* Ref.make(0);
|
|
63
|
+
const events = yield* PubSub.unbounded();
|
|
64
|
+
const closeCause = yield* Ref.make(void 0);
|
|
65
|
+
const write = yield* socket.writer;
|
|
66
|
+
const dispatch = (raw) => Effect.gen(function* () {
|
|
67
|
+
const json = yield* JSONL.parseSafe(raw);
|
|
68
|
+
if (json === void 0) return;
|
|
69
|
+
const message = yield* decodeMessage(json).pipe(Effect.option);
|
|
70
|
+
if (Option.isNone(message)) return;
|
|
71
|
+
if (message.value.id === void 0) {
|
|
72
|
+
if (message.value.method !== void 0) yield* PubSub.publish(events, {
|
|
73
|
+
method: message.value.method,
|
|
74
|
+
params: message.value.params,
|
|
75
|
+
sessionId: message.value.sessionId
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const id = message.value.id;
|
|
80
|
+
const taken = yield* Ref.modify(pending, (m) => [HashMap.get(m, id), HashMap.remove(m, id)]);
|
|
81
|
+
if (Option.isNone(taken)) return;
|
|
82
|
+
yield* message.value.error === void 0 ? Deferred.succeed(taken.value.deferred, message.value.result ?? {}) : Deferred.fail(taken.value.deferred, new CdpError({
|
|
83
|
+
kind: "reply",
|
|
84
|
+
method: taken.value.method,
|
|
85
|
+
reason: message.value.error.message ?? "CDP error",
|
|
86
|
+
raw: message.value.error
|
|
87
|
+
}));
|
|
88
|
+
});
|
|
89
|
+
const failPending = Effect.gen(function* () {
|
|
90
|
+
const raw = yield* Ref.get(closeCause);
|
|
91
|
+
const taken = yield* Ref.getAndSet(pending, HashMap.empty());
|
|
92
|
+
yield* Effect.forEach(HashMap.values(taken), ({ deferred, method }) => Deferred.fail(deferred, new CdpError({
|
|
93
|
+
kind: "closed",
|
|
94
|
+
method,
|
|
95
|
+
reason: "connection closed",
|
|
96
|
+
raw
|
|
97
|
+
})));
|
|
98
|
+
});
|
|
99
|
+
yield* socket.runString(dispatch).pipe(Effect.tapCause((cause) => Ref.set(closeCause, Cause.squash(cause))), Effect.ensuring(failPending), Effect.forkScoped);
|
|
100
|
+
const send = (method, params, sessionId) => Effect.gen(function* () {
|
|
101
|
+
const id = yield* Ref.updateAndGet(counter, (n) => n + 1);
|
|
102
|
+
const deferred = yield* Deferred.make();
|
|
103
|
+
const entry = {
|
|
104
|
+
method,
|
|
105
|
+
deferred
|
|
106
|
+
};
|
|
107
|
+
yield* Ref.update(pending, HashMap.set(id, entry));
|
|
108
|
+
return yield* write(frame(id, method, params ?? {}, sessionId)).pipe(Effect.mapError((raw) => new CdpError({
|
|
109
|
+
kind: "write",
|
|
110
|
+
method,
|
|
111
|
+
reason: "socket write failed",
|
|
112
|
+
raw
|
|
113
|
+
})), Effect.flatMap(() => Deferred.await(deferred)), Effect.ensuring(Ref.update(pending, HashMap.remove(id))));
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
send,
|
|
117
|
+
subscribe: PubSub.subscribe(events)
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
//#endregion
|
|
121
|
+
export { CdpError, openCdp };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=cdp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdp.mjs","names":[],"sources":["../../src/internal/cdp.ts"],"sourcesContent":["/**\n * Minimal raw Chrome DevTools Protocol client over a WebSocket.\n *\n * CDP is JSON-RPC-shaped: commands are `{ id, method, params, sessionId? }`,\n * replies are `{ id, result }` or `{ id, error }`, and events are `{ method,\n * params }` with no id. Commands are correlated via a pending-`Deferred`\n * map; events are demuxed into a `PubSub` that callers subscribe to (e.g.\n * navigation waits on `Page.loadEventFired` instead of polling).\n *\n * Commands are typed against the official `devtools-protocol` schema (types\n * only, zero runtime): `send(\"Page.navigate\", params)` gets its params and\n * return type from the protocol. The transport rides Effect's native\n * `WebSocket` (via `layerWebSocketConstructorGlobal`), so no `ws` dependency is\n * needed on modern runtimes; CDP connect uses a bare `ws://` URL with no custom\n * headers.\n *\n * Failures are transport-shaped ({@link CdpError}), not the public\n * `BrowserError`: the transport cannot know which browser verb a command was\n * serving, so the session layer maps `CdpError` onto the typed `BrowserError`\n * using the operation it knows.\n */\nimport {\n Cause,\n Data,\n Deferred,\n Effect,\n HashMap,\n Option,\n PubSub,\n Ref,\n Schema,\n type Scope,\n} from \"effect\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport type { ProtocolMapping } from \"devtools-protocol/types/protocol-mapping.js\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\n\ntype Commands = ProtocolMapping.Commands\n\nexport type CdpMethod = keyof Commands\nexport type CdpParams<M extends CdpMethod> = Commands[M][\"paramsType\"][0]\nexport type CdpReturn<M extends CdpMethod> = Commands[M][\"returnType\"]\n\n/**\n * Transport-level failure. `reply` is the browser rejecting a command,\n * `write` is a failed socket send, `closed` is the connection going away\n * (or never opening; the socket connects lazily) with the command still in\n * flight. `method` names the CDP command that was in flight.\n */\nexport class CdpError extends Data.TaggedError(\"CdpError\")<{\n kind: \"reply\" | \"write\" | \"closed\"\n method?: string\n reason?: string\n raw?: unknown\n}> {}\n\n/** An id-less CDP message: an event emitted by the browser. */\nexport type CdpEvent = {\n readonly method: string\n readonly params: unknown\n readonly sessionId: string | undefined\n}\n\nexport type Cdp = {\n readonly send: <M extends CdpMethod>(\n method: M,\n params?: CdpParams<M>,\n sessionId?: string,\n ) => Effect.Effect<CdpReturn<M>, CdpError>\n /**\n * Subscribe to browser events. Subscribe BEFORE issuing the command\n * whose event you await, or the event can slip past. Subscription\n * lifetime is the scope.\n */\n readonly subscribe: Effect.Effect<PubSub.Subscription<CdpEvent>, never, Scope.Scope>\n}\n\n// Replies are heterogeneous per command; the map is keyed by request id and the\n// awaited value is narrowed back to `CdpReturn<M>` at the `send` boundary. The\n// method rides along so a failure can name the command it was for.\ntype Pending = {\n readonly method: string\n readonly deferred: Deferred.Deferred<unknown, CdpError>\n}\n\nconst CdpMessage = Schema.Struct({\n id: Schema.optional(Schema.Number),\n result: Schema.optional(Schema.Unknown),\n error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),\n method: Schema.optional(Schema.String),\n params: Schema.optional(Schema.Unknown),\n sessionId: Schema.optional(Schema.String),\n})\nconst decodeMessage = Schema.decodeUnknownEffect(CdpMessage)\n\nconst frame = (\n id: number,\n method: string,\n params: unknown,\n sessionId: string | undefined,\n): string =>\n JSON.stringify(\n sessionId === undefined ? { id, method, params } : { id, method, params, sessionId },\n )\n\n/**\n * Open a scoped CDP connection to a browser-level WebSocket endpoint. The\n * socket and its reader fiber are torn down on scope close. The underlying\n * socket connects lazily, so a bad endpoint surfaces on the first `send` as\n * a `CdpError` of kind `closed` (carrying the socket failure on `raw`), not\n * here.\n */\nexport const openCdp = (endpoint: string): Effect.Effect<Cdp, never, Scope.Scope> =>\n Effect.gen(function* () {\n const socket = yield* Socket.makeWebSocket(endpoint, {\n // Effect's Socket treats every close code as an error; whitelist the\n // standard clean-close codes.\n closeCodeIsError: (code) => code !== 1000 && code !== 1001 && code !== 1005,\n }).pipe(Effect.provide(Socket.layerWebSocketConstructorGlobal))\n\n const pending = yield* Ref.make(HashMap.empty<number, Pending>())\n const counter = yield* Ref.make(0)\n const events = yield* PubSub.unbounded<CdpEvent>()\n // Why the socket died (failed to open, dropped, dirty close); carried on\n // the `closed` errors handed to in-flight commands.\n const closeCause = yield* Ref.make<unknown>(undefined)\n const write = yield* socket.writer\n\n const dispatch = (raw: string): Effect.Effect<void> =>\n Effect.gen(function* () {\n const json = yield* JSONL.parseSafe(raw)\n if (json === undefined) return\n const message = yield* decodeMessage(json).pipe(Effect.option)\n if (Option.isNone(message)) return\n if (message.value.id === undefined) {\n if (message.value.method !== undefined) {\n yield* PubSub.publish(events, {\n method: message.value.method,\n params: message.value.params,\n sessionId: message.value.sessionId,\n })\n }\n return\n }\n const id = message.value.id\n const taken = yield* Ref.modify(pending, (m) => [HashMap.get(m, id), HashMap.remove(m, id)])\n if (Option.isNone(taken)) return\n yield* message.value.error === undefined\n ? Deferred.succeed(taken.value.deferred, message.value.result ?? {})\n : Deferred.fail(\n taken.value.deferred,\n new CdpError({\n kind: \"reply\",\n method: taken.value.method,\n reason: message.value.error.message ?? \"CDP error\",\n raw: message.value.error,\n }),\n )\n })\n\n // Reader fiber: fail every still-pending command when the socket closes so\n // callers don't hang.\n const failPending = Effect.gen(function* () {\n const raw = yield* Ref.get(closeCause)\n const taken = yield* Ref.getAndSet(pending, HashMap.empty())\n yield* Effect.forEach(HashMap.values(taken), ({ deferred, method }) =>\n Deferred.fail(\n deferred,\n new CdpError({ kind: \"closed\", method, reason: \"connection closed\", raw }),\n ),\n )\n })\n yield* socket.runString(dispatch).pipe(\n Effect.tapCause((cause) => Ref.set(closeCause, Cause.squash(cause))),\n Effect.ensuring(failPending),\n Effect.forkScoped,\n )\n\n const send = <M extends CdpMethod>(\n method: M,\n params?: CdpParams<M>,\n sessionId?: string,\n ): Effect.Effect<CdpReturn<M>, CdpError> =>\n Effect.gen(function* () {\n const id = yield* Ref.updateAndGet(counter, (n) => n + 1)\n const deferred = yield* Deferred.make<unknown, CdpError>()\n const entry: Pending = { method, deferred }\n yield* Ref.update(pending, HashMap.set(id, entry))\n // `ensuring` reclaims the entry on write failure and on caller\n // interruption; on the success path dispatch has already removed it.\n const result = yield* write(frame(id, method, params ?? {}, sessionId)).pipe(\n Effect.mapError(\n (raw) => new CdpError({ kind: \"write\", method, reason: \"socket write failed\", raw }),\n ),\n Effect.flatMap(() => Deferred.await(deferred)),\n Effect.ensuring(Ref.update(pending, HashMap.remove(id))),\n )\n return result as CdpReturn<M>\n })\n\n const cdp: Cdp = { send, subscribe: PubSub.subscribe(events) }\n return cdp\n })\n"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/injected.d.ts
|
|
4
|
+
/** Viewport-relative center of an element, for `Input.dispatch*` events. */
|
|
5
|
+
declare const InjectedPoint: Schema.Struct<{
|
|
6
|
+
readonly x: Schema.Number;
|
|
7
|
+
readonly y: Schema.Number;
|
|
8
|
+
}>;
|
|
9
|
+
type InjectedPoint = typeof InjectedPoint.Type;
|
|
10
|
+
/** Element box: viewport-relative from `query`, page coords from `pageBox`. */
|
|
11
|
+
declare const InjectedBox: Schema.Struct<{
|
|
12
|
+
readonly x: Schema.Number;
|
|
13
|
+
readonly y: Schema.Number;
|
|
14
|
+
readonly width: Schema.Number;
|
|
15
|
+
readonly height: Schema.Number;
|
|
16
|
+
}>;
|
|
17
|
+
type InjectedBox = typeof InjectedBox.Type;
|
|
18
|
+
declare const InjectedSize: Schema.Struct<{
|
|
19
|
+
readonly width: Schema.Number;
|
|
20
|
+
readonly height: Schema.Number;
|
|
21
|
+
}>;
|
|
22
|
+
type InjectedSize = typeof InjectedSize.Type;
|
|
23
|
+
/** Element data returned by `query`; serializable, never a live handle. */
|
|
24
|
+
declare const InjectedElementInfo: Schema.Struct<{
|
|
25
|
+
readonly ref: Schema.String;
|
|
26
|
+
readonly tag: Schema.String;
|
|
27
|
+
readonly text: Schema.String;
|
|
28
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.String>;
|
|
29
|
+
readonly box: Schema.Struct<{
|
|
30
|
+
readonly x: Schema.Number;
|
|
31
|
+
readonly y: Schema.Number;
|
|
32
|
+
readonly width: Schema.Number;
|
|
33
|
+
readonly height: Schema.Number;
|
|
34
|
+
}>;
|
|
35
|
+
}>;
|
|
36
|
+
type InjectedElementInfo = typeof InjectedElementInfo.Type;
|
|
37
|
+
/** The surface installed on `globalThis.__effectUaiPageAgent`. */
|
|
38
|
+
type PageAgentApi = {
|
|
39
|
+
readonly query: (selector: string, limit: number) => Array<InjectedElementInfo>;
|
|
40
|
+
readonly targetPoint: (selector: string) => InjectedPoint;
|
|
41
|
+
readonly pageBox: (selector: string) => InjectedBox;
|
|
42
|
+
readonly contentSize: () => InjectedSize;
|
|
43
|
+
readonly fill: (selector: string, text: string) => void;
|
|
44
|
+
readonly selectValue: (selector: string, value: string) => void;
|
|
45
|
+
readonly isChecked: (selector: string) => boolean;
|
|
46
|
+
readonly focusEl: (selector: string) => void;
|
|
47
|
+
readonly scrollBy: (direction: "up" | "down" | "left" | "right", pixels: number) => void;
|
|
48
|
+
readonly scrollIntoView: (selector: string) => void;
|
|
49
|
+
readonly waitForSelector: (selector: string, timeoutMs: number) => Promise<boolean>;
|
|
50
|
+
readonly html: () => string;
|
|
51
|
+
readonly markdown: () => string;
|
|
52
|
+
readonly readyState: () => string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Build the `Runtime.evaluate` expression for one agent call: install the
|
|
56
|
+
* agent (idempotent) and invoke the named helper. Arguments are embedded via
|
|
57
|
+
* `JSON.stringify`, so user input never becomes code.
|
|
58
|
+
*
|
|
59
|
+
* The whole thing is one IIFE that `return`s the helper result, so the
|
|
60
|
+
* expression has a single, well-defined completion value. A bare sequence of
|
|
61
|
+
* `;`-separated statements would leave the completion value engine-dependent
|
|
62
|
+
* (obscura's `Runtime.evaluate` yields `undefined` for it), and a returned
|
|
63
|
+
* promise still resolves correctly under `awaitPromise`.
|
|
64
|
+
*/
|
|
65
|
+
declare const agentExpression: (fn: keyof PageAgentApi, args: ReadonlyArray<unknown>) => string;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { InjectedBox, InjectedElementInfo, InjectedPoint, InjectedSize, PageAgentApi, agentExpression };
|
|
68
|
+
//# sourceMappingURL=injected.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injected.d.mts","names":[],"sources":["../../src/internal/injected.ts"],"mappings":";;;;cAiCa,aAAA,EAAa,MAAA,CAAA,MAAA;EAAA;;;KACd,aAAA,UAAuB,aAAA,CAAc,IAAA;;cAGpC,WAAA,EAAW,MAAA,CAAA,MAAA;EAAA;;;;;KAMZ,WAAA,UAAqB,WAAA,CAAY,IAAA;AAAA,cAEhC,YAAA,EAAY,MAAA,CAAA,MAAA;EAAA;;;KACb,YAAA,UAAsB,YAAA,CAAa,IAAA;;cAGlC,mBAAA,EAAmB,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;KAOpB,mBAAA,UAA6B,mBAAA,CAAoB,IAAA;;KAGjD,YAAA;EAAA,SACD,KAAA,GAAQ,QAAA,UAAkB,KAAA,aAAkB,KAAA,CAAM,mBAAA;EAAA,SAClD,WAAA,GAAc,QAAA,aAAqB,aAAA;EAAA,SACnC,OAAA,GAAU,QAAA,aAAqB,WAAA;EAAA,SAC/B,WAAA,QAAmB,YAAA;EAAA,SACnB,IAAA,GAAO,QAAA,UAAkB,IAAA;EAAA,SACzB,WAAA,GAAc,QAAA,UAAkB,KAAA;EAAA,SAChC,SAAA,GAAY,QAAA;EAAA,SACZ,OAAA,GAAU,QAAA;EAAA,SACV,QAAA,GAAW,SAAA,oCAA6C,MAAA;EAAA,SACxD,cAAA,GAAiB,QAAA;EAAA,SACjB,eAAA,GAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;EAAA,SAC1D,IAAA;EAAA,SACA,QAAA;EAAA,SACA,UAAA;AAAA;;;AA3BX;;;;;AAGA;;;;cAwWa,eAAA,GAAmB,EAAA,QAAU,YAAA,EAAc,IAAA,EAAM,aAAA"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
//#region src/internal/injected.ts
|
|
3
|
+
/**
|
|
4
|
+
* The page agent: the code that runs INSIDE the browser page.
|
|
5
|
+
*
|
|
6
|
+
* Everything lives in one self-contained function, {@link installPageAgent},
|
|
7
|
+
* which is serialized with `Function.prototype.toString` and shipped through
|
|
8
|
+
* `Runtime.evaluate` (the lowest-common-denominator CDP method, so it works
|
|
9
|
+
* against partial CDP servers like obscura). It installs an idempotent
|
|
10
|
+
* global (`globalThis.__effectUaiPageAgent`) holding the helpers the session
|
|
11
|
+
* verbs call. Arguments cross the wire as `JSON.stringify` values, never as
|
|
12
|
+
* interpolated code, so there is no escaping surface.
|
|
13
|
+
*
|
|
14
|
+
* Rules for code inside `installPageAgent`:
|
|
15
|
+
* - Self-contained: no captures of module scope, no imports. Types are fine
|
|
16
|
+
* (they erase); values are not.
|
|
17
|
+
* - Helpers signal failure by throwing `Error` with a descriptive message;
|
|
18
|
+
* the host surfaces it via `exceptionDetails`. The exception is
|
|
19
|
+
* `waitForSelector`, which reports timeout as `false` so the host can
|
|
20
|
+
* raise a typed `BrowserTimeout` instead of an action failure.
|
|
21
|
+
*
|
|
22
|
+
* The whole in-page world (including the ref registry) is reset by every
|
|
23
|
+
* navigation, which is exactly the documented lifetime of element refs.
|
|
24
|
+
*/
|
|
25
|
+
/** Viewport-relative center of an element, for `Input.dispatch*` events. */
|
|
26
|
+
const InjectedPoint = Schema.Struct({
|
|
27
|
+
x: Schema.Number,
|
|
28
|
+
y: Schema.Number
|
|
29
|
+
});
|
|
30
|
+
/** Element box: viewport-relative from `query`, page coords from `pageBox`. */
|
|
31
|
+
const InjectedBox = Schema.Struct({
|
|
32
|
+
x: Schema.Number,
|
|
33
|
+
y: Schema.Number,
|
|
34
|
+
width: Schema.Number,
|
|
35
|
+
height: Schema.Number
|
|
36
|
+
});
|
|
37
|
+
const InjectedSize = Schema.Struct({
|
|
38
|
+
width: Schema.Number,
|
|
39
|
+
height: Schema.Number
|
|
40
|
+
});
|
|
41
|
+
/** Element data returned by `query`; serializable, never a live handle. */
|
|
42
|
+
const InjectedElementInfo = Schema.Struct({
|
|
43
|
+
ref: Schema.String,
|
|
44
|
+
tag: Schema.String,
|
|
45
|
+
text: Schema.String,
|
|
46
|
+
attributes: Schema.Record(Schema.String, Schema.String),
|
|
47
|
+
box: InjectedBox
|
|
48
|
+
});
|
|
49
|
+
const AGENT_GLOBAL = "__effectUaiPageAgent";
|
|
50
|
+
const installPageAgent = () => {
|
|
51
|
+
const holder = globalThis;
|
|
52
|
+
if (holder["__effectUaiPageAgent"] !== void 0) return;
|
|
53
|
+
const byRef = /* @__PURE__ */ new Map();
|
|
54
|
+
const refOf = /* @__PURE__ */ new WeakMap();
|
|
55
|
+
let refCounter = 0;
|
|
56
|
+
const refFor = (el) => {
|
|
57
|
+
const existing = refOf.get(el);
|
|
58
|
+
if (existing !== void 0) return existing;
|
|
59
|
+
refCounter += 1;
|
|
60
|
+
const ref = `@e${refCounter}`;
|
|
61
|
+
byRef.set(ref, el);
|
|
62
|
+
refOf.set(el, ref);
|
|
63
|
+
return ref;
|
|
64
|
+
};
|
|
65
|
+
const mustResolve = (selector) => {
|
|
66
|
+
if (selector.startsWith("@")) {
|
|
67
|
+
const el = byRef.get(selector);
|
|
68
|
+
if (el === void 0) throw new Error(`stale or unknown ref ${selector} (refs reset on navigation)`);
|
|
69
|
+
if (!el.isConnected) throw new Error(`ref ${selector} is no longer in the document`);
|
|
70
|
+
return el;
|
|
71
|
+
}
|
|
72
|
+
const el = document.querySelector(selector);
|
|
73
|
+
if (el === null) throw new Error(`no element matches selector ${selector}`);
|
|
74
|
+
return el;
|
|
75
|
+
};
|
|
76
|
+
const SKIP = new Set([
|
|
77
|
+
"SCRIPT",
|
|
78
|
+
"STYLE",
|
|
79
|
+
"NOSCRIPT",
|
|
80
|
+
"TEMPLATE",
|
|
81
|
+
"IFRAME",
|
|
82
|
+
"SVG",
|
|
83
|
+
"HEAD"
|
|
84
|
+
]);
|
|
85
|
+
const isHidden = (el) => el.hasAttribute("hidden") || el.getAttribute("aria-hidden") === "true";
|
|
86
|
+
const collapse = (text) => text.replace(/\s+/g, " ").trim();
|
|
87
|
+
const inlineText = (node) => {
|
|
88
|
+
if (node.nodeType === Node.TEXT_NODE) return (node.textContent ?? "").replace(/\s+/g, " ");
|
|
89
|
+
if (!(node instanceof Element)) return "";
|
|
90
|
+
const tag = node.tagName.toUpperCase();
|
|
91
|
+
if (SKIP.has(tag) || isHidden(node)) return "";
|
|
92
|
+
const kids = () => Array.from(node.childNodes).map(inlineText).join("");
|
|
93
|
+
if (tag === "BR") return "\n";
|
|
94
|
+
if (tag === "STRONG" || tag === "B") {
|
|
95
|
+
const t = collapse(kids());
|
|
96
|
+
return t === "" ? "" : `**${t}**`;
|
|
97
|
+
}
|
|
98
|
+
if (tag === "EM" || tag === "I") {
|
|
99
|
+
const t = collapse(kids());
|
|
100
|
+
return t === "" ? "" : `*${t}*`;
|
|
101
|
+
}
|
|
102
|
+
if (tag === "CODE") {
|
|
103
|
+
const t = collapse(node.textContent ?? "");
|
|
104
|
+
return t === "" ? "" : `\`${t}\``;
|
|
105
|
+
}
|
|
106
|
+
if (tag === "A") {
|
|
107
|
+
const t = collapse(kids());
|
|
108
|
+
if (t === "") return "";
|
|
109
|
+
const href = node.getAttribute("href");
|
|
110
|
+
return href === null || href.startsWith("javascript:") ? t : `[${t}](${href})`;
|
|
111
|
+
}
|
|
112
|
+
if (tag === "IMG") {
|
|
113
|
+
const src = node.getAttribute("src");
|
|
114
|
+
return src === null ? "" : ``;
|
|
115
|
+
}
|
|
116
|
+
return kids();
|
|
117
|
+
};
|
|
118
|
+
const renderList = (el, depth) => {
|
|
119
|
+
const ordered = el.tagName.toUpperCase() === "OL";
|
|
120
|
+
const items = [];
|
|
121
|
+
let index = 0;
|
|
122
|
+
for (const li of Array.from(el.children)) {
|
|
123
|
+
if (li.tagName.toUpperCase() !== "LI") continue;
|
|
124
|
+
index += 1;
|
|
125
|
+
let own = "";
|
|
126
|
+
const sublists = [];
|
|
127
|
+
for (const child of Array.from(li.childNodes)) {
|
|
128
|
+
const childTag = child instanceof Element ? child.tagName.toUpperCase() : "";
|
|
129
|
+
if (childTag === "UL" || childTag === "OL") sublists.push(child);
|
|
130
|
+
else own += inlineText(child);
|
|
131
|
+
}
|
|
132
|
+
let line = " ".repeat(depth) + (ordered ? `${index}. ` : "- ") + collapse(own);
|
|
133
|
+
for (const sub of sublists) line += "\n" + renderList(sub, depth + 1);
|
|
134
|
+
items.push(line);
|
|
135
|
+
}
|
|
136
|
+
return items.join("\n");
|
|
137
|
+
};
|
|
138
|
+
const renderTable = (el) => {
|
|
139
|
+
const rows = Array.from(el.querySelectorAll("tr"));
|
|
140
|
+
const first = rows[0];
|
|
141
|
+
if (first === void 0) return "";
|
|
142
|
+
const cells = (tr) => Array.from(tr.children).filter((c) => c.tagName.toUpperCase() === "TD" || c.tagName.toUpperCase() === "TH").map((c) => collapse(inlineText(c)) || " ");
|
|
143
|
+
const lines = rows.map((tr) => `| ${cells(tr).join(" | ")} |`);
|
|
144
|
+
const separator = `| ${cells(first).map(() => "---").join(" | ")} |`;
|
|
145
|
+
return [
|
|
146
|
+
lines[0],
|
|
147
|
+
separator,
|
|
148
|
+
...lines.slice(1)
|
|
149
|
+
].join("\n");
|
|
150
|
+
};
|
|
151
|
+
const walkBlocks = (el, out) => {
|
|
152
|
+
for (const child of Array.from(el.children)) {
|
|
153
|
+
const tag = child.tagName.toUpperCase();
|
|
154
|
+
if (SKIP.has(tag) || isHidden(child)) continue;
|
|
155
|
+
const heading = /^H([1-6])$/.exec(tag);
|
|
156
|
+
if (heading !== null) {
|
|
157
|
+
const t = collapse(inlineText(child));
|
|
158
|
+
if (t !== "") out.push("#".repeat(Number(heading[1] ?? "1")) + " " + t);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (tag === "P") {
|
|
162
|
+
const t = collapse(inlineText(child));
|
|
163
|
+
if (t !== "") out.push(t);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (tag === "PRE") {
|
|
167
|
+
out.push("```\n" + (child.textContent ?? "").replace(/\s+$/, "") + "\n```");
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (tag === "BLOCKQUOTE") {
|
|
171
|
+
const t = collapse(inlineText(child));
|
|
172
|
+
if (t !== "") out.push("> " + t);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (tag === "UL" || tag === "OL") {
|
|
176
|
+
const t = renderList(child, 0);
|
|
177
|
+
if (t !== "") out.push(t);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (tag === "TABLE") {
|
|
181
|
+
const t = renderTable(child);
|
|
182
|
+
if (t !== "") out.push(t);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (tag === "HR") {
|
|
186
|
+
out.push("---");
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (child.children.length > 0) {
|
|
190
|
+
walkBlocks(child, out);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const t = collapse(inlineText(child));
|
|
194
|
+
if (t !== "") out.push(t);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
holder["__effectUaiPageAgent"] = {
|
|
198
|
+
query: (selector, limit) => {
|
|
199
|
+
return (selector.startsWith("@") ? [mustResolve(selector)] : Array.from(document.querySelectorAll(selector))).slice(0, limit).map((el) => {
|
|
200
|
+
const rect = el.getBoundingClientRect();
|
|
201
|
+
const attributes = {};
|
|
202
|
+
for (const attr of Array.from(el.attributes)) attributes[attr.name] = attr.value;
|
|
203
|
+
return {
|
|
204
|
+
ref: refFor(el),
|
|
205
|
+
tag: el.tagName.toLowerCase(),
|
|
206
|
+
text: collapse(el.textContent ?? "").slice(0, 200),
|
|
207
|
+
attributes,
|
|
208
|
+
box: {
|
|
209
|
+
x: rect.x,
|
|
210
|
+
y: rect.y,
|
|
211
|
+
width: rect.width,
|
|
212
|
+
height: rect.height
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
targetPoint: (selector) => {
|
|
218
|
+
const el = mustResolve(selector);
|
|
219
|
+
el.scrollIntoView({
|
|
220
|
+
block: "center",
|
|
221
|
+
inline: "center"
|
|
222
|
+
});
|
|
223
|
+
const rect = el.getBoundingClientRect();
|
|
224
|
+
if (rect.width === 0 || rect.height === 0) throw new Error(`element ${selector} has no visible box`);
|
|
225
|
+
return {
|
|
226
|
+
x: rect.x + rect.width / 2,
|
|
227
|
+
y: rect.y + rect.height / 2
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
pageBox: (selector) => {
|
|
231
|
+
const rect = mustResolve(selector).getBoundingClientRect();
|
|
232
|
+
return {
|
|
233
|
+
x: rect.x + window.scrollX,
|
|
234
|
+
y: rect.y + window.scrollY,
|
|
235
|
+
width: rect.width,
|
|
236
|
+
height: rect.height
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
contentSize: () => ({
|
|
240
|
+
width: document.documentElement.scrollWidth,
|
|
241
|
+
height: document.documentElement.scrollHeight
|
|
242
|
+
}),
|
|
243
|
+
fill: (selector, text) => {
|
|
244
|
+
const el = mustResolve(selector);
|
|
245
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
246
|
+
const proto = el instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
|
|
247
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
248
|
+
el.focus();
|
|
249
|
+
if (setter !== void 0) setter.call(el, text);
|
|
250
|
+
else el.value = text;
|
|
251
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
252
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (el instanceof HTMLElement && el.isContentEditable) {
|
|
256
|
+
el.focus();
|
|
257
|
+
el.textContent = text;
|
|
258
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`element ${selector} is not fillable (input, textarea, or contenteditable)`);
|
|
262
|
+
},
|
|
263
|
+
selectValue: (selector, value) => {
|
|
264
|
+
const el = mustResolve(selector);
|
|
265
|
+
if (!(el instanceof HTMLSelectElement)) throw new Error(`element ${selector} is not a <select>`);
|
|
266
|
+
if (Array.from(el.options).find((o) => o.value === value) === void 0) throw new Error(`select ${selector} has no option with value ${JSON.stringify(value)}`);
|
|
267
|
+
el.value = value;
|
|
268
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
269
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
270
|
+
},
|
|
271
|
+
isChecked: (selector) => {
|
|
272
|
+
const el = mustResolve(selector);
|
|
273
|
+
if (el instanceof HTMLInputElement && (el.type === "checkbox" || el.type === "radio")) return el.checked;
|
|
274
|
+
throw new Error(`element ${selector} is not a checkbox or radio input`);
|
|
275
|
+
},
|
|
276
|
+
focusEl: (selector) => {
|
|
277
|
+
const el = mustResolve(selector);
|
|
278
|
+
if (!(el instanceof HTMLElement)) throw new Error(`element ${selector} is not focusable`);
|
|
279
|
+
el.focus();
|
|
280
|
+
},
|
|
281
|
+
scrollBy: (direction, pixels) => {
|
|
282
|
+
const [dx, dy] = direction === "up" ? [0, -pixels] : direction === "down" ? [0, pixels] : direction === "left" ? [-pixels, 0] : [pixels, 0];
|
|
283
|
+
window.scrollBy({
|
|
284
|
+
left: dx,
|
|
285
|
+
top: dy,
|
|
286
|
+
behavior: "instant"
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
scrollIntoView: (selector) => {
|
|
290
|
+
mustResolve(selector).scrollIntoView({
|
|
291
|
+
block: "center",
|
|
292
|
+
inline: "center"
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
waitForSelector: (selector, timeoutMs) => new Promise((resolve) => {
|
|
296
|
+
const found = () => selector.startsWith("@") ? byRef.get(selector)?.isConnected ?? false : document.querySelector(selector) !== null;
|
|
297
|
+
if (found()) return resolve(true);
|
|
298
|
+
const observer = new MutationObserver(() => {
|
|
299
|
+
if (!found()) return;
|
|
300
|
+
observer.disconnect();
|
|
301
|
+
window.clearTimeout(timer);
|
|
302
|
+
resolve(true);
|
|
303
|
+
});
|
|
304
|
+
const timer = window.setTimeout(() => {
|
|
305
|
+
observer.disconnect();
|
|
306
|
+
resolve(false);
|
|
307
|
+
}, timeoutMs);
|
|
308
|
+
observer.observe(document.documentElement, {
|
|
309
|
+
childList: true,
|
|
310
|
+
subtree: true,
|
|
311
|
+
attributes: true
|
|
312
|
+
});
|
|
313
|
+
}),
|
|
314
|
+
html: () => document.documentElement.outerHTML,
|
|
315
|
+
markdown: () => {
|
|
316
|
+
const out = [];
|
|
317
|
+
const title = document.title.trim();
|
|
318
|
+
if (title !== "") out.push(`# ${title}`);
|
|
319
|
+
const root = document.querySelector("main, [role=main], article") ?? document.body;
|
|
320
|
+
if (root !== null) walkBlocks(root, out);
|
|
321
|
+
return out.join("\n\n");
|
|
322
|
+
},
|
|
323
|
+
readyState: () => document.readyState
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Build the `Runtime.evaluate` expression for one agent call: install the
|
|
328
|
+
* agent (idempotent) and invoke the named helper. Arguments are embedded via
|
|
329
|
+
* `JSON.stringify`, so user input never becomes code.
|
|
330
|
+
*
|
|
331
|
+
* The whole thing is one IIFE that `return`s the helper result, so the
|
|
332
|
+
* expression has a single, well-defined completion value. A bare sequence of
|
|
333
|
+
* `;`-separated statements would leave the completion value engine-dependent
|
|
334
|
+
* (obscura's `Runtime.evaluate` yields `undefined` for it), and a returned
|
|
335
|
+
* promise still resolves correctly under `awaitPromise`.
|
|
336
|
+
*/
|
|
337
|
+
const agentExpression = (fn, args) => `(()=>{(${installPageAgent.toString()})();return globalThis.${AGENT_GLOBAL}.${fn}(${args.map((a) => JSON.stringify(a)).join(",")});})()`;
|
|
338
|
+
//#endregion
|
|
339
|
+
export { InjectedBox, InjectedElementInfo, InjectedPoint, InjectedSize, agentExpression };
|
|
340
|
+
|
|
341
|
+
//# sourceMappingURL=injected.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injected.mjs","names":[],"sources":["../../src/internal/injected.ts"],"sourcesContent":["/**\n * The page agent: the code that runs INSIDE the browser page.\n *\n * Everything lives in one self-contained function, {@link installPageAgent},\n * which is serialized with `Function.prototype.toString` and shipped through\n * `Runtime.evaluate` (the lowest-common-denominator CDP method, so it works\n * against partial CDP servers like obscura). It installs an idempotent\n * global (`globalThis.__effectUaiPageAgent`) holding the helpers the session\n * verbs call. Arguments cross the wire as `JSON.stringify` values, never as\n * interpolated code, so there is no escaping surface.\n *\n * Rules for code inside `installPageAgent`:\n * - Self-contained: no captures of module scope, no imports. Types are fine\n * (they erase); values are not.\n * - Helpers signal failure by throwing `Error` with a descriptive message;\n * the host surfaces it via `exceptionDetails`. The exception is\n * `waitForSelector`, which reports timeout as `false` so the host can\n * raise a typed `BrowserTimeout` instead of an action failure.\n *\n * The whole in-page world (including the ref registry) is reset by every\n * navigation, which is exactly the documented lifetime of element refs.\n */\n\nimport { Schema } from \"effect\"\n\n// ---------------------------------------------------------------------------\n// Wire shapes. Values crossing page -> host are untrusted JSON; the host\n// decodes them with these schemas instead of casting. The derived types\n// double as the agent's own annotations (types erase, so serialization\n// stays self-contained).\n// ---------------------------------------------------------------------------\n\n/** Viewport-relative center of an element, for `Input.dispatch*` events. */\nexport const InjectedPoint = Schema.Struct({ x: Schema.Number, y: Schema.Number })\nexport type InjectedPoint = typeof InjectedPoint.Type\n\n/** Element box: viewport-relative from `query`, page coords from `pageBox`. */\nexport const InjectedBox = Schema.Struct({\n x: Schema.Number,\n y: Schema.Number,\n width: Schema.Number,\n height: Schema.Number,\n})\nexport type InjectedBox = typeof InjectedBox.Type\n\nexport const InjectedSize = Schema.Struct({ width: Schema.Number, height: Schema.Number })\nexport type InjectedSize = typeof InjectedSize.Type\n\n/** Element data returned by `query`; serializable, never a live handle. */\nexport const InjectedElementInfo = Schema.Struct({\n ref: Schema.String,\n tag: Schema.String,\n text: Schema.String,\n attributes: Schema.Record(Schema.String, Schema.String),\n box: InjectedBox,\n})\nexport type InjectedElementInfo = typeof InjectedElementInfo.Type\n\n/** The surface installed on `globalThis.__effectUaiPageAgent`. */\nexport type PageAgentApi = {\n readonly query: (selector: string, limit: number) => Array<InjectedElementInfo>\n readonly targetPoint: (selector: string) => InjectedPoint\n readonly pageBox: (selector: string) => InjectedBox\n readonly contentSize: () => InjectedSize\n readonly fill: (selector: string, text: string) => void\n readonly selectValue: (selector: string, value: string) => void\n readonly isChecked: (selector: string) => boolean\n readonly focusEl: (selector: string) => void\n readonly scrollBy: (direction: \"up\" | \"down\" | \"left\" | \"right\", pixels: number) => void\n readonly scrollIntoView: (selector: string) => void\n readonly waitForSelector: (selector: string, timeoutMs: number) => Promise<boolean>\n readonly html: () => string\n readonly markdown: () => string\n readonly readyState: () => string\n}\n\nconst AGENT_GLOBAL = \"__effectUaiPageAgent\"\n\nconst installPageAgent = (): void => {\n const holder = globalThis as Record<string, unknown>\n // Global name must match AGENT_GLOBAL; duplicated because this function\n // is serialized and cannot capture module scope.\n if (holder[\"__effectUaiPageAgent\"] !== undefined) return\n\n // -- ref registry: `@e1`-style refs minted by `query`, valid until the\n // -- next navigation (a navigation resets this whole world).\n const byRef = new Map<string, Element>()\n const refOf = new WeakMap<Element, string>()\n let refCounter = 0\n\n const refFor = (el: Element): string => {\n const existing = refOf.get(el)\n if (existing !== undefined) return existing\n refCounter += 1\n const ref = `@e${refCounter}`\n byRef.set(ref, el)\n refOf.set(el, ref)\n return ref\n }\n\n const mustResolve = (selector: string): Element => {\n if (selector.startsWith(\"@\")) {\n const el = byRef.get(selector)\n if (el === undefined)\n throw new Error(`stale or unknown ref ${selector} (refs reset on navigation)`)\n if (!el.isConnected) throw new Error(`ref ${selector} is no longer in the document`)\n return el\n }\n const el = document.querySelector(selector)\n if (el === null) throw new Error(`no element matches selector ${selector}`)\n return el\n }\n\n // -- markdown serialization ------------------------------------------------\n\n const SKIP = new Set([\"SCRIPT\", \"STYLE\", \"NOSCRIPT\", \"TEMPLATE\", \"IFRAME\", \"SVG\", \"HEAD\"])\n\n const isHidden = (el: Element): boolean =>\n el.hasAttribute(\"hidden\") || el.getAttribute(\"aria-hidden\") === \"true\"\n\n const collapse = (text: string): string => text.replace(/\\s+/g, \" \").trim()\n\n const inlineText = (node: Node): string => {\n if (node.nodeType === Node.TEXT_NODE) return (node.textContent ?? \"\").replace(/\\s+/g, \" \")\n if (!(node instanceof Element)) return \"\"\n const tag = node.tagName.toUpperCase()\n if (SKIP.has(tag) || isHidden(node)) return \"\"\n const kids = (): string => Array.from(node.childNodes).map(inlineText).join(\"\")\n if (tag === \"BR\") return \"\\n\"\n if (tag === \"STRONG\" || tag === \"B\") {\n const t = collapse(kids())\n return t === \"\" ? \"\" : `**${t}**`\n }\n if (tag === \"EM\" || tag === \"I\") {\n const t = collapse(kids())\n return t === \"\" ? \"\" : `*${t}*`\n }\n if (tag === \"CODE\") {\n const t = collapse(node.textContent ?? \"\")\n return t === \"\" ? \"\" : `\\`${t}\\``\n }\n if (tag === \"A\") {\n const t = collapse(kids())\n if (t === \"\") return \"\"\n const href = node.getAttribute(\"href\")\n return href === null || href.startsWith(\"javascript:\") ? t : `[${t}](${href})`\n }\n if (tag === \"IMG\") {\n const src = node.getAttribute(\"src\")\n return src === null ? \"\" : ``\n }\n return kids()\n }\n\n const renderList = (el: Element, depth: number): string => {\n const ordered = el.tagName.toUpperCase() === \"OL\"\n const items: Array<string> = []\n let index = 0\n for (const li of Array.from(el.children)) {\n if (li.tagName.toUpperCase() !== \"LI\") continue\n index += 1\n let own = \"\"\n const sublists: Array<Element> = []\n for (const child of Array.from(li.childNodes)) {\n const childTag = child instanceof Element ? child.tagName.toUpperCase() : \"\"\n if (childTag === \"UL\" || childTag === \"OL\") sublists.push(child as Element)\n else own += inlineText(child)\n }\n let line = \" \".repeat(depth) + (ordered ? `${index}. ` : \"- \") + collapse(own)\n for (const sub of sublists) line += \"\\n\" + renderList(sub, depth + 1)\n items.push(line)\n }\n return items.join(\"\\n\")\n }\n\n const renderTable = (el: Element): string => {\n const rows = Array.from(el.querySelectorAll(\"tr\"))\n const first = rows[0]\n if (first === undefined) return \"\"\n const cells = (tr: Element): Array<string> =>\n Array.from(tr.children)\n .filter((c) => c.tagName.toUpperCase() === \"TD\" || c.tagName.toUpperCase() === \"TH\")\n .map((c) => collapse(inlineText(c)) || \" \")\n const lines = rows.map((tr) => `| ${cells(tr).join(\" | \")} |`)\n const separator = `| ${cells(first)\n .map(() => \"---\")\n .join(\" | \")} |`\n return [lines[0], separator, ...lines.slice(1)].join(\"\\n\")\n }\n\n const walkBlocks = (el: Element, out: Array<string>): void => {\n for (const child of Array.from(el.children)) {\n const tag = child.tagName.toUpperCase()\n if (SKIP.has(tag) || isHidden(child)) continue\n const heading = /^H([1-6])$/.exec(tag)\n if (heading !== null) {\n const t = collapse(inlineText(child))\n if (t !== \"\") out.push(\"#\".repeat(Number(heading[1] ?? \"1\")) + \" \" + t)\n continue\n }\n if (tag === \"P\") {\n const t = collapse(inlineText(child))\n if (t !== \"\") out.push(t)\n continue\n }\n if (tag === \"PRE\") {\n out.push(\"```\\n\" + (child.textContent ?? \"\").replace(/\\s+$/, \"\") + \"\\n```\")\n continue\n }\n if (tag === \"BLOCKQUOTE\") {\n const t = collapse(inlineText(child))\n if (t !== \"\") out.push(\"> \" + t)\n continue\n }\n if (tag === \"UL\" || tag === \"OL\") {\n const t = renderList(child, 0)\n if (t !== \"\") out.push(t)\n continue\n }\n if (tag === \"TABLE\") {\n const t = renderTable(child)\n if (t !== \"\") out.push(t)\n continue\n }\n if (tag === \"HR\") {\n out.push(\"---\")\n continue\n }\n if (child.children.length > 0) {\n walkBlocks(child, out)\n continue\n }\n const t = collapse(inlineText(child))\n if (t !== \"\") out.push(t)\n }\n }\n\n // -- the agent surface -----------------------------------------------------\n\n const agent: PageAgentApi = {\n query: (selector, limit) => {\n const els = selector.startsWith(\"@\")\n ? [mustResolve(selector)]\n : Array.from(document.querySelectorAll(selector))\n return els.slice(0, limit).map((el) => {\n const rect = el.getBoundingClientRect()\n const attributes: Record<string, string> = {}\n for (const attr of Array.from(el.attributes)) attributes[attr.name] = attr.value\n return {\n ref: refFor(el),\n tag: el.tagName.toLowerCase(),\n text: collapse(el.textContent ?? \"\").slice(0, 200),\n attributes,\n box: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },\n }\n })\n },\n\n targetPoint: (selector) => {\n const el = mustResolve(selector)\n el.scrollIntoView({ block: \"center\", inline: \"center\" })\n const rect = el.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) {\n throw new Error(`element ${selector} has no visible box`)\n }\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }\n },\n\n pageBox: (selector) => {\n const rect = mustResolve(selector).getBoundingClientRect()\n return {\n x: rect.x + window.scrollX,\n y: rect.y + window.scrollY,\n width: rect.width,\n height: rect.height,\n }\n },\n\n contentSize: () => ({\n width: document.documentElement.scrollWidth,\n height: document.documentElement.scrollHeight,\n }),\n\n fill: (selector, text) => {\n const el = mustResolve(selector)\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n // Set through the native prototype setter so framework value\n // trackers (React et al.) see the change, then fire the events a\n // real keyboard session would.\n const proto =\n el instanceof HTMLInputElement\n ? HTMLInputElement.prototype\n : HTMLTextAreaElement.prototype\n const setter = Object.getOwnPropertyDescriptor(proto, \"value\")?.set\n el.focus()\n if (setter !== undefined) setter.call(el, text)\n else el.value = text\n el.dispatchEvent(new Event(\"input\", { bubbles: true }))\n el.dispatchEvent(new Event(\"change\", { bubbles: true }))\n return\n }\n if (el instanceof HTMLElement && el.isContentEditable) {\n el.focus()\n el.textContent = text\n el.dispatchEvent(new Event(\"input\", { bubbles: true }))\n return\n }\n throw new Error(`element ${selector} is not fillable (input, textarea, or contenteditable)`)\n },\n\n selectValue: (selector, value) => {\n const el = mustResolve(selector)\n if (!(el instanceof HTMLSelectElement)) {\n throw new Error(`element ${selector} is not a <select>`)\n }\n const option = Array.from(el.options).find((o) => o.value === value)\n if (option === undefined) {\n throw new Error(`select ${selector} has no option with value ${JSON.stringify(value)}`)\n }\n el.value = value\n el.dispatchEvent(new Event(\"input\", { bubbles: true }))\n el.dispatchEvent(new Event(\"change\", { bubbles: true }))\n },\n\n isChecked: (selector) => {\n const el = mustResolve(selector)\n if (el instanceof HTMLInputElement && (el.type === \"checkbox\" || el.type === \"radio\")) {\n return el.checked\n }\n throw new Error(`element ${selector} is not a checkbox or radio input`)\n },\n\n focusEl: (selector) => {\n const el = mustResolve(selector)\n if (!(el instanceof HTMLElement)) throw new Error(`element ${selector} is not focusable`)\n el.focus()\n },\n\n scrollBy: (direction, pixels) => {\n const [dx, dy] =\n direction === \"up\"\n ? [0, -pixels]\n : direction === \"down\"\n ? [0, pixels]\n : direction === \"left\"\n ? [-pixels, 0]\n : [pixels, 0]\n window.scrollBy({ left: dx, top: dy, behavior: \"instant\" })\n },\n\n scrollIntoView: (selector) => {\n mustResolve(selector).scrollIntoView({ block: \"center\", inline: \"center\" })\n },\n\n waitForSelector: (selector, timeoutMs) =>\n new Promise((resolve) => {\n const found = (): boolean =>\n selector.startsWith(\"@\")\n ? (byRef.get(selector)?.isConnected ?? false)\n : document.querySelector(selector) !== null\n if (found()) return resolve(true)\n const observer = new MutationObserver(() => {\n if (!found()) return\n observer.disconnect()\n window.clearTimeout(timer)\n resolve(true)\n })\n const timer = window.setTimeout(() => {\n observer.disconnect()\n resolve(false)\n }, timeoutMs)\n observer.observe(document.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n })\n }),\n\n html: () => document.documentElement.outerHTML,\n\n markdown: () => {\n const out: Array<string> = []\n const title = document.title.trim()\n if (title !== \"\") out.push(`# ${title}`)\n // Prefer the primary content region so the serialization leads with the\n // article, not a site-wide nav/sidebar (which in DOM order often comes\n // first and would otherwise dominate a truncated view).\n const root = document.querySelector(\"main, [role=main], article\") ?? document.body\n if (root !== null) walkBlocks(root, out)\n return out.join(\"\\n\\n\")\n },\n\n readyState: () => document.readyState,\n }\n\n holder[\"__effectUaiPageAgent\"] = agent\n}\n\n/**\n * Build the `Runtime.evaluate` expression for one agent call: install the\n * agent (idempotent) and invoke the named helper. Arguments are embedded via\n * `JSON.stringify`, so user input never becomes code.\n *\n * The whole thing is one IIFE that `return`s the helper result, so the\n * expression has a single, well-defined completion value. A bare sequence of\n * `;`-separated statements would leave the completion value engine-dependent\n * (obscura's `Runtime.evaluate` yields `undefined` for it), and a returned\n * promise still resolves correctly under `awaitPromise`.\n */\nexport const agentExpression = (fn: keyof PageAgentApi, args: ReadonlyArray<unknown>): string =>\n `(()=>{(${installPageAgent.toString()})();return globalThis.${AGENT_GLOBAL}.${fn}(${args\n .map((a) => JSON.stringify(a))\n .join(\",\")});})()`\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { n as CdpError, t as Cdp } from "../cdp-DZgualSC.mjs";
|
|
2
|
+
import * as CoreBrowser from "@effect-uai/core/Browser";
|
|
3
|
+
import { Effect, Scope } from "effect";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/session.d.ts
|
|
6
|
+
/** Reply errors that mean "this endpoint does not implement the method". */
|
|
7
|
+
declare const isMethodMissing: (e: CdpError) => boolean;
|
|
8
|
+
type MakeSessionOptions = {
|
|
9
|
+
readonly cdp: Cdp;
|
|
10
|
+
readonly id: CoreBrowser.BrowserSessionId; /** CDP target session id (flatten mode); routes every command. */
|
|
11
|
+
readonly sessionId: string;
|
|
12
|
+
readonly provider: string;
|
|
13
|
+
readonly request: CoreBrowser.CommonSessionRequest; /** Best-effort provider-side teardown, run when the session expires. */
|
|
14
|
+
readonly dispose: Effect.Effect<void>;
|
|
15
|
+
};
|
|
16
|
+
declare const makeSession: (options: MakeSessionOptions) => Effect.Effect<CoreBrowser.BrowserSession, never, Scope.Scope>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { MakeSessionOptions, isMethodMissing, makeSession };
|
|
19
|
+
//# sourceMappingURL=session.d.mts.map
|