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/rpc/renderer.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type AnyCapDef,
|
|
3
|
+
type ClientOf,
|
|
4
|
+
type Connection,
|
|
2
5
|
createConnection,
|
|
3
6
|
createFrameTransport,
|
|
4
7
|
createWebSocketPipe,
|
|
5
|
-
type Connection,
|
|
6
|
-
type CapDef,
|
|
7
8
|
type Schema,
|
|
8
9
|
type SchemaRoots,
|
|
9
|
-
type ClientOf,
|
|
10
10
|
type WebSocketLike,
|
|
11
11
|
} from "./index";
|
|
12
12
|
|
|
@@ -22,8 +22,10 @@ export interface BuniteWebGlobal {
|
|
|
22
22
|
declare global {
|
|
23
23
|
interface Window {
|
|
24
24
|
host?: {
|
|
25
|
-
bootstrap<C extends
|
|
26
|
-
bootstrap<R extends SchemaRoots>(
|
|
25
|
+
bootstrap<C extends AnyCapDef>(cap: C): Promise<ClientOf<C>>;
|
|
26
|
+
bootstrap<R extends SchemaRoots>(
|
|
27
|
+
schema: Schema<R>,
|
|
28
|
+
): Promise<{ [K in keyof R]: ClientOf<R[K]> }>;
|
|
27
29
|
runtime(): Promise<ClientOf<typeof import("./framework").RuntimeCap>>;
|
|
28
30
|
releaseRef(proxy: unknown): Promise<void>;
|
|
29
31
|
/** Full Connection for renderer-as-server (serve / serveAll / unserve / replace / on). */
|
|
@@ -62,7 +64,9 @@ function ensureWebConnection(path = "/rpc"): Promise<Connection> {
|
|
|
62
64
|
ws.binaryType = "arraybuffer";
|
|
63
65
|
await new Promise<void>((resolve, reject) => {
|
|
64
66
|
ws.addEventListener("open", () => resolve(), { once: true });
|
|
65
|
-
ws.addEventListener("error", () => reject(new Error("web RPC ws connect failed")), {
|
|
67
|
+
ws.addEventListener("error", () => reject(new Error("web RPC ws connect failed")), {
|
|
68
|
+
once: true,
|
|
69
|
+
});
|
|
66
70
|
});
|
|
67
71
|
const conn = createConnection({
|
|
68
72
|
transport: createFrameTransport(createWebSocketPipe(ws as unknown as WebSocketLike)),
|
|
@@ -79,9 +83,11 @@ function ensureWebConnection(path = "/rpc"): Promise<Connection> {
|
|
|
79
83
|
return attempt;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
export function bootstrap<C extends
|
|
83
|
-
export function bootstrap<R extends SchemaRoots>(
|
|
84
|
-
|
|
86
|
+
export function bootstrap<C extends AnyCapDef>(cap: C): Promise<ClientOf<C>>;
|
|
87
|
+
export function bootstrap<R extends SchemaRoots>(
|
|
88
|
+
schema: Schema<R>,
|
|
89
|
+
): Promise<{ [K in keyof R]: ClientOf<R[K]> }>;
|
|
90
|
+
export async function bootstrap(target: AnyCapDef | Schema<any>): Promise<unknown> {
|
|
85
91
|
if (isNative()) {
|
|
86
92
|
if (!window.host?.bootstrap) throw new Error("host preload not ready");
|
|
87
93
|
return (window.host.bootstrap as (t: unknown) => Promise<unknown>)(target);
|
package/src/rpc/schema.ts
CHANGED
|
@@ -10,31 +10,37 @@ declare const EXPORTED_CAP_BRAND: unique symbol;
|
|
|
10
10
|
|
|
11
11
|
export type ReturnsKind = "type" | "cap" | "capArray" | "capRecord";
|
|
12
12
|
|
|
13
|
-
export type CapRefToken<C extends
|
|
13
|
+
export type CapRefToken<C extends AnyCapDef> = {
|
|
14
14
|
readonly [CAP_REF_TAG]: true;
|
|
15
15
|
readonly cap: C;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export type CapArrayToken<C extends
|
|
18
|
+
export type CapArrayToken<C extends AnyCapDef> = {
|
|
19
19
|
readonly [CAP_ARRAY_TAG]: true;
|
|
20
20
|
readonly cap: C;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export type CapRecordToken<C extends
|
|
23
|
+
export type CapRecordToken<C extends AnyCapDef> = {
|
|
24
24
|
readonly [CAP_RECORD_TAG]: true;
|
|
25
25
|
readonly cap: C;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
export type AnyCapToken<C extends
|
|
28
|
+
export type AnyCapToken<C extends AnyCapDef = AnyCapDef> =
|
|
29
29
|
| CapRefToken<C>
|
|
30
30
|
| CapArrayToken<C>
|
|
31
31
|
| CapRecordToken<C>;
|
|
32
32
|
|
|
33
|
-
function _cap<C extends
|
|
33
|
+
function _cap<C extends AnyCapDef>(c: C): CapRefToken<C> {
|
|
34
34
|
return { [CAP_REF_TAG]: true, cap: c };
|
|
35
35
|
}
|
|
36
|
-
_cap.array = <C extends
|
|
37
|
-
|
|
36
|
+
_cap.array = <C extends AnyCapDef>(c: C): CapArrayToken<C> => ({
|
|
37
|
+
[CAP_ARRAY_TAG]: true,
|
|
38
|
+
cap: c,
|
|
39
|
+
});
|
|
40
|
+
_cap.record = <C extends AnyCapDef>(c: C): CapRecordToken<C> => ({
|
|
41
|
+
[CAP_RECORD_TAG]: true,
|
|
42
|
+
cap: c,
|
|
43
|
+
});
|
|
38
44
|
export const cap = _cap;
|
|
39
45
|
|
|
40
46
|
export function isCapRef(v: unknown): v is CapRefToken<any> {
|
|
@@ -75,15 +81,18 @@ export function isStreamDef(v: unknown): v is StreamDef {
|
|
|
75
81
|
return typeof v === "object" && v !== null && (v as any)[STREAM_TAG] === true;
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
export function call<P, C extends
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
export function call<P, C extends AnyCapDef>(opts: {
|
|
85
|
+
returns: CapRefToken<C>;
|
|
86
|
+
idempotent?: boolean;
|
|
87
|
+
}): CallDef<P, CapRefToken<C>>;
|
|
88
|
+
export function call<P, C extends AnyCapDef>(opts: {
|
|
89
|
+
returns: CapArrayToken<C>;
|
|
90
|
+
idempotent?: boolean;
|
|
91
|
+
}): CallDef<P, CapArrayToken<C>>;
|
|
92
|
+
export function call<P, C extends AnyCapDef>(opts: {
|
|
93
|
+
returns: CapRecordToken<C>;
|
|
94
|
+
idempotent?: boolean;
|
|
95
|
+
}): CallDef<P, CapRecordToken<C>>;
|
|
87
96
|
export function call<P = void, R = void>(opts?: { idempotent?: boolean }): CallDef<P, R>;
|
|
88
97
|
export function call(opts?: { idempotent?: boolean; returns?: AnyCapToken }): CallDef<any, any> {
|
|
89
98
|
return {
|
|
@@ -93,7 +102,9 @@ export function call(opts?: { idempotent?: boolean; returns?: AnyCapToken }): Ca
|
|
|
93
102
|
};
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
export function stream<P = void, Y = unknown>(opts?: {
|
|
105
|
+
export function stream<P = void, Y = unknown>(opts?: {
|
|
106
|
+
hint?: Record<string, unknown>;
|
|
107
|
+
}): StreamDef<P, Y> {
|
|
97
108
|
return { [STREAM_TAG]: true, hint: opts?.hint };
|
|
98
109
|
}
|
|
99
110
|
|
|
@@ -104,7 +115,10 @@ export interface DisposalSpec<M extends MethodsRecord = MethodsRecord> {
|
|
|
104
115
|
|
|
105
116
|
export type MethodsRecord = Record<string, MethodDef>;
|
|
106
117
|
|
|
107
|
-
export interface CapDef<
|
|
118
|
+
export interface CapDef<
|
|
119
|
+
M extends MethodsRecord = MethodsRecord,
|
|
120
|
+
D extends DisposalSpec<M> | undefined = undefined,
|
|
121
|
+
> {
|
|
108
122
|
readonly [CAP_TAG]: true;
|
|
109
123
|
readonly name: string;
|
|
110
124
|
readonly version?: string;
|
|
@@ -112,16 +126,17 @@ export interface CapDef<M extends MethodsRecord = MethodsRecord, D extends Dispo
|
|
|
112
126
|
readonly disposal: D;
|
|
113
127
|
}
|
|
114
128
|
|
|
129
|
+
export type AnyCapDef = CapDef<any, any>;
|
|
130
|
+
|
|
115
131
|
export interface DefineCapOpts<M extends MethodsRecord, D extends DisposalSpec<M> | undefined> {
|
|
116
132
|
version?: string | number;
|
|
117
133
|
disposal?: D;
|
|
118
134
|
}
|
|
119
135
|
|
|
120
|
-
export function defineCap<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
): CapDef<M, D> {
|
|
136
|
+
export function defineCap<
|
|
137
|
+
M extends MethodsRecord,
|
|
138
|
+
D extends DisposalSpec<M> | undefined = undefined,
|
|
139
|
+
>(name: string, methods: M, opts?: DefineCapOpts<M, D>): CapDef<M, D> {
|
|
125
140
|
return {
|
|
126
141
|
[CAP_TAG]: true,
|
|
127
142
|
name,
|
|
@@ -136,7 +151,7 @@ export function isCapDef(v: unknown): v is CapDef {
|
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
// Schema = grouping sugar (Record<rootName, CapDef>). TS atomicity for serveAll.
|
|
139
|
-
export type SchemaRoots = Record<string,
|
|
154
|
+
export type SchemaRoots = Record<string, AnyCapDef>;
|
|
140
155
|
|
|
141
156
|
export interface Schema<R extends SchemaRoots = SchemaRoots> {
|
|
142
157
|
readonly [SCHEMA_TAG]: true;
|
|
@@ -155,7 +170,7 @@ export function isSchema(v: unknown): v is Schema {
|
|
|
155
170
|
return typeof v === "object" && v !== null && (v as any)[SCHEMA_TAG] === true;
|
|
156
171
|
}
|
|
157
172
|
|
|
158
|
-
export interface ExportedCap<C extends
|
|
173
|
+
export interface ExportedCap<C extends AnyCapDef> {
|
|
159
174
|
readonly [EXPORTED_CAP_BRAND]: true;
|
|
160
175
|
readonly cap: C;
|
|
161
176
|
readonly capId: number;
|
|
@@ -168,7 +183,7 @@ export interface CallCtx {
|
|
|
168
183
|
attestation: Attestation;
|
|
169
184
|
signal: AbortSignal;
|
|
170
185
|
deadline?: number;
|
|
171
|
-
exportCap<C extends
|
|
186
|
+
exportCap<C extends AnyCapDef>(capDef: C, impl: ImplOf<C>): ExportedCap<C>;
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
export interface Attestation {
|
|
@@ -184,45 +199,55 @@ export interface Attestation {
|
|
|
184
199
|
type MaybePromise<T> = T | Promise<T>;
|
|
185
200
|
|
|
186
201
|
type ServerReturn<R> =
|
|
187
|
-
R extends CapRefToken<infer C>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
R extends CapRefToken<infer C>
|
|
203
|
+
? ExportedCap<C>
|
|
204
|
+
: R extends CapArrayToken<infer C>
|
|
205
|
+
? ExportedCap<C>[]
|
|
206
|
+
: R extends CapRecordToken<infer C>
|
|
207
|
+
? Record<string, ExportedCap<C>>
|
|
208
|
+
: R;
|
|
191
209
|
|
|
192
210
|
export type ClientReturn<R> =
|
|
193
|
-
R extends CapRefToken<infer C>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
?
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
211
|
+
R extends CapRefToken<infer C>
|
|
212
|
+
? ClientOf<C>
|
|
213
|
+
: R extends CapArrayToken<infer C>
|
|
214
|
+
? C extends CapDef<any, infer D>
|
|
215
|
+
? [D] extends [DisposalSpec]
|
|
216
|
+
? ClientOf<C>[] & Disposable
|
|
217
|
+
: ClientOf<C>[]
|
|
218
|
+
: ClientOf<C>[]
|
|
219
|
+
: R extends CapRecordToken<infer C>
|
|
220
|
+
? Record<string, ClientOf<C>>
|
|
221
|
+
: R;
|
|
201
222
|
|
|
202
223
|
export type Stream<T> = AsyncIterable<T> & Disposable & { cancel(): void };
|
|
203
224
|
|
|
204
|
-
export type ClientOf<T> =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
export type ClientOf<T> =
|
|
226
|
+
T extends CapDef<infer M, infer D>
|
|
227
|
+
? {
|
|
228
|
+
[K in keyof M]: M[K] extends CallDef<infer P, infer R>
|
|
229
|
+
? [P] extends [void]
|
|
230
|
+
? () => Promise<ClientReturn<R>>
|
|
231
|
+
: (params: P) => Promise<ClientReturn<R>>
|
|
232
|
+
: M[K] extends StreamDef<infer P, infer Y>
|
|
233
|
+
? [P] extends [void]
|
|
234
|
+
? () => Stream<Y>
|
|
235
|
+
: (params: P) => Stream<Y>
|
|
236
|
+
: never;
|
|
237
|
+
} & ([D] extends [DisposalSpec] ? Disposable : unknown)
|
|
238
|
+
: never;
|
|
239
|
+
|
|
240
|
+
export type ImplOf<T> =
|
|
241
|
+
T extends CapDef<infer M, any>
|
|
242
|
+
? {
|
|
243
|
+
[K in keyof M]: M[K] extends CallDef<infer P, infer R>
|
|
244
|
+
? (params: P, ctx: CallCtx) => MaybePromise<ServerReturn<R>>
|
|
245
|
+
: M[K] extends StreamDef<infer P, infer Y>
|
|
246
|
+
? (params: P, ctx: CallCtx) => Stream<Y>
|
|
247
|
+
: never;
|
|
248
|
+
}
|
|
249
|
+
: never;
|
|
250
|
+
|
|
251
|
+
export function methodKeys(cap: AnyCapDef): string[] {
|
|
227
252
|
return Object.keys(cap.methods);
|
|
228
253
|
}
|
package/src/rpc/stream.ts
CHANGED
|
@@ -4,7 +4,10 @@ type Setup<T> = (emit: (chunk: T) => void, signal: AbortSignal) => void | (() =>
|
|
|
4
4
|
|
|
5
5
|
class ServerStream<T> implements AsyncIterable<T>, Disposable {
|
|
6
6
|
private readonly buffer: T[] = [];
|
|
7
|
-
private readonly waiters: Array<{
|
|
7
|
+
private readonly waiters: Array<{
|
|
8
|
+
resolve: (r: IteratorResult<T>) => void;
|
|
9
|
+
reject: (e: unknown) => void;
|
|
10
|
+
}> = [];
|
|
8
11
|
private readonly ctrl = new AbortController();
|
|
9
12
|
private cleanup?: () => void;
|
|
10
13
|
private ended = false;
|
|
@@ -36,7 +39,9 @@ class ServerStream<T> implements AsyncIterable<T>, Disposable {
|
|
|
36
39
|
}
|
|
37
40
|
if (this.failure) throw this.failure;
|
|
38
41
|
if (this.ended) return { value: undefined as unknown as T, done: true };
|
|
39
|
-
return new Promise<IteratorResult<T>>((resolve, reject) =>
|
|
42
|
+
return new Promise<IteratorResult<T>>((resolve, reject) =>
|
|
43
|
+
this.waiters.push({ resolve, reject }),
|
|
44
|
+
);
|
|
40
45
|
},
|
|
41
46
|
return: async (): Promise<IteratorResult<T>> => {
|
|
42
47
|
this.dispose();
|
|
@@ -57,7 +62,11 @@ class ServerStream<T> implements AsyncIterable<T>, Disposable {
|
|
|
57
62
|
if (this.ended) return;
|
|
58
63
|
this.ended = true;
|
|
59
64
|
this.ctrl.abort();
|
|
60
|
-
try {
|
|
65
|
+
try {
|
|
66
|
+
this.cleanup?.();
|
|
67
|
+
} catch {
|
|
68
|
+
/* swallow */
|
|
69
|
+
}
|
|
61
70
|
while (this.waiters.length > 0) {
|
|
62
71
|
const w = this.waiters.shift()!;
|
|
63
72
|
w.resolve({ value: undefined as unknown as T, done: true });
|
package/src/rpc/transport.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Frame, createCodec, isFrame } from "./wire";
|
|
2
1
|
import type { Transport } from "./peer";
|
|
2
|
+
import { createCodec, type Frame, isFrame } from "./wire";
|
|
3
3
|
|
|
4
4
|
export interface BytesPipe {
|
|
5
5
|
send(bytes: Uint8Array): void;
|
|
@@ -48,21 +48,37 @@ export function createFrameTransport(pipe: BytesPipe, opts: FrameTransportOption
|
|
|
48
48
|
|
|
49
49
|
export interface WebSocketLike {
|
|
50
50
|
send(data: Uint8Array | ArrayBuffer): unknown;
|
|
51
|
-
addEventListener(
|
|
52
|
-
|
|
51
|
+
addEventListener(
|
|
52
|
+
type: "message",
|
|
53
|
+
listener: (event: { data: ArrayBuffer | Uint8Array | Blob | string }) => void,
|
|
54
|
+
): void;
|
|
55
|
+
removeEventListener?(
|
|
56
|
+
type: "message",
|
|
57
|
+
listener: (event: { data: ArrayBuffer | Uint8Array | Blob | string }) => void,
|
|
58
|
+
): void;
|
|
53
59
|
close?(): void;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export function createWebSocketPipe(ws: WebSocketLike): BytesPipe {
|
|
57
63
|
if ("binaryType" in ws) {
|
|
58
|
-
try {
|
|
64
|
+
try {
|
|
65
|
+
(ws as { binaryType?: string }).binaryType = "arraybuffer";
|
|
66
|
+
} catch {
|
|
67
|
+
/* readonly in some envs */
|
|
68
|
+
}
|
|
59
69
|
}
|
|
60
70
|
let handler: ((bytes: Uint8Array) => void) | undefined;
|
|
61
71
|
const onMessage = (event: { data: ArrayBuffer | Uint8Array | Blob | string }) => {
|
|
62
72
|
if (!handler) return;
|
|
63
73
|
const d = event.data;
|
|
64
|
-
if (d instanceof Uint8Array) {
|
|
65
|
-
|
|
74
|
+
if (d instanceof Uint8Array) {
|
|
75
|
+
handler(d);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (d instanceof ArrayBuffer) {
|
|
79
|
+
handler(new Uint8Array(d));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
66
82
|
if (typeof Blob !== "undefined" && d instanceof Blob) {
|
|
67
83
|
void d.arrayBuffer().then((buf) => handler?.(new Uint8Array(buf)));
|
|
68
84
|
return;
|
|
@@ -70,12 +86,15 @@ export function createWebSocketPipe(ws: WebSocketLike): BytesPipe {
|
|
|
70
86
|
};
|
|
71
87
|
ws.addEventListener("message", onMessage);
|
|
72
88
|
return {
|
|
73
|
-
send(bytes) {
|
|
74
|
-
|
|
89
|
+
send(bytes) {
|
|
90
|
+
ws.send(bytes);
|
|
91
|
+
},
|
|
92
|
+
setReceive(h) {
|
|
93
|
+
handler = h;
|
|
94
|
+
},
|
|
75
95
|
close() {
|
|
76
96
|
ws.removeEventListener?.("message", onMessage);
|
|
77
97
|
ws.close?.();
|
|
78
98
|
},
|
|
79
99
|
};
|
|
80
100
|
}
|
|
81
|
-
|
package/src/rpc/wire.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Packr, Unpackr
|
|
1
|
+
import { addExtension, Packr, Unpackr } from "msgpackr";
|
|
2
2
|
import type { IpcStatus } from "./error";
|
|
3
3
|
|
|
4
4
|
export type u32 = number;
|
|
@@ -75,28 +75,41 @@ export function isFrame(value: unknown): value is Frame {
|
|
|
75
75
|
if (typeof op !== "string" || !OPS.has(op as Frame["op"])) return false;
|
|
76
76
|
switch (op as Frame["op"]) {
|
|
77
77
|
case "hello":
|
|
78
|
-
return
|
|
79
|
-
|
|
78
|
+
return (
|
|
79
|
+
f.v === 1 &&
|
|
80
|
+
(f.mode === "native" || f.mode === "web") &&
|
|
81
|
+
Array.isArray(f.features) &&
|
|
82
|
+
typeof f.maxBytes === "number" &&
|
|
83
|
+
typeof f.origin === "string"
|
|
84
|
+
);
|
|
80
85
|
case "goaway":
|
|
81
86
|
return f.reason === undefined || typeof f.reason === "string";
|
|
82
87
|
case "call":
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
return (
|
|
89
|
+
typeof f.id === "number" &&
|
|
90
|
+
typeof f.method === "string" &&
|
|
91
|
+
typeof f.target === "object" &&
|
|
92
|
+
f.target !== null &&
|
|
93
|
+
(f.target as { kind?: unknown }).kind === "cap" &&
|
|
94
|
+
typeof (f.target as { id?: unknown }).id === "number"
|
|
95
|
+
);
|
|
87
96
|
case "result":
|
|
88
97
|
return typeof f.id === "number" && (f.ok === true || f.ok === false);
|
|
89
98
|
case "cancel":
|
|
90
99
|
return typeof f.id === "number";
|
|
91
100
|
case "stream":
|
|
92
|
-
return
|
|
93
|
-
|
|
101
|
+
return (
|
|
102
|
+
typeof f.id === "number" &&
|
|
103
|
+
typeof f.ev === "string" &&
|
|
104
|
+
(f.ev === "next" || f.ev === "credit" || f.ev === "end" || f.ev === "error")
|
|
105
|
+
);
|
|
94
106
|
case "drop":
|
|
95
107
|
return Array.isArray(f.caps);
|
|
96
108
|
case "cap_revoked":
|
|
97
109
|
if (!Array.isArray(f.capIds)) return false;
|
|
98
110
|
for (const id of f.capIds) {
|
|
99
|
-
if (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id >
|
|
111
|
+
if (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > 0xffffffff)
|
|
112
|
+
return false;
|
|
100
113
|
}
|
|
101
114
|
return true;
|
|
102
115
|
}
|