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.
@@ -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) return Promise.resolve(_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")), { once: true });
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(createWebSocketPipe(ws as unknown as WebSocketLike), rawKey);
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: CapDef<any, any> | Schema<SchemaRoots>): Promise<unknown> => {
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 () => (await ensureConnection()).runtime() as ClientOf<typeof import("../rpc/framework").RuntimeCap>;
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 { return JSON.stringify(a); } catch { return String(a); }
102
+ try {
103
+ return JSON.stringify(a);
104
+ } catch {
105
+ return String(a);
106
+ }
93
107
  }
94
108
 
95
- let reportingCap: Promise<{ reportConsoleBatch(args: { entries: ConsoleEntry[] }): Promise<void> | void }> | null = null;
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) { clearTimeout(batchTimer); batchTimer = null; }
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(console);
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 { original(...args); } catch { /* extreme: original throws */ }
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 { /* never propagate to page */ }
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(() => { _windowCap = null; });
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("mousedown", (e) => {
197
- if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
198
- windowCap().then((c) => c.beginMoveDrag()).catch(() => {});
199
- }, true);
200
- document.addEventListener("dblclick", (e) => {
201
- if (e.button !== 0 || window.self !== window.top || !isDragHit(e.target)) return;
202
- windowCap().then((c) => c.toggleMaximize()).catch(() => {});
203
- }, true);
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";
@@ -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, ["encrypt", "decrypt"]);
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
- const closeOnce = () => { if (!closed) { closed = true; base.close(); } };
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({ name: "AES-GCM", iv: ivBuf }, key, payload);
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" }
@@ -1,5 +1,5 @@
1
- import { call, defineCap, stream, cap } from "./schema";
2
- import type { CapDef } from "./schema";
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>(), // vetoable — routes through close-requested
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>(), // emits current state on subscribe, then on change
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>({ returns: cap.array(BrowserWindowCap), idempotent: true }),
34
- current: call<void, typeof BrowserWindowCap>({ returns: cap(BrowserWindowCap), idempotent: true }),
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("bunite.FileRef", {
47
- text: call<void, string>({ idempotent: true }),
48
- bytes: call<void, Uint8Array>({ idempotent: true }),
49
- path: call<void, string>({ idempotent: true }),
50
- revoke: call<void, void>(),
51
- }, { disposal: { method: "revoke" } });
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
- | { ok: false; code: "not_found" | "runtime_error" | "cross_origin" | "not_supported"; message: string };
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
- | { kind: "started"; id: string; url: string; suggestedFilename: string; mimeType?: string; sizeBytes?: number }
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
- | { kind: "blocked"; id: string; url: string; reason: "host-policy" | "backend-block" | "mime-blocked" | "not_supported" | "ask-not-implemented" };
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
- | { ok: true; id: string; suggestedFilename: string; url: string; mimeType?: string; sizeBytes?: number; localPath: string }
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 } // epoch ms of the new deadline
239
- | { ok: false; code: "not_found" | "already_adopted" | "already_dismissed" | "cap_exceeded"; message: string };
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
- | { ok: false; code: "cross_origin" | "runtime_error" | "not_supported" | "timeout"; message: string };
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 { surfaceId: number; text: string }
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
- | { ok: false; code: "not_supported" | "runtime_error" | "timeout" | "black_frame"; message: string };
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
- | { ok: true; rect: { x: number; y: number; width: number; height: number }; isTrustedEvent: boolean }
409
- | { ok: false; code: "not_found" | "not_visible" | "runtime_error" | "cross_origin" | "not_supported"; message: string };
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
- src: string;
414
- x: number;
415
- y: number;
416
- width: number;
417
- height: number;
418
- hidden?: boolean;
419
- }, { surfaceId: number }>(),
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[]>({ idempotent: true }),
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>({ returns: cap(PageReportingCap), idempotent: true }),
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<CapDef<any, any>, number>([
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: CapDef<any, any>): number | undefined {
576
+ export function frameworkTypeIdOf(cap: AnyCapDef): number | undefined {
507
577
  return FRAMEWORK_CAP_TYPE_IDS.get(cap);
508
578
  }