bunite-core 0.17.1 → 0.17.2
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 +12 -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 +18 -11
- package/src/preload/runtime.built.js +1 -1
- package/src/preload/runtime.ts +61 -24
- package/src/rpc/encrypt.ts +15 -4
- package/src/rpc/error.ts +2 -7
- package/src/rpc/framework.ts +105 -35
- package/src/rpc/index.ts +129 -140
- package/src/rpc/peer.ts +430 -159
- package/src/rpc/renderer.ts +15 -9
- package/src/rpc/schema.ts +86 -61
- package/src/rpc/stream.ts +12 -3
- package/src/rpc/transport.ts +28 -9
- 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
|
|
|
@@ -23,15 +23,20 @@ function ensureConnection(): Promise<Connection> {
|
|
|
23
23
|
if (_connPromise) return _connPromise;
|
|
24
24
|
const attempt = (async () => {
|
|
25
25
|
const ws = new WebSocket(
|
|
26
|
-
`ws://localhost:${__buniteRpcSocketPort}/rpc?webviewId=${__buniteWebviewId}
|
|
26
|
+
`ws://localhost:${__buniteRpcSocketPort}/rpc?webviewId=${__buniteWebviewId}`,
|
|
27
27
|
);
|
|
28
28
|
ws.binaryType = "arraybuffer";
|
|
29
29
|
await new Promise<void>((resolve, reject) => {
|
|
30
30
|
ws.addEventListener("open", () => resolve(), { once: true });
|
|
31
|
-
ws.addEventListener("error", () => reject(new Error("bunite preload ws connect failed")), {
|
|
31
|
+
ws.addEventListener("error", () => reject(new Error("bunite preload ws connect failed")), {
|
|
32
|
+
once: true,
|
|
33
|
+
});
|
|
32
34
|
});
|
|
33
35
|
const rawKey = Uint8Array.from(atob(__buniteSecretKeyBase64), (c) => c.charCodeAt(0));
|
|
34
|
-
const pipe = await createEncryptedPipe(
|
|
36
|
+
const pipe = await createEncryptedPipe(
|
|
37
|
+
createWebSocketPipe(ws as unknown as WebSocketLike),
|
|
38
|
+
rawKey,
|
|
39
|
+
);
|
|
35
40
|
const conn = createConnection({
|
|
36
41
|
transport: createFrameTransport(pipe),
|
|
37
42
|
mode: "native",
|
|
@@ -53,12 +58,13 @@ w.__buniteWebviewId = __buniteWebviewId;
|
|
|
53
58
|
w.__buniteRpcSocketPort = __buniteRpcSocketPort;
|
|
54
59
|
w.host ??= {};
|
|
55
60
|
|
|
56
|
-
w.host.bootstrap = async (target:
|
|
61
|
+
w.host.bootstrap = async (target: AnyCapDef | Schema<SchemaRoots>): Promise<unknown> => {
|
|
57
62
|
const conn = await ensureConnection();
|
|
58
63
|
return (conn.bootstrap as (t: unknown) => Promise<unknown>)(target);
|
|
59
64
|
};
|
|
60
65
|
|
|
61
|
-
w.host.runtime = async () =>
|
|
66
|
+
w.host.runtime = async () =>
|
|
67
|
+
(await ensureConnection()).runtime() as ClientOf<typeof import("../rpc/framework").RuntimeCap>;
|
|
62
68
|
|
|
63
69
|
w.host.releaseRef = async (proxy: unknown): Promise<void> => {
|
|
64
70
|
(await ensureConnection()).releaseRef(proxy);
|
|
@@ -89,10 +95,16 @@ const pageBuffer = (w as any)[CONSOLE_BUFFER_KEY] as ConsoleEntry[];
|
|
|
89
95
|
function serializeArg(a: unknown): string {
|
|
90
96
|
if (typeof a === "string") return a;
|
|
91
97
|
if (a instanceof Error) return a.stack ?? a.message;
|
|
92
|
-
try {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.stringify(a);
|
|
100
|
+
} catch {
|
|
101
|
+
return String(a);
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
|
|
95
|
-
let reportingCap: Promise<{
|
|
105
|
+
let reportingCap: Promise<{
|
|
106
|
+
reportConsoleBatch(args: { entries: ConsoleEntry[] }): Promise<void> | void;
|
|
107
|
+
}> | null = null;
|
|
96
108
|
// Circuit breaker: after a failed cap fetch, hold off for a cooldown so a
|
|
97
109
|
// permanently-disconnected host doesn't burn a retry per batch (16ms cadence).
|
|
98
110
|
let nextRetryMs = 0;
|
|
@@ -115,7 +127,10 @@ let batch: ConsoleEntry[] = [];
|
|
|
115
127
|
let batchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
116
128
|
|
|
117
129
|
function flushBatch() {
|
|
118
|
-
if (batchTimer) {
|
|
130
|
+
if (batchTimer) {
|
|
131
|
+
clearTimeout(batchTimer);
|
|
132
|
+
batchTimer = null;
|
|
133
|
+
}
|
|
119
134
|
if (batch.length === 0) return;
|
|
120
135
|
const entries = batch;
|
|
121
136
|
batch = [];
|
|
@@ -139,11 +154,17 @@ function scheduleFlush() {
|
|
|
139
154
|
|
|
140
155
|
function installConsoleProxy() {
|
|
141
156
|
for (const level of ["log", "warn", "error", "info", "debug"] as const) {
|
|
142
|
-
const original = (console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level].bind(
|
|
157
|
+
const original = (console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level].bind(
|
|
158
|
+
console,
|
|
159
|
+
);
|
|
143
160
|
(console as Record<ConsoleLevel, (...a: unknown[]) => void>)[level] = (...args: unknown[]) => {
|
|
144
161
|
// Original console first — preserve page-author dev experience even if
|
|
145
162
|
// the bunite plumbing throws further down (shouldn't, but defensive).
|
|
146
|
-
try {
|
|
163
|
+
try {
|
|
164
|
+
original(...args);
|
|
165
|
+
} catch {
|
|
166
|
+
/* extreme: original throws */
|
|
167
|
+
}
|
|
147
168
|
try {
|
|
148
169
|
const entry: ConsoleEntry = {
|
|
149
170
|
level,
|
|
@@ -156,7 +177,9 @@ function installConsoleProxy() {
|
|
|
156
177
|
}
|
|
157
178
|
batch.push(entry);
|
|
158
179
|
scheduleFlush();
|
|
159
|
-
} catch {
|
|
180
|
+
} catch {
|
|
181
|
+
/* never propagate to page */
|
|
182
|
+
}
|
|
160
183
|
};
|
|
161
184
|
}
|
|
162
185
|
}
|
|
@@ -176,7 +199,9 @@ function windowCap() {
|
|
|
176
199
|
const wc = await rt.window();
|
|
177
200
|
return await wc.current();
|
|
178
201
|
})();
|
|
179
|
-
_windowCap.catch(() => {
|
|
202
|
+
_windowCap.catch(() => {
|
|
203
|
+
_windowCap = null;
|
|
204
|
+
});
|
|
180
205
|
}
|
|
181
206
|
return _windowCap;
|
|
182
207
|
}
|
|
@@ -193,13 +218,25 @@ function isDragHit(target: EventTarget | null): boolean {
|
|
|
193
218
|
// call — lazy resolution (bootstrap + window + current) would otherwise miss
|
|
194
219
|
// the drag's first frames while the cursor has already moved.
|
|
195
220
|
void windowCap();
|
|
196
|
-
document.addEventListener(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
},
|
|
221
|
+
document.addEventListener(
|
|
222
|
+
"mousedown",
|
|
223
|
+
(e) => {
|
|
224
|
+
if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
|
|
225
|
+
windowCap()
|
|
226
|
+
.then((c) => c.beginMoveDrag())
|
|
227
|
+
.catch(() => {});
|
|
228
|
+
},
|
|
229
|
+
true,
|
|
230
|
+
);
|
|
231
|
+
document.addEventListener(
|
|
232
|
+
"dblclick",
|
|
233
|
+
(e) => {
|
|
234
|
+
if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
|
|
235
|
+
windowCap()
|
|
236
|
+
.then((c) => c.toggleMaximize())
|
|
237
|
+
.catch(() => {});
|
|
238
|
+
},
|
|
239
|
+
true,
|
|
240
|
+
);
|
|
204
241
|
|
|
205
242
|
import "../webview/native";
|
package/src/rpc/encrypt.ts
CHANGED
|
@@ -11,7 +11,10 @@ function toBufferSource(view: Uint8Array): ArrayBuffer {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async function importAesGcmKey(rawKey: Uint8Array): Promise<CryptoKey> {
|
|
14
|
-
return crypto.subtle.importKey("raw", toBufferSource(rawKey), "AES-GCM", false, [
|
|
14
|
+
return crypto.subtle.importKey("raw", toBufferSource(rawKey), "AES-GCM", false, [
|
|
15
|
+
"encrypt",
|
|
16
|
+
"decrypt",
|
|
17
|
+
]);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
// WebCrypto AES-256-GCM (browser / preload). For Bun-side use `host/encryptedPipe.ts` (node:crypto).
|
|
@@ -21,7 +24,12 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
21
24
|
let sendChain: Promise<void> = Promise.resolve();
|
|
22
25
|
let recvChain: Promise<void> = Promise.resolve();
|
|
23
26
|
let closed = false;
|
|
24
|
-
const closeOnce = () => {
|
|
27
|
+
const closeOnce = () => {
|
|
28
|
+
if (!closed) {
|
|
29
|
+
closed = true;
|
|
30
|
+
base.close();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
25
33
|
|
|
26
34
|
base.setReceive((frame) => {
|
|
27
35
|
if (closed) return;
|
|
@@ -51,7 +59,11 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
51
59
|
try {
|
|
52
60
|
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
53
61
|
const ivBuf = toBufferSource(iv);
|
|
54
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
62
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
63
|
+
{ name: "AES-GCM", iv: ivBuf },
|
|
64
|
+
key,
|
|
65
|
+
payload,
|
|
66
|
+
);
|
|
55
67
|
const encArr = new Uint8Array(encrypted);
|
|
56
68
|
const out = new Uint8Array(HEADER_LENGTH + encArr.byteLength);
|
|
57
69
|
out[0] = VERSION;
|
|
@@ -71,4 +83,3 @@ export async function createEncryptedPipe(base: BytesPipe, rawKey: Uint8Array):
|
|
|
71
83
|
},
|
|
72
84
|
};
|
|
73
85
|
}
|
|
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
|
}
|