bunite-core 0.17.1 → 0.17.3
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/package.json +1 -1
- package/src/host/core/App.ts +28 -18
- package/src/host/core/BrowserView.ts +333 -116
- package/src/host/core/BrowserWindow.ts +44 -22
- package/src/host/core/Socket.ts +26 -9
- package/src/host/core/SurfaceBrowserIPC.ts +15 -5
- package/src/host/core/SurfaceManager.ts +345 -158
- package/src/host/core/SurfaceRegistry.ts +1 -1
- package/src/host/core/inputDispatch.ts +71 -42
- package/src/host/core/singleInstanceLock.ts +10 -2
- package/src/host/core/windowCap.ts +42 -23
- package/src/host/encryptedPipe.ts +20 -3
- package/src/host/events/appEvents.ts +1 -1
- package/src/host/events/eventEmitter.ts +6 -6
- package/src/host/events/webviewEvents.ts +11 -4
- package/src/host/events/windowEvents.ts +1 -2
- package/src/host/index.ts +9 -17
- package/src/host/log.ts +6 -4
- package/src/host/native.ts +271 -163
- package/src/host/paths.ts +18 -15
- package/src/host/platform.ts +2 -4
- package/src/host/preloadBundle.ts +1 -1
- package/src/host/serveWeb.ts +45 -30
- package/src/preload/runtime.built.js +1 -1
- package/src/preload/runtime.ts +66 -25
- package/src/rpc/encrypt.ts +23 -4
- package/src/rpc/error.ts +2 -7
- package/src/rpc/framework.ts +105 -35
- package/src/rpc/index.ts +130 -140
- package/src/rpc/peer.ts +442 -164
- package/src/rpc/renderer.ts +29 -11
- package/src/rpc/schema.ts +86 -61
- package/src/rpc/stream.ts +12 -3
- package/src/rpc/transport.ts +76 -12
- package/src/rpc/wire.ts +23 -10
- package/src/webview/native.ts +150 -73
- package/src/webview/polyfill.ts +184 -48
package/src/preload/runtime.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type AnyCapDef,
|
|
3
|
+
type ClientOf,
|
|
4
|
+
type Connection,
|
|
2
5
|
createConnection,
|
|
6
|
+
createEncryptedPipe,
|
|
3
7
|
createFrameTransport,
|
|
4
8
|
createWebSocketPipe,
|
|
5
|
-
createEncryptedPipe,
|
|
6
|
-
type Connection,
|
|
7
|
-
type CapDef,
|
|
8
9
|
type Schema,
|
|
9
10
|
type SchemaRoots,
|
|
10
|
-
type ClientOf,
|
|
11
11
|
type WebSocketLike,
|
|
12
12
|
} from "../rpc/index";
|
|
13
13
|
|
|
@@ -19,19 +19,28 @@ let _conn: Connection | null = null;
|
|
|
19
19
|
let _connPromise: Promise<Connection> | null = null;
|
|
20
20
|
|
|
21
21
|
function ensureConnection(): Promise<Connection> {
|
|
22
|
-
if (_conn)
|
|
22
|
+
if (_conn) {
|
|
23
|
+
if (!_conn.closed) return Promise.resolve(_conn);
|
|
24
|
+
_conn = null;
|
|
25
|
+
_connPromise = null;
|
|
26
|
+
}
|
|
23
27
|
if (_connPromise) return _connPromise;
|
|
24
28
|
const attempt = (async () => {
|
|
25
29
|
const ws = new WebSocket(
|
|
26
|
-
`ws://localhost:${__buniteRpcSocketPort}/rpc?webviewId=${__buniteWebviewId}
|
|
30
|
+
`ws://localhost:${__buniteRpcSocketPort}/rpc?webviewId=${__buniteWebviewId}`,
|
|
27
31
|
);
|
|
28
32
|
ws.binaryType = "arraybuffer";
|
|
29
33
|
await new Promise<void>((resolve, reject) => {
|
|
30
34
|
ws.addEventListener("open", () => resolve(), { once: true });
|
|
31
|
-
ws.addEventListener("error", () => reject(new Error("bunite preload ws connect failed")), {
|
|
35
|
+
ws.addEventListener("error", () => reject(new Error("bunite preload ws connect failed")), {
|
|
36
|
+
once: true,
|
|
37
|
+
});
|
|
32
38
|
});
|
|
33
39
|
const rawKey = Uint8Array.from(atob(__buniteSecretKeyBase64), (c) => c.charCodeAt(0));
|
|
34
|
-
const pipe = await createEncryptedPipe(
|
|
40
|
+
const pipe = await createEncryptedPipe(
|
|
41
|
+
createWebSocketPipe(ws as unknown as WebSocketLike),
|
|
42
|
+
rawKey,
|
|
43
|
+
);
|
|
35
44
|
const conn = createConnection({
|
|
36
45
|
transport: createFrameTransport(pipe),
|
|
37
46
|
mode: "native",
|
|
@@ -53,12 +62,13 @@ w.__buniteWebviewId = __buniteWebviewId;
|
|
|
53
62
|
w.__buniteRpcSocketPort = __buniteRpcSocketPort;
|
|
54
63
|
w.host ??= {};
|
|
55
64
|
|
|
56
|
-
w.host.bootstrap = async (target:
|
|
65
|
+
w.host.bootstrap = async (target: AnyCapDef | Schema<SchemaRoots>): Promise<unknown> => {
|
|
57
66
|
const conn = await ensureConnection();
|
|
58
67
|
return (conn.bootstrap as (t: unknown) => Promise<unknown>)(target);
|
|
59
68
|
};
|
|
60
69
|
|
|
61
|
-
w.host.runtime = async () =>
|
|
70
|
+
w.host.runtime = async () =>
|
|
71
|
+
(await ensureConnection()).runtime() as ClientOf<typeof import("../rpc/framework").RuntimeCap>;
|
|
62
72
|
|
|
63
73
|
w.host.releaseRef = async (proxy: unknown): Promise<void> => {
|
|
64
74
|
(await ensureConnection()).releaseRef(proxy);
|
|
@@ -89,10 +99,16 @@ const pageBuffer = (w as any)[CONSOLE_BUFFER_KEY] as ConsoleEntry[];
|
|
|
89
99
|
function serializeArg(a: unknown): string {
|
|
90
100
|
if (typeof a === "string") return a;
|
|
91
101
|
if (a instanceof Error) return a.stack ?? a.message;
|
|
92
|
-
try {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.stringify(a);
|
|
104
|
+
} catch {
|
|
105
|
+
return String(a);
|
|
106
|
+
}
|
|
93
107
|
}
|
|
94
108
|
|
|
95
|
-
let reportingCap: Promise<{
|
|
109
|
+
let reportingCap: Promise<{
|
|
110
|
+
reportConsoleBatch(args: { entries: ConsoleEntry[] }): Promise<void> | void;
|
|
111
|
+
}> | null = null;
|
|
96
112
|
// Circuit breaker: after a failed cap fetch, hold off for a cooldown so a
|
|
97
113
|
// permanently-disconnected host doesn't burn a retry per batch (16ms cadence).
|
|
98
114
|
let nextRetryMs = 0;
|
|
@@ -115,7 +131,10 @@ let batch: ConsoleEntry[] = [];
|
|
|
115
131
|
let batchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
116
132
|
|
|
117
133
|
function flushBatch() {
|
|
118
|
-
if (batchTimer) {
|
|
134
|
+
if (batchTimer) {
|
|
135
|
+
clearTimeout(batchTimer);
|
|
136
|
+
batchTimer = null;
|
|
137
|
+
}
|
|
119
138
|
if (batch.length === 0) return;
|
|
120
139
|
const entries = batch;
|
|
121
140
|
batch = [];
|
|
@@ -139,11 +158,17 @@ function scheduleFlush() {
|
|
|
139
158
|
|
|
140
159
|
function installConsoleProxy() {
|
|
141
160
|
for (const level of ["log", "warn", "error", "info", "debug"] as const) {
|
|
142
|
-
const original = (console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level].bind(
|
|
161
|
+
const original = (console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level].bind(
|
|
162
|
+
console,
|
|
163
|
+
);
|
|
143
164
|
(console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level] = (...args: unknown[]) => {
|
|
144
165
|
// Original console first — preserve page-author dev experience even if
|
|
145
166
|
// the bunite plumbing throws further down (shouldn't, but defensive).
|
|
146
|
-
try {
|
|
167
|
+
try {
|
|
168
|
+
original(...args);
|
|
169
|
+
} catch {
|
|
170
|
+
/* extreme: original throws */
|
|
171
|
+
}
|
|
147
172
|
try {
|
|
148
173
|
const entry: ConsoleEntry = {
|
|
149
174
|
level,
|
|
@@ -156,7 +181,9 @@ function installConsoleProxy() {
|
|
|
156
181
|
}
|
|
157
182
|
batch.push(entry);
|
|
158
183
|
scheduleFlush();
|
|
159
|
-
} catch {
|
|
184
|
+
} catch {
|
|
185
|
+
/* never propagate to page */
|
|
186
|
+
}
|
|
160
187
|
};
|
|
161
188
|
}
|
|
162
189
|
}
|
|
@@ -176,7 +203,9 @@ function windowCap() {
|
|
|
176
203
|
const wc = await rt.window();
|
|
177
204
|
return await wc.current();
|
|
178
205
|
})();
|
|
179
|
-
_windowCap.catch(() => {
|
|
206
|
+
_windowCap.catch(() => {
|
|
207
|
+
_windowCap = null;
|
|
208
|
+
});
|
|
180
209
|
}
|
|
181
210
|
return _windowCap;
|
|
182
211
|
}
|
|
@@ -193,13 +222,25 @@ function isDragHit(target: EventTarget | null): boolean {
|
|
|
193
222
|
// call — lazy resolution (bootstrap + window + current) would otherwise miss
|
|
194
223
|
// the drag's first frames while the cursor has already moved.
|
|
195
224
|
void windowCap();
|
|
196
|
-
document.addEventListener(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
},
|
|
225
|
+
document.addEventListener(
|
|
226
|
+
"mousedown",
|
|
227
|
+
(e) => {
|
|
228
|
+
if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
|
|
229
|
+
windowCap()
|
|
230
|
+
.then((c) => c.beginMoveDrag())
|
|
231
|
+
.catch(() => {});
|
|
232
|
+
},
|
|
233
|
+
true,
|
|
234
|
+
);
|
|
235
|
+
document.addEventListener(
|
|
236
|
+
"dblclick",
|
|
237
|
+
(e) => {
|
|
238
|
+
if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
|
|
239
|
+
windowCap()
|
|
240
|
+
.then((c) => c.toggleMaximize())
|
|
241
|
+
.catch(() => {});
|
|
242
|
+
},
|
|
243
|
+
true,
|
|
244
|
+
);
|
|
204
245
|
|
|
205
246
|
import "../webview/native";
|
package/src/rpc/encrypt.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CloseInfo } from "./peer";
|
|
1
2
|
import type { BytesPipe } from "./transport";
|
|
2
3
|
|
|
3
4
|
const VERSION = 1;
|
|
@@ -11,7 +12,10 @@ function toBufferSource(view: Uint8Array): ArrayBuffer {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
async function importAesGcmKey(rawKey: Uint8Array): Promise<CryptoKey> {
|
|
14
|
-
return crypto.subtle.importKey("raw", toBufferSource(rawKey), "AES-GCM", false, [
|
|
15
|
+
return crypto.subtle.importKey("raw", toBufferSource(rawKey), "AES-GCM", false, [
|
|
16
|
+
"encrypt",
|
|
17
|
+
"decrypt",
|
|
18
|
+
]);
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
// WebCrypto AES-256-GCM (browser / preload). For Bun-side use `host/encryptedPipe.ts` (node:crypto).
|
|
@@ -21,7 +25,16 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
21
25
|
let sendChain: Promise<void> = Promise.resolve();
|
|
22
26
|
let recvChain: Promise<void> = Promise.resolve();
|
|
23
27
|
let closed = false;
|
|
24
|
-
|
|
28
|
+
let onCloseHandler: ((info?: CloseInfo) => void) | undefined;
|
|
29
|
+
// Report close (so the Connection tears down) for both a base disconnect and a
|
|
30
|
+
// local crypto/frame failure, then close the base transport.
|
|
31
|
+
const closeOnce = (info?: CloseInfo) => {
|
|
32
|
+
if (closed) return;
|
|
33
|
+
closed = true;
|
|
34
|
+
onCloseHandler?.(info);
|
|
35
|
+
base.close();
|
|
36
|
+
};
|
|
37
|
+
base.onClose?.((info) => closeOnce(info));
|
|
25
38
|
|
|
26
39
|
base.setReceive((frame) => {
|
|
27
40
|
if (closed) return;
|
|
@@ -51,7 +64,11 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
51
64
|
try {
|
|
52
65
|
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
53
66
|
const ivBuf = toBufferSource(iv);
|
|
54
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
67
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
68
|
+
{ name: "AES-GCM", iv: ivBuf },
|
|
69
|
+
key,
|
|
70
|
+
payload,
|
|
71
|
+
);
|
|
55
72
|
const encArr = new Uint8Array(encrypted);
|
|
56
73
|
const out = new Uint8Array(HEADER_LENGTH + encArr.byteLength);
|
|
57
74
|
out[0] = VERSION;
|
|
@@ -66,9 +83,11 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
66
83
|
setReceive(handler) {
|
|
67
84
|
downstream = handler;
|
|
68
85
|
},
|
|
86
|
+
onClose(h) {
|
|
87
|
+
onCloseHandler = h;
|
|
88
|
+
},
|
|
69
89
|
close() {
|
|
70
90
|
closeOnce();
|
|
71
91
|
},
|
|
72
92
|
};
|
|
73
93
|
}
|
|
74
|
-
|
package/src/rpc/error.ts
CHANGED
|
@@ -22,14 +22,9 @@ export type ResourceExhaustedReason =
|
|
|
22
22
|
| "stream_credit_window"
|
|
23
23
|
| "rate_limited";
|
|
24
24
|
|
|
25
|
-
export type UnavailableReason =
|
|
26
|
-
| "peer_closing"
|
|
27
|
-
| "goaway"
|
|
28
|
-
| "plugin_unloading";
|
|
25
|
+
export type UnavailableReason = "peer_closing" | "goaway" | "plugin_unloading";
|
|
29
26
|
|
|
30
|
-
export type AlreadyExistsReason =
|
|
31
|
-
| "name_collision"
|
|
32
|
-
| "reserved_namespace";
|
|
27
|
+
export type AlreadyExistsReason = "name_collision" | "reserved_namespace";
|
|
33
28
|
|
|
34
29
|
export type RetrySpec =
|
|
35
30
|
| { kind: "never" }
|
package/src/rpc/framework.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { AnyCapDef } from "./schema";
|
|
2
|
+
import { call, cap, defineCap, stream } from "./schema";
|
|
3
3
|
|
|
4
4
|
/** Window state for custom-titlebar rendering (max/restore glyph + focus ring). */
|
|
5
5
|
export interface WindowState {
|
|
@@ -10,7 +10,7 @@ export interface WindowState {
|
|
|
10
10
|
|
|
11
11
|
export const BrowserWindowCap = defineCap("bunite.BrowserWindow", {
|
|
12
12
|
focus: call<void, void>(),
|
|
13
|
-
close: call<void, void>(),
|
|
13
|
+
close: call<void, void>(), // vetoable — routes through close-requested
|
|
14
14
|
setBounds: call<{ x: number; y: number; w: number; h: number }, void>(),
|
|
15
15
|
setTitle: call<{ title: string }, void>(),
|
|
16
16
|
id: call<void, number>({ idempotent: true }),
|
|
@@ -21,7 +21,7 @@ export const BrowserWindowCap = defineCap("bunite.BrowserWindow", {
|
|
|
21
21
|
unmaximize: call<void, void>(),
|
|
22
22
|
toggleMaximize: call<void, void>(),
|
|
23
23
|
getState: call<void, WindowState>({ idempotent: true }),
|
|
24
|
-
stateWatch: stream<void, WindowState>(),
|
|
24
|
+
stateWatch: stream<void, WindowState>(), // emits current state on subscribe, then on change
|
|
25
25
|
// Start an OS window move (Tauri startDragging equiv) — call from a custom
|
|
26
26
|
// titlebar mousedown. Preload auto-calls this for `app-region: drag`; manual
|
|
27
27
|
// callers use it for custom hit-testing. Start-only; native follows the cursor.
|
|
@@ -30,8 +30,14 @@ export const BrowserWindowCap = defineCap("bunite.BrowserWindow", {
|
|
|
30
30
|
|
|
31
31
|
export const WindowCap = defineCap("bunite.Window", {
|
|
32
32
|
create: call<WindowCreateOpts, typeof BrowserWindowCap>({ returns: cap(BrowserWindowCap) }),
|
|
33
|
-
list: call<void, typeof BrowserWindowCap>({
|
|
34
|
-
|
|
33
|
+
list: call<void, typeof BrowserWindowCap>({
|
|
34
|
+
returns: cap.array(BrowserWindowCap),
|
|
35
|
+
idempotent: true,
|
|
36
|
+
}),
|
|
37
|
+
current: call<void, typeof BrowserWindowCap>({
|
|
38
|
+
returns: cap(BrowserWindowCap),
|
|
39
|
+
idempotent: true,
|
|
40
|
+
}),
|
|
35
41
|
focus: call<{ id?: number; label?: string }, void>(),
|
|
36
42
|
close: call<{ id?: number; label?: string }, void>(),
|
|
37
43
|
});
|
|
@@ -43,12 +49,16 @@ export interface WindowCreateOpts {
|
|
|
43
49
|
label?: string;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
export const FileRefCap = defineCap(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
export const FileRefCap = defineCap(
|
|
53
|
+
"bunite.FileRef",
|
|
54
|
+
{
|
|
55
|
+
text: call<void, string>({ idempotent: true }),
|
|
56
|
+
bytes: call<void, Uint8Array>({ idempotent: true }),
|
|
57
|
+
path: call<void, string>({ idempotent: true }),
|
|
58
|
+
revoke: call<void, void>(),
|
|
59
|
+
},
|
|
60
|
+
{ disposal: { method: "revoke" } },
|
|
61
|
+
);
|
|
52
62
|
|
|
53
63
|
export const DialogsCap = defineCap("bunite.Dialogs", {
|
|
54
64
|
openFile: call<DialogOpenFileOpts, typeof FileRefCap>({ returns: cap.array(FileRefCap) }),
|
|
@@ -184,7 +194,11 @@ export type BoundingRectResult =
|
|
|
184
194
|
/** `visible` = rect has size AND intersects the frame's viewport. opacity /
|
|
185
195
|
* visibility:hidden / occlusion are NOT checked — agent must `evaluate` for those. */
|
|
186
196
|
| { ok: true; rect: { x: number; y: number; width: number; height: number }; visible: boolean }
|
|
187
|
-
| {
|
|
197
|
+
| {
|
|
198
|
+
ok: false;
|
|
199
|
+
code: "not_found" | "runtime_error" | "cross_origin" | "not_supported";
|
|
200
|
+
message: string;
|
|
201
|
+
};
|
|
188
202
|
|
|
189
203
|
export interface Frame {
|
|
190
204
|
frameId: string;
|
|
@@ -201,14 +215,39 @@ export type ListFramesResult =
|
|
|
201
215
|
export type DownloadPolicy = "auto" | "ask" | "block";
|
|
202
216
|
|
|
203
217
|
export type DownloadEvent =
|
|
204
|
-
| {
|
|
218
|
+
| {
|
|
219
|
+
kind: "started";
|
|
220
|
+
id: string;
|
|
221
|
+
url: string;
|
|
222
|
+
suggestedFilename: string;
|
|
223
|
+
mimeType?: string;
|
|
224
|
+
sizeBytes?: number;
|
|
225
|
+
}
|
|
205
226
|
| { kind: "progress"; id: string; receivedBytes: number; totalBytes?: number }
|
|
206
227
|
| { kind: "completed"; id: string; localPath: string }
|
|
207
228
|
| { kind: "failed"; id: string; reason: string }
|
|
208
|
-
| {
|
|
229
|
+
| {
|
|
230
|
+
kind: "blocked";
|
|
231
|
+
id: string;
|
|
232
|
+
url: string;
|
|
233
|
+
reason:
|
|
234
|
+
| "host-policy"
|
|
235
|
+
| "backend-block"
|
|
236
|
+
| "mime-blocked"
|
|
237
|
+
| "not_supported"
|
|
238
|
+
| "ask-not-implemented";
|
|
239
|
+
};
|
|
209
240
|
|
|
210
241
|
export type WaitForDownloadResult =
|
|
211
|
-
| {
|
|
242
|
+
| {
|
|
243
|
+
ok: true;
|
|
244
|
+
id: string;
|
|
245
|
+
suggestedFilename: string;
|
|
246
|
+
url: string;
|
|
247
|
+
mimeType?: string;
|
|
248
|
+
sizeBytes?: number;
|
|
249
|
+
localPath: string;
|
|
250
|
+
}
|
|
212
251
|
| { ok: false; code: "timeout" | "blocked" | "failed" | "not_supported"; message: string };
|
|
213
252
|
|
|
214
253
|
export interface SetDownloadPolicyArgs {
|
|
@@ -235,8 +274,12 @@ export interface ExtendPopupTimeoutArgs {
|
|
|
235
274
|
gracePeriodMs: number;
|
|
236
275
|
}
|
|
237
276
|
export type ExtendPopupTimeoutResult =
|
|
238
|
-
| { ok: true; deadlineMs: number }
|
|
239
|
-
| {
|
|
277
|
+
| { ok: true; deadlineMs: number } // epoch ms of the new deadline
|
|
278
|
+
| {
|
|
279
|
+
ok: false;
|
|
280
|
+
code: "not_found" | "already_adopted" | "already_dismissed" | "cap_exceeded";
|
|
281
|
+
message: string;
|
|
282
|
+
};
|
|
240
283
|
|
|
241
284
|
/** Surface lifecycle event arm before the surface pipeline stamps `epoch`. */
|
|
242
285
|
export type SurfaceEventBase =
|
|
@@ -268,7 +311,11 @@ export interface NavigationState {
|
|
|
268
311
|
|
|
269
312
|
export type EvaluateResult =
|
|
270
313
|
| { ok: true; value: unknown }
|
|
271
|
-
| {
|
|
314
|
+
| {
|
|
315
|
+
ok: false;
|
|
316
|
+
code: "cross_origin" | "runtime_error" | "not_supported" | "timeout";
|
|
317
|
+
message: string;
|
|
318
|
+
};
|
|
272
319
|
|
|
273
320
|
/** Modifier bitmask for input dispatch. Backends translate to native form. */
|
|
274
321
|
export type Modifier = "alt" | "ctrl" | "meta" | "shift";
|
|
@@ -281,7 +328,10 @@ export interface ClickArgs {
|
|
|
281
328
|
clickCount?: number;
|
|
282
329
|
modifiers?: Modifier[];
|
|
283
330
|
}
|
|
284
|
-
export interface TypeArgs {
|
|
331
|
+
export interface TypeArgs {
|
|
332
|
+
surfaceId: number;
|
|
333
|
+
text: string;
|
|
334
|
+
}
|
|
285
335
|
export interface PressArgs {
|
|
286
336
|
surfaceId: number;
|
|
287
337
|
key: string;
|
|
@@ -390,7 +440,11 @@ export interface ScreenshotArgs {
|
|
|
390
440
|
}
|
|
391
441
|
export type ScreenshotResult =
|
|
392
442
|
| { ok: true; data: Uint8Array; mime: string; format: "png" | "jpeg" }
|
|
393
|
-
| {
|
|
443
|
+
| {
|
|
444
|
+
ok: false;
|
|
445
|
+
code: "not_supported" | "runtime_error" | "timeout" | "black_frame";
|
|
446
|
+
message: string;
|
|
447
|
+
};
|
|
394
448
|
|
|
395
449
|
export interface ResolveAndClickArgs {
|
|
396
450
|
surfaceId: number;
|
|
@@ -405,18 +459,29 @@ export interface ResolveAndClickArgs {
|
|
|
405
459
|
* CEF/WV2 CDP `Input.dispatchMouseEvent` produces trusted events; mac NSEvent
|
|
406
460
|
* direct dispatch is also trusted. All shipped backends report `true`. */
|
|
407
461
|
export type ResolveAndClickResult =
|
|
408
|
-
| {
|
|
409
|
-
|
|
462
|
+
| {
|
|
463
|
+
ok: true;
|
|
464
|
+
rect: { x: number; y: number; width: number; height: number };
|
|
465
|
+
isTrustedEvent: boolean;
|
|
466
|
+
}
|
|
467
|
+
| {
|
|
468
|
+
ok: false;
|
|
469
|
+
code: "not_found" | "not_visible" | "runtime_error" | "cross_origin" | "not_supported";
|
|
470
|
+
message: string;
|
|
471
|
+
};
|
|
410
472
|
|
|
411
473
|
export const SurfaceCap = defineCap("bunite.Surface", {
|
|
412
|
-
init: call<
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
474
|
+
init: call<
|
|
475
|
+
{
|
|
476
|
+
src: string;
|
|
477
|
+
x: number;
|
|
478
|
+
y: number;
|
|
479
|
+
width: number;
|
|
480
|
+
height: number;
|
|
481
|
+
hidden?: boolean;
|
|
482
|
+
},
|
|
483
|
+
{ surfaceId: number }
|
|
484
|
+
>(),
|
|
420
485
|
resize: call<{ surfaceId: number; x: number; y: number; w: number; h: number }, void>(),
|
|
421
486
|
remove: call<{ surfaceId: number }, void>(),
|
|
422
487
|
setHidden: call<{ surfaceId: number; hidden: boolean }, void>(),
|
|
@@ -438,7 +503,9 @@ export const SurfaceCap = defineCap("bunite.Surface", {
|
|
|
438
503
|
waitForFunction: call<WaitForFunctionArgs, WaitResult>(),
|
|
439
504
|
respondToDialog: call<RespondToDialogArgs, void>(),
|
|
440
505
|
setDialogTimeout: call<SetDialogTimeoutArgs, void>(),
|
|
441
|
-
getConsoleBuffer: call<{ surfaceId: number; clear?: boolean }, ConsoleEntry[]>({
|
|
506
|
+
getConsoleBuffer: call<{ surfaceId: number; clear?: boolean }, ConsoleEntry[]>({
|
|
507
|
+
idempotent: true,
|
|
508
|
+
}),
|
|
442
509
|
surfaceEvents: stream<{ surfaceId: number }, SurfaceEvent>(),
|
|
443
510
|
dialogs: stream<{ surfaceId: number }, DialogEvent>(),
|
|
444
511
|
consoleEvents: stream<{ surfaceId: number }, ConsoleEntry>(),
|
|
@@ -475,7 +542,10 @@ export const RuntimeCap = defineCap("bunite.Runtime", {
|
|
|
475
542
|
theme: call<void, "light" | "dark">({ idempotent: true }),
|
|
476
543
|
themeWatch: stream<void, "light" | "dark">(),
|
|
477
544
|
surface: call<void, typeof SurfaceCap>({ returns: cap(SurfaceCap), idempotent: true }),
|
|
478
|
-
reporting: call<void, typeof PageReportingCap>({
|
|
545
|
+
reporting: call<void, typeof PageReportingCap>({
|
|
546
|
+
returns: cap(PageReportingCap),
|
|
547
|
+
idempotent: true,
|
|
548
|
+
}),
|
|
479
549
|
popupMetrics: call<void, PopupMetrics>({ idempotent: true }),
|
|
480
550
|
});
|
|
481
551
|
|
|
@@ -491,7 +561,7 @@ export const FRAMEWORK_TYPE_IDS = {
|
|
|
491
561
|
PageReporting: 9,
|
|
492
562
|
} as const;
|
|
493
563
|
|
|
494
|
-
const FRAMEWORK_CAP_TYPE_IDS = new Map<
|
|
564
|
+
const FRAMEWORK_CAP_TYPE_IDS = new Map<AnyCapDef, number>([
|
|
495
565
|
[RuntimeCap, FRAMEWORK_TYPE_IDS.Runtime],
|
|
496
566
|
[WindowCap, FRAMEWORK_TYPE_IDS.Window],
|
|
497
567
|
[DialogsCap, FRAMEWORK_TYPE_IDS.Dialogs],
|
|
@@ -503,6 +573,6 @@ const FRAMEWORK_CAP_TYPE_IDS = new Map<CapDef<any, any>, number>([
|
|
|
503
573
|
[PageReportingCap, FRAMEWORK_TYPE_IDS.PageReporting],
|
|
504
574
|
]);
|
|
505
575
|
|
|
506
|
-
export function frameworkTypeIdOf(cap:
|
|
576
|
+
export function frameworkTypeIdOf(cap: AnyCapDef): number | undefined {
|
|
507
577
|
return FRAMEWORK_CAP_TYPE_IDS.get(cap);
|
|
508
578
|
}
|