@neta-art/cohub 8.24.0 → 9.0.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/dist/board/animation.d.ts +57 -181
- package/dist/board/animation.js +374 -220
- package/dist/board/core/arrow-geometry.d.ts +33 -18
- package/dist/board/core/arrow-geometry.js +283 -52
- package/dist/board/core/draw-geometry.d.ts +22 -36
- package/dist/board/core/draw-geometry.js +148 -85
- package/dist/board/core/export-assets.d.ts +3 -6
- package/dist/board/core/export-assets.js +0 -2
- package/dist/board/core/export-plan.d.ts +12 -63
- package/dist/board/core/export-plan.js +55 -94
- package/dist/board/core/palette.d.ts +4 -21
- package/dist/board/core/palette.js +8 -13
- package/dist/board/core/scene.d.ts +33 -0
- package/dist/board/core/scene.js +302 -0
- package/dist/board/core/shape-definition.d.ts +12 -24
- package/dist/board/core/shape-definition.js +0 -19
- package/dist/board/core/shape-types.d.ts +6 -120
- package/dist/board/core/shape-types.js +5 -5
- package/dist/board/core/text-metrics.d.ts +8 -11
- package/dist/board/core/text-metrics.js +5 -34
- package/dist/board/core/tool-styles.d.ts +6 -16
- package/dist/board/core/tool-styles.js +17 -25
- package/dist/board/export/index.d.ts +10 -17
- package/dist/board/export/index.js +22 -21
- package/dist/board/export/scene.d.ts +10 -16
- package/dist/board/export/scene.js +17 -30
- package/dist/board/geometry.d.ts +27 -56
- package/dist/board/geometry.js +7 -67
- package/dist/board/headless/index.d.ts +6 -27
- package/dist/board/headless/index.js +4 -19
- package/dist/board/headless/sketch.d.ts +19 -0
- package/dist/board/headless/sketch.js +146 -0
- package/dist/board/image-key.d.ts +2 -12
- package/dist/board/image-key.js +3 -13
- package/dist/board/index.d.ts +18 -20
- package/dist/board/index.js +17 -17
- package/dist/board/media-playback.d.ts +3 -6
- package/dist/board/media-playback.js +6 -6
- package/dist/board/presets.d.ts +150 -0
- package/dist/board/presets.js +226 -0
- package/dist/board/render/board-background.d.ts +5 -3
- package/dist/board/render/board-background.js +8 -7
- package/dist/board/render/clip.d.ts +14 -0
- package/dist/board/render/clip.js +47 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +4 -5
- package/dist/board/render/media-interaction.js +1 -2
- package/dist/board/render/palette.d.ts +6 -2
- package/dist/board/render/palette.js +7 -1
- package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
- package/dist/board/render/renderers/audio-card-renderer.js +3 -3
- package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
- package/dist/board/render/renderers/base-card-renderer.js +1 -24
- package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
- package/dist/board/render/renderers/board-renderer-registry.js +6 -7
- package/dist/board/render/renderers/draw-card-renderer.js +89 -56
- package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
- package/dist/board/render/renderers/effect-card-renderer.js +197 -0
- package/dist/board/render/renderers/far-plate.d.ts +4 -21
- package/dist/board/render/renderers/far-plate.js +15 -34
- package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
- package/dist/board/render/renderers/file-card-renderer.js +8 -58
- package/dist/board/render/renderers/frame-card-renderer.js +28 -28
- package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
- package/dist/board/render/renderers/image-card-renderer.js +0 -9
- package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
- package/dist/board/render/renderers/shape-card-renderer.js +240 -0
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
- package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
- package/dist/board/render/renderers/task-card-renderer.js +14 -16
- package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
- package/dist/board/render/renderers/text-card-renderer.js +56 -46
- package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
- package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sketch-host.d.ts +14 -0
- package/dist/board/render/sketch-host.js +216 -0
- package/dist/board/render/stroke.d.ts +12 -0
- package/dist/board/render/stroke.js +90 -0
- package/dist/board/render/text-measurement.d.ts +6 -26
- package/dist/board/render/text-measurement.js +11 -30
- package/dist/board/replay.d.ts +6 -31
- package/dist/board/replay.js +45 -203
- package/dist/board/runtime/board-player.d.ts +24 -0
- package/dist/board/runtime/board-player.js +161 -0
- package/dist/board/task.d.ts +2 -10
- package/dist/board/task.js +1 -10
- package/dist/chunks/environment.d.ts +2291 -3864
- package/dist/chunks/host-bridge.d.ts +253 -0
- package/dist/chunks/http.d.ts +233 -58
- package/dist/chunks/http.js +1046 -1028
- package/dist/chunks/transport.js +113 -59
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/host-bridge.d.ts +54 -0
- package/dist/host-bridge.js +322 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +74 -191
- package/dist/index.js +2032 -1186
- package/dist/protocol/dist/board-constants.d.ts +1 -29
- package/dist/protocol/dist/board-constants.js +2 -115
- package/dist/protocol/dist/board-layout.d.ts +59 -0
- package/dist/protocol/dist/board-layout.js +482 -0
- package/dist/protocol/dist/board-model.d.ts +2052 -0
- package/dist/protocol/dist/board-model.js +592 -0
- package/dist/protocol/dist/board-patch.d.ts +86 -0
- package/dist/protocol/dist/board-patch.js +734 -0
- package/dist/protocol/dist/board-schema.d.ts +33 -0
- package/dist/protocol/dist/board-schema.js +59 -0
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-url.d.ts +1 -3
- package/dist/protocol/dist/board-url.js +2 -2
- package/dist/protocol/dist/board.d.ts +37 -232
- package/dist/protocol/dist/board.js +62 -160
- package/dist/protocol/dist/display/actions.d.ts +1 -0
- package/dist/protocol/dist/display/index.d.ts +2 -0
- package/dist/protocol/dist/index.d.ts +11 -12
- package/dist/protocol/dist/model/metrics.d.ts +2 -0
- package/dist/protocol/dist/model/session.d.ts +2 -1
- package/dist/protocol/dist/realtime/types.d.ts +2 -2
- package/dist/protocol/dist/runtime/index.d.ts +1 -0
- package/dist/space-picker.d.ts +1 -0
- package/dist/space-picker.js +1 -0
- package/dist/types.d.ts +2 -5
- package/package.json +9 -1
- package/dist/board/core/connections.d.ts +0 -108
- package/dist/board/core/connections.js +0 -455
- package/dist/board/mutation.d.ts +0 -6
- package/dist/board/mutation.js +0 -14
- package/dist/board/render/connection-layer.d.ts +0 -53
- package/dist/board/render/connection-layer.js +0 -375
- package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
- package/dist/board/render/renderers/geo-card-renderer.js +0 -127
- package/dist/board/semantic-document.d.ts +0 -37
- package/dist/board/semantic-document.js +0 -288
- package/dist/board/semantic-mutation.d.ts +0 -10
- package/dist/board/semantic-mutation.js +0 -209
- package/dist/protocol/dist/board-animation.js +0 -34
- package/dist/protocol/dist/board-authoring.d.ts +0 -1326
- package/dist/protocol/dist/board-authoring.js +0 -302
- package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
- package/dist/protocol/dist/board-capability-registry.js +0 -123
- package/dist/protocol/dist/board-codec.d.ts +0 -3
- package/dist/protocol/dist/board-codec.js +0 -383
- package/dist/protocol/dist/board-composition.d.ts +0 -383
- package/dist/protocol/dist/board-composition.js +0 -310
- package/dist/protocol/dist/board-connection.d.ts +0 -317
- package/dist/protocol/dist/board-connection.js +0 -219
- package/dist/protocol/dist/board-document.d.ts +0 -1566
- package/dist/protocol/dist/board-document.js +0 -456
- package/dist/protocol/dist/board-effect.d.ts +0 -96
- package/dist/protocol/dist/board-effect.js +0 -54
- package/dist/protocol/dist/board-geometry.d.ts +0 -15
- package/dist/protocol/dist/board-geometry.js +0 -137
- package/dist/protocol/dist/board-node.d.ts +0 -8
- package/dist/protocol/dist/board-node.js +0 -252
- /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
package/dist/chunks/transport.js
CHANGED
|
@@ -1,70 +1,20 @@
|
|
|
1
1
|
import { o as resolveApiBaseUrl } from "./environment.js";
|
|
2
|
-
//#region ../protocol/dist/realtime/types.js
|
|
3
|
-
const WS_COMPACT_STREAM_CAPABILITY = "session.compact_stream.v1";
|
|
4
|
-
const WS_ROOM_SUBSCRIPTION_CAPABILITY = "realtime.rooms.v1";
|
|
5
|
-
const WS_BOARD_AWARENESS_CAPABILITY = "board.awareness.v1";
|
|
6
|
-
const WS_REALTIME_ROOM_CAPABILITY = "realtime.room.v1";
|
|
7
|
-
const REALTIME_DOMAINS = [
|
|
8
|
-
"system",
|
|
9
|
-
"session",
|
|
10
|
-
"space",
|
|
11
|
-
"label",
|
|
12
|
-
"room",
|
|
13
|
-
"ui",
|
|
14
|
-
"desktop"
|
|
15
|
-
];
|
|
16
|
-
const isRealtimeDomain = (value) => typeof value === "string" && REALTIME_DOMAINS.some((domain) => domain === value);
|
|
17
|
-
/** Accepted room event names. Shared so a client can reject one before sending. */
|
|
18
|
-
const REALTIME_ROOM_EVENT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/;
|
|
19
|
-
/** Maximum encoded size of a room event payload. */
|
|
20
|
-
const REALTIME_ROOM_MAX_PAYLOAD_BYTES = 16384;
|
|
21
|
-
const getRealtimeSpaceRoom = (spaceId) => `space:${spaceId}`;
|
|
22
|
-
const getRealtimeBoardRoom = (boardId) => `board:${boardId}`;
|
|
23
|
-
const parseRealtimeRoom = (room) => {
|
|
24
|
-
const trimmed = room.trim();
|
|
25
|
-
const separatorIndex = trimmed.indexOf(":");
|
|
26
|
-
if (separatorIndex <= 0) return null;
|
|
27
|
-
const kind = trimmed.slice(0, separatorIndex);
|
|
28
|
-
const id = trimmed.slice(separatorIndex + 1).trim();
|
|
29
|
-
if (!id) return null;
|
|
30
|
-
if (kind !== "space" && kind !== "user" && kind !== "board" && kind !== "room") return null;
|
|
31
|
-
return {
|
|
32
|
-
kind,
|
|
33
|
-
id
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
const normalizeRealtimeRooms = (rooms) => {
|
|
37
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
38
|
-
for (const room of rooms) {
|
|
39
|
-
const parsed = typeof room === "string" ? parseRealtimeRoom(room) : null;
|
|
40
|
-
if (!parsed) continue;
|
|
41
|
-
normalized.add(`${parsed.kind}:${parsed.id}`);
|
|
42
|
-
}
|
|
43
|
-
return [...normalized];
|
|
44
|
-
};
|
|
45
|
-
const getNonEmptyString = (value) => typeof value === "string" && value.trim() ? value : null;
|
|
46
|
-
const getSessionTurnPatchStreamKey = (input, options = {}) => {
|
|
47
|
-
const turnId = getNonEmptyString(input.turnId);
|
|
48
|
-
const messageKey = getNonEmptyString(input.messageId) ?? getNonEmptyString(input.sourceMessageId) ?? getNonEmptyString(input.anchorUserMessageId) ?? (typeof input.messageOrdinal === "number" && Number.isFinite(input.messageOrdinal) ? `ordinal:${input.messageOrdinal}` : null);
|
|
49
|
-
if (turnId && messageKey) return `${turnId}:${messageKey}`;
|
|
50
|
-
const streamKey = messageKey ?? turnId;
|
|
51
|
-
if (streamKey) return streamKey;
|
|
52
|
-
return options.includeSessionFallback ? getNonEmptyString(input.sessionId) : null;
|
|
53
|
-
};
|
|
54
|
-
//#endregion
|
|
55
2
|
//#region ../protocol/dist/identifiers.js
|
|
56
3
|
const UUID_SHAPE_PATTERN = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
57
4
|
const UUID_PATTERN = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}";
|
|
58
5
|
const UUID_OR_SHORT_UUID_PATTERN = `^(?:${UUID_PATTERN}|[0-9a-fA-F]{32})$`;
|
|
59
6
|
const UUID_SHAPE_REGEX = new RegExp(`^${UUID_SHAPE_PATTERN}$`);
|
|
60
7
|
const UUID_REGEX = new RegExp(`^${UUID_PATTERN}$`);
|
|
61
|
-
new RegExp(UUID_OR_SHORT_UUID_PATTERN);
|
|
8
|
+
const UUID_OR_SHORT_UUID_REGEX = new RegExp(UUID_OR_SHORT_UUID_PATTERN);
|
|
62
9
|
function isUuidLike(value) {
|
|
63
10
|
return typeof value === "string" && UUID_SHAPE_REGEX.test(value);
|
|
64
11
|
}
|
|
65
12
|
function isUuid(value) {
|
|
66
13
|
return typeof value === "string" && UUID_REGEX.test(value);
|
|
67
14
|
}
|
|
15
|
+
function isUuidOrShortUuid(value) {
|
|
16
|
+
return typeof value === "string" && UUID_OR_SHORT_UUID_REGEX.test(value);
|
|
17
|
+
}
|
|
68
18
|
//#endregion
|
|
69
19
|
//#region ../protocol/dist/provenance.js
|
|
70
20
|
/** Request provenance via X-Cohub-Source-* headers. Never used for authorization. */
|
|
@@ -125,7 +75,8 @@ const normalizeRequestSource = (input) => {
|
|
|
125
75
|
const spaceId = asUuid(record.spaceId);
|
|
126
76
|
const sessionId = asUuid(record.sessionId);
|
|
127
77
|
const turnId = asUuid(record.turnId);
|
|
128
|
-
const toolCallId =
|
|
78
|
+
const toolCallId = asNonEmpty(record.toolCallId);
|
|
79
|
+
const validToolCallId = toolCallId && toolCallId.length <= 255 ? toolCallId : void 0;
|
|
129
80
|
const clientId = asClientId(record.clientId);
|
|
130
81
|
const sandboxVersion = asSandboxVersion(record.sandboxVersion);
|
|
131
82
|
const via = asVia(record.via);
|
|
@@ -133,7 +84,7 @@ const normalizeRequestSource = (input) => {
|
|
|
133
84
|
...spaceId ? { spaceId } : {},
|
|
134
85
|
...sessionId ? { sessionId } : {},
|
|
135
86
|
...turnId ? { turnId } : {},
|
|
136
|
-
...
|
|
87
|
+
...validToolCallId ? { toolCallId: validToolCallId } : {},
|
|
137
88
|
...clientId ? { clientId } : {},
|
|
138
89
|
...sandboxVersion ? { sandboxVersion } : {},
|
|
139
90
|
...via ? { via } : {}
|
|
@@ -196,10 +147,98 @@ const mergeRequestSourceIntoMeta = (meta, headerSource) => {
|
|
|
196
147
|
};
|
|
197
148
|
const COHUB_SOURCE_HEADER_NAMES = Object.values(COHUB_SOURCE_HEADER);
|
|
198
149
|
//#endregion
|
|
150
|
+
//#region ../protocol/dist/realtime/types.js
|
|
151
|
+
const WS_COMPACT_STREAM_CAPABILITY = "session.compact_stream.v1";
|
|
152
|
+
const WS_ROOM_SUBSCRIPTION_CAPABILITY = "realtime.rooms.v1";
|
|
153
|
+
const WS_BOARD_AWARENESS_CAPABILITY = "board.awareness.v1";
|
|
154
|
+
const WS_REALTIME_ROOM_CAPABILITY = "realtime.room.v1";
|
|
155
|
+
const REALTIME_DOMAINS = [
|
|
156
|
+
"system",
|
|
157
|
+
"session",
|
|
158
|
+
"space",
|
|
159
|
+
"label",
|
|
160
|
+
"room",
|
|
161
|
+
"ui",
|
|
162
|
+
"desktop"
|
|
163
|
+
];
|
|
164
|
+
const isRealtimeDomain = (value) => typeof value === "string" && REALTIME_DOMAINS.some((domain) => domain === value);
|
|
165
|
+
/** Accepted room event names. Shared so a client can reject one before sending. */
|
|
166
|
+
const REALTIME_ROOM_EVENT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/;
|
|
167
|
+
/** Maximum encoded size of a room event payload. */
|
|
168
|
+
const REALTIME_ROOM_MAX_PAYLOAD_BYTES = 16384;
|
|
169
|
+
const getRealtimeSpaceRoom = (spaceId) => `space:${spaceId}`;
|
|
170
|
+
const getRealtimeBoardRoom = (boardId) => `board:${boardId}`;
|
|
171
|
+
const parseRealtimeRoom = (room) => {
|
|
172
|
+
const trimmed = room.trim();
|
|
173
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
174
|
+
if (separatorIndex <= 0) return null;
|
|
175
|
+
const kind = trimmed.slice(0, separatorIndex);
|
|
176
|
+
const id = trimmed.slice(separatorIndex + 1).trim();
|
|
177
|
+
if (!id) return null;
|
|
178
|
+
if (kind !== "space" && kind !== "user" && kind !== "board" && kind !== "room") return null;
|
|
179
|
+
return {
|
|
180
|
+
kind,
|
|
181
|
+
id
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
const normalizeRealtimeRooms = (rooms) => {
|
|
185
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
186
|
+
for (const room of rooms) {
|
|
187
|
+
const parsed = typeof room === "string" ? parseRealtimeRoom(room) : null;
|
|
188
|
+
if (!parsed) continue;
|
|
189
|
+
normalized.add(`${parsed.kind}:${parsed.id}`);
|
|
190
|
+
}
|
|
191
|
+
return [...normalized];
|
|
192
|
+
};
|
|
193
|
+
const getNonEmptyString = (value) => typeof value === "string" && value.trim() ? value : null;
|
|
194
|
+
const getSessionTurnPatchStreamKey = (input, options = {}) => {
|
|
195
|
+
const turnId = getNonEmptyString(input.turnId);
|
|
196
|
+
const messageKey = getNonEmptyString(input.messageId) ?? getNonEmptyString(input.sourceMessageId) ?? getNonEmptyString(input.anchorUserMessageId) ?? (typeof input.messageOrdinal === "number" && Number.isFinite(input.messageOrdinal) ? `ordinal:${input.messageOrdinal}` : null);
|
|
197
|
+
if (turnId && messageKey) return `${turnId}:${messageKey}`;
|
|
198
|
+
const streamKey = messageKey ?? turnId;
|
|
199
|
+
if (streamKey) return streamKey;
|
|
200
|
+
return options.includeSessionFallback ? getNonEmptyString(input.sessionId) : null;
|
|
201
|
+
};
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/revalidation-cache.ts
|
|
204
|
+
const MAX_ENTRY_CHARS = 262144;
|
|
205
|
+
const MAX_TOTAL_CHARS = 8388608;
|
|
206
|
+
const entries = /* @__PURE__ */ new Map();
|
|
207
|
+
let totalChars = 0;
|
|
208
|
+
const runtime = globalThis;
|
|
209
|
+
const revalidationEnabled = runtime.window === void 0 && Boolean(runtime.process?.versions?.node);
|
|
210
|
+
function remove(key) {
|
|
211
|
+
const entry = entries.get(key);
|
|
212
|
+
if (!entry) return;
|
|
213
|
+
entries.delete(key);
|
|
214
|
+
totalChars -= entry.body.length;
|
|
215
|
+
}
|
|
216
|
+
function readRevalidationEntry(key) {
|
|
217
|
+
const entry = entries.get(key);
|
|
218
|
+
if (!entry) return void 0;
|
|
219
|
+
entries.delete(key);
|
|
220
|
+
entries.set(key, entry);
|
|
221
|
+
return entry;
|
|
222
|
+
}
|
|
223
|
+
function writeRevalidationEntry(key, etag, body) {
|
|
224
|
+
remove(key);
|
|
225
|
+
if (!etag || body.length > MAX_ENTRY_CHARS) return;
|
|
226
|
+
entries.set(key, {
|
|
227
|
+
etag,
|
|
228
|
+
body
|
|
229
|
+
});
|
|
230
|
+
totalChars += body.length;
|
|
231
|
+
for (const oldest of entries.keys()) {
|
|
232
|
+
if (totalChars <= MAX_TOTAL_CHARS) break;
|
|
233
|
+
remove(oldest);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
199
237
|
//#region src/transport.ts
|
|
200
238
|
const responseBodyForError = async (response) => {
|
|
201
239
|
return (response.headers.get("content-type") ?? "").includes("application/json") ? await response.json().catch(() => null) : await response.text().catch(() => response.statusText);
|
|
202
240
|
};
|
|
241
|
+
const isFailure = (response) => !response.ok && response.status !== 304;
|
|
203
242
|
const messageFromErrorBody = (body, fallback) => {
|
|
204
243
|
if (typeof body === "string") return body.trim() || fallback;
|
|
205
244
|
if (body && typeof body === "object") {
|
|
@@ -399,7 +438,7 @@ var HttpTransport = class {
|
|
|
399
438
|
throw error;
|
|
400
439
|
}
|
|
401
440
|
if (retryResponse.status !== 401) {
|
|
402
|
-
if (
|
|
441
|
+
if (isFailure(retryResponse)) {
|
|
403
442
|
const body = await responseBodyForError(retryResponse);
|
|
404
443
|
throw new HttpError(messageFromErrorBody(body, retryResponse.statusText), retryResponse.status, body);
|
|
405
444
|
}
|
|
@@ -427,7 +466,7 @@ var HttpTransport = class {
|
|
|
427
466
|
rejectedTokenByError.set(error, rejectedToken);
|
|
428
467
|
throw error;
|
|
429
468
|
}
|
|
430
|
-
if (
|
|
469
|
+
if (isFailure(response)) {
|
|
431
470
|
const body = await responseBodyForError(response);
|
|
432
471
|
throw new HttpError(messageFromErrorBody(body, response.statusText), response.status, body);
|
|
433
472
|
}
|
|
@@ -438,6 +477,21 @@ var HttpTransport = class {
|
|
|
438
477
|
if (response.status === 204) return null;
|
|
439
478
|
return response.json();
|
|
440
479
|
}
|
|
480
|
+
async requestRevalidated(path, init) {
|
|
481
|
+
if (!revalidationEnabled) return this.request(path, init);
|
|
482
|
+
const key = joinApiUrl(this.baseUrl, path);
|
|
483
|
+
const cached = readRevalidationEntry(key);
|
|
484
|
+
const headers = new Headers(init?.headers);
|
|
485
|
+
if (cached) headers.set("If-None-Match", cached.etag);
|
|
486
|
+
const response = await this.send(path, {
|
|
487
|
+
...init,
|
|
488
|
+
headers
|
|
489
|
+
});
|
|
490
|
+
if (response.status === 304 && cached) return JSON.parse(cached.body);
|
|
491
|
+
const body = await response.text();
|
|
492
|
+
writeRevalidationEntry(key, response.headers.get("etag"), body);
|
|
493
|
+
return JSON.parse(body);
|
|
494
|
+
}
|
|
441
495
|
async raw(path, init) {
|
|
442
496
|
const response = await this.send(path, init);
|
|
443
497
|
return {
|
|
@@ -453,4 +507,4 @@ var HttpTransport = class {
|
|
|
453
507
|
}
|
|
454
508
|
};
|
|
455
509
|
//#endregion
|
|
456
|
-
export {
|
|
510
|
+
export { isUuid as A, isRequestSourceUuid as C, readRequestSourceFromEnv as D, parseRequestSourceFromHeaders as E, isUuidOrShortUuid as M, requestSourceToHeaders as O, isRequestSourceEmpty as S, normalizeRequestSource as T, COHUB_SOURCE_HEADER as _, sanitizeAccessToken as a, hasRequestSourceIdentity as b, WS_BOARD_AWARENESS_CAPABILITY as c, WS_ROOM_SUBSCRIPTION_CAPABILITY as d, getRealtimeBoardRoom as f, normalizeRealtimeRooms as g, isRealtimeDomain as h, matchesUnauthorizedErrorToken as i, isUuidLike as j, resolveRequestSourceChannel as k, WS_COMPACT_STREAM_CAPABILITY as l, getSessionTurnPatchStreamKey as m, HttpTransport as n, REALTIME_ROOM_EVENT_NAME_PATTERN as o, getRealtimeSpaceRoom as p, joinApiUrl as r, REALTIME_ROOM_MAX_PAYLOAD_BYTES as s, HttpError as t, WS_REALTIME_ROOM_CAPABILITY as u, COHUB_SOURCE_HEADER_NAMES as v, mergeRequestSourceIntoMeta as w, isRequestSourceClientId as x, REQUEST_SOURCE_VIA_MAX_LENGTH as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Dc as ModelThinkingLevel, J as BillingResponsePayload, ao as RealtimeRoom, eo as ChannelEnvelope, is as BoardAwarenessUpdate, jc as ContentBlock, r as CohubEnvironment } from "./environment.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/websocket.d.ts
|
|
4
4
|
type WebsocketEventPayload = ChannelEnvelope;
|
package/dist/chunks/websocket.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as WS_BOARD_AWARENESS_CAPABILITY, d as WS_ROOM_SUBSCRIPTION_CAPABILITY, g as normalizeRealtimeRooms, h as isRealtimeDomain, l as WS_COMPACT_STREAM_CAPABILITY, m as getSessionTurnPatchStreamKey, p as getRealtimeSpaceRoom, t as HttpError, u as WS_REALTIME_ROOM_CAPABILITY } from "./transport.js";
|
|
2
2
|
import { f as resolveWebsocketUrl } from "./environment.js";
|
|
3
3
|
//#region src/http-error.ts
|
|
4
4
|
/** Shared HTTP error code for every plan entitlement gate (402). */
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { a as HostPlatform, i as HostBridgeMethod, n as HostBridgeCapability, r as HostBridgeEvent, t as HOST_BRIDGE_METHODS } from "./chunks/host-bridge.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/host-bridge.d.ts
|
|
4
|
+
type HostBridgeError = HostBridgeErrorClass;
|
|
5
|
+
type HostBridgeResult<M extends HostBridgeMethod> = z.infer<(typeof HOST_BRIDGE_METHODS)[M]["result"]>;
|
|
6
|
+
type HostBridgeParams<M extends HostBridgeMethod> = z.infer<(typeof HOST_BRIDGE_METHODS)[M]["params"]>;
|
|
7
|
+
declare class HostBridgeErrorClass extends Error {
|
|
8
|
+
readonly code: string;
|
|
9
|
+
constructor(code: string, message: string);
|
|
10
|
+
get isCanceled(): boolean;
|
|
11
|
+
get isUnsupported(): boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Minimal surface a host must provide (Android: web message listener). */
|
|
14
|
+
type HostBridgeChannel = {
|
|
15
|
+
postMessage(message: string): void;
|
|
16
|
+
addEventListener(type: "message", listener: (event: {
|
|
17
|
+
data: unknown;
|
|
18
|
+
}) => void): void;
|
|
19
|
+
removeEventListener(type: "message", listener: (event: {
|
|
20
|
+
data: unknown;
|
|
21
|
+
}) => void): void;
|
|
22
|
+
};
|
|
23
|
+
type HostBridgeStatus = {
|
|
24
|
+
platform: HostPlatform;
|
|
25
|
+
version: number;
|
|
26
|
+
hostId: string;
|
|
27
|
+
capabilities: readonly HostBridgeCapability[];
|
|
28
|
+
};
|
|
29
|
+
type HostBridgeCallOptions = {
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
};
|
|
33
|
+
type HostBridgeClient = {
|
|
34
|
+
readonly status: HostBridgeStatus | null;
|
|
35
|
+
readonly connected: boolean;
|
|
36
|
+
ready(): Promise<HostBridgeStatus | null>;
|
|
37
|
+
supports(capability: HostBridgeCapability): boolean;
|
|
38
|
+
call<M extends HostBridgeMethod>(method: M, params?: HostBridgeParams<M>, options?: HostBridgeCallOptions): Promise<HostBridgeResult<M>>;
|
|
39
|
+
onEvent(listener: (event: HostBridgeEvent) => void): () => void;
|
|
40
|
+
dispose(): void;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Connect to the injected host, if any. With no channel this resolves `null`
|
|
44
|
+
* immediately — the browser path must not pay for a host that is not there.
|
|
45
|
+
*
|
|
46
|
+
* Until the handshake completes every capability is absent, so a
|
|
47
|
+
* half-initialised host is never mistaken for a capable one.
|
|
48
|
+
*/
|
|
49
|
+
declare function createHostBridge(options?: {
|
|
50
|
+
channel?: HostBridgeChannel | null;
|
|
51
|
+
handshakeTimeoutMs?: number;
|
|
52
|
+
}): HostBridgeClient;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { HostBridgeCallOptions, HostBridgeChannel, HostBridgeClient, HostBridgeError, HostBridgeErrorClass, HostBridgeParams, HostBridgeResult, HostBridgeStatus, createHostBridge };
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region ../protocol/dist/host-bridge.js
|
|
3
|
+
const HOST_BRIDGE_GLOBAL = "cohubHost";
|
|
4
|
+
const hostBridgeCapabilitySchema = z.enum([
|
|
5
|
+
"auth.token",
|
|
6
|
+
"auth.signIn",
|
|
7
|
+
"auth.session",
|
|
8
|
+
"notifications",
|
|
9
|
+
"share",
|
|
10
|
+
"filePicker",
|
|
11
|
+
"navigation",
|
|
12
|
+
"cache",
|
|
13
|
+
"runtime",
|
|
14
|
+
"appearance",
|
|
15
|
+
"launch",
|
|
16
|
+
"haptics",
|
|
17
|
+
"files",
|
|
18
|
+
"navigation.back",
|
|
19
|
+
"shortcuts",
|
|
20
|
+
"display"
|
|
21
|
+
]);
|
|
22
|
+
const hostPlatformSchema = z.enum([
|
|
23
|
+
"android",
|
|
24
|
+
"ios",
|
|
25
|
+
"web",
|
|
26
|
+
"unknown"
|
|
27
|
+
]);
|
|
28
|
+
const identifier = z.string().min(1).max(128);
|
|
29
|
+
/** Unknown capabilities from a newer host are dropped, not rejected. */
|
|
30
|
+
const knownCapabilitiesSchema = z.array(z.string().min(1).max(64)).max(64).transform((names) => names.filter((name) => hostBridgeCapabilitySchema.safeParse(name).success));
|
|
31
|
+
/** Pushed by hosts that predate the `host.describe` handshake. */
|
|
32
|
+
const hostBridgeHelloSchema = z.object({
|
|
33
|
+
type: z.literal("hello"),
|
|
34
|
+
version: z.number().int().positive(),
|
|
35
|
+
platform: hostPlatformSchema,
|
|
36
|
+
hostId: identifier,
|
|
37
|
+
capabilities: knownCapabilitiesSchema
|
|
38
|
+
}).strict();
|
|
39
|
+
const hostDescriptionSchema = hostBridgeHelloSchema.omit({ type: true });
|
|
40
|
+
const hostBridgeRequestSchema = z.object({
|
|
41
|
+
type: z.literal("request"),
|
|
42
|
+
id: identifier,
|
|
43
|
+
method: z.string().min(1).max(64),
|
|
44
|
+
params: z.unknown().optional()
|
|
45
|
+
}).strict();
|
|
46
|
+
const hostBridgeResponseSchema = z.object({
|
|
47
|
+
type: z.literal("response"),
|
|
48
|
+
id: identifier,
|
|
49
|
+
result: z.unknown().optional(),
|
|
50
|
+
error: z.object({
|
|
51
|
+
code: z.string().min(1).max(64),
|
|
52
|
+
message: z.string().min(1).max(2e3)
|
|
53
|
+
}).strict().optional()
|
|
54
|
+
}).strict();
|
|
55
|
+
const hostBridgeEventSchema = z.object({
|
|
56
|
+
type: z.literal("event"),
|
|
57
|
+
name: z.string().min(1).max(64),
|
|
58
|
+
payload: z.unknown().optional()
|
|
59
|
+
}).strict();
|
|
60
|
+
const hostBridgeMessageSchema = z.discriminatedUnion("type", [
|
|
61
|
+
hostBridgeHelloSchema,
|
|
62
|
+
hostBridgeRequestSchema,
|
|
63
|
+
hostBridgeResponseSchema,
|
|
64
|
+
hostBridgeEventSchema
|
|
65
|
+
]);
|
|
66
|
+
z.object({ forceRefresh: z.boolean().optional() }).strict();
|
|
67
|
+
z.object({
|
|
68
|
+
authenticated: z.boolean(),
|
|
69
|
+
userUuid: z.string().min(1).max(128).nullable(),
|
|
70
|
+
subject: z.string().min(1).max(255).nullable(),
|
|
71
|
+
email: z.string().max(320).nullable()
|
|
72
|
+
}).strict();
|
|
73
|
+
const DEVICE_RUNTIME_REFUSALS = [
|
|
74
|
+
"space_in_use",
|
|
75
|
+
"folder_in_use",
|
|
76
|
+
"folder_unavailable"
|
|
77
|
+
];
|
|
78
|
+
const devicePath = z.string().min(1).max(4096);
|
|
79
|
+
const deviceLabel = z.string().min(1).max(4096);
|
|
80
|
+
const deviceRuntimeInstanceSchema = z.object({
|
|
81
|
+
spaceId: z.uuid(),
|
|
82
|
+
root: devicePath,
|
|
83
|
+
label: deviceLabel,
|
|
84
|
+
state: z.enum([
|
|
85
|
+
"stopped",
|
|
86
|
+
"connecting",
|
|
87
|
+
"ready",
|
|
88
|
+
"error"
|
|
89
|
+
]),
|
|
90
|
+
error: z.string().min(1).max(64).nullable()
|
|
91
|
+
}).strict();
|
|
92
|
+
const deviceRuntimeSchema = z.object({ instances: z.array(deviceRuntimeInstanceSchema).max(256) }).strict();
|
|
93
|
+
z.object({
|
|
94
|
+
path: devicePath,
|
|
95
|
+
label: deviceLabel,
|
|
96
|
+
parent: devicePath.nullable(),
|
|
97
|
+
spaceId: z.uuid().nullable(),
|
|
98
|
+
folders: z.array(z.object({
|
|
99
|
+
name: z.string().min(1).max(255),
|
|
100
|
+
path: devicePath
|
|
101
|
+
}).strict()).max(1e4),
|
|
102
|
+
volumes: z.array(z.object({
|
|
103
|
+
path: devicePath,
|
|
104
|
+
label: deviceLabel
|
|
105
|
+
}).strict()).max(16)
|
|
106
|
+
}).strict();
|
|
107
|
+
z.object({
|
|
108
|
+
sharedWith: z.uuid().nullable(),
|
|
109
|
+
control: z.boolean(),
|
|
110
|
+
error: z.string().min(1).max(64).nullable()
|
|
111
|
+
}).strict();
|
|
112
|
+
const hostColorSchema = z.string().regex(/^#[0-9a-f]{6}$/i);
|
|
113
|
+
const HOST_HAPTICS = [
|
|
114
|
+
"tick",
|
|
115
|
+
"confirm",
|
|
116
|
+
"reject",
|
|
117
|
+
"longPress"
|
|
118
|
+
];
|
|
119
|
+
/** Hosts parse messages on their UI thread; larger files go by URL. */
|
|
120
|
+
const HOST_FILE_DATA_MAX_LENGTH = 16777216;
|
|
121
|
+
const savedFileSchema = z.object({
|
|
122
|
+
name: z.string().min(1).max(255),
|
|
123
|
+
mimeType: z.string().min(1).max(255).optional()
|
|
124
|
+
});
|
|
125
|
+
z.union([savedFileSchema.extend({ url: z.url({ protocol: /^https$/ }).max(8192) }).strict(), savedFileSchema.extend({ data: z.base64().max(HOST_FILE_DATA_MAX_LENGTH) }).strict()]);
|
|
126
|
+
z.object({
|
|
127
|
+
id: z.string().min(1).max(128),
|
|
128
|
+
label: z.string().min(1).max(64),
|
|
129
|
+
path: z.string().regex(/^\/(?!\/)/, "an in-app path").max(2048)
|
|
130
|
+
}).strict();
|
|
131
|
+
z.undefined(), z.string().min(1).nullable(), z.undefined(), z.number().int().nonnegative(), z.undefined(), z.object({ redirectPath: z.string().max(2048).optional() }).strict(), z.boolean(), z.undefined(), z.boolean(), z.undefined(), z.boolean(), z.object({
|
|
132
|
+
text: z.string().max(1e5),
|
|
133
|
+
title: z.string().max(512).optional()
|
|
134
|
+
}).strict(), z.boolean(), z.object({ path: z.string().min(1).max(2048) }).strict(), z.boolean(), z.object({ scope: z.enum(["all", "user"]) }).strict(), z.boolean(), z.undefined(), z.object({ path: devicePath.optional() }).strict(), z.object({
|
|
135
|
+
spaceId: z.uuid(),
|
|
136
|
+
root: devicePath
|
|
137
|
+
}).strict(), deviceRuntimeSchema.extend({ refused: z.enum(DEVICE_RUNTIME_REFUSALS).nullable() }).strict(), z.object({ spaceId: z.uuid() }).strict(), z.object({ backgroundColor: hostColorSchema }).strict(), z.boolean(), z.undefined(), z.boolean(), z.object({ kind: z.enum(HOST_HAPTICS) }).strict(), z.boolean(), z.boolean(), z.object({ enabled: z.boolean() }).strict(), z.boolean(), z.boolean(), z.undefined(), z.object({ spaceId: z.uuid() }).strict(), z.undefined(), z.undefined(), z.boolean();
|
|
138
|
+
const HOST_BRIDGE_ERROR = {
|
|
139
|
+
unsupported: "unsupported",
|
|
140
|
+
unauthorized: "unauthorized",
|
|
141
|
+
canceled: "canceled",
|
|
142
|
+
failed: "failed"
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/host-bridge.ts
|
|
146
|
+
var HostBridgeErrorClass = class extends Error {
|
|
147
|
+
code;
|
|
148
|
+
constructor(code, message) {
|
|
149
|
+
super(message);
|
|
150
|
+
this.name = "HostBridgeError";
|
|
151
|
+
this.code = code;
|
|
152
|
+
}
|
|
153
|
+
get isCanceled() {
|
|
154
|
+
return this.code === HOST_BRIDGE_ERROR.canceled;
|
|
155
|
+
}
|
|
156
|
+
get isUnsupported() {
|
|
157
|
+
return this.code === HOST_BRIDGE_ERROR.unsupported;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const readChannel = () => {
|
|
161
|
+
const candidate = globalThis[HOST_BRIDGE_GLOBAL];
|
|
162
|
+
if (!candidate || typeof candidate !== "object") return null;
|
|
163
|
+
const channel = candidate;
|
|
164
|
+
return typeof channel.postMessage === "function" ? channel : null;
|
|
165
|
+
};
|
|
166
|
+
const parseIncoming = (data) => {
|
|
167
|
+
if (typeof data === "string") try {
|
|
168
|
+
return hostBridgeMessageSchema.safeParse(JSON.parse(data));
|
|
169
|
+
} catch {
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
error: null
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return hostBridgeMessageSchema.safeParse(data);
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Connect to the injected host, if any. With no channel this resolves `null`
|
|
179
|
+
* immediately — the browser path must not pay for a host that is not there.
|
|
180
|
+
*
|
|
181
|
+
* Until the handshake completes every capability is absent, so a
|
|
182
|
+
* half-initialised host is never mistaken for a capable one.
|
|
183
|
+
*/
|
|
184
|
+
function createHostBridge(options = {}) {
|
|
185
|
+
const channel = options.channel === void 0 ? readChannel() : options.channel;
|
|
186
|
+
const handshakeTimeoutMs = options.handshakeTimeoutMs ?? 2e3;
|
|
187
|
+
const pending = /* @__PURE__ */ new Map();
|
|
188
|
+
const eventListeners = /* @__PURE__ */ new Set();
|
|
189
|
+
let status = null;
|
|
190
|
+
let disposed = false;
|
|
191
|
+
let sequence = 0;
|
|
192
|
+
let handshake = null;
|
|
193
|
+
const nextRequestId = () => {
|
|
194
|
+
sequence += 1;
|
|
195
|
+
const random = globalThis.crypto?.randomUUID?.().replaceAll("-", "").slice(0, 16);
|
|
196
|
+
return `h${sequence}-${random ?? sequence.toString(36)}`;
|
|
197
|
+
};
|
|
198
|
+
const settle = (id, apply) => {
|
|
199
|
+
const call = pending.get(id);
|
|
200
|
+
if (!call) return;
|
|
201
|
+
pending.delete(id);
|
|
202
|
+
clearTimeout(call.timer);
|
|
203
|
+
if (call.signal && call.onAbort) call.signal.removeEventListener("abort", call.onAbort);
|
|
204
|
+
apply(call);
|
|
205
|
+
};
|
|
206
|
+
const adoptHello = (hello) => {
|
|
207
|
+
status = {
|
|
208
|
+
platform: hello.platform,
|
|
209
|
+
version: hello.version,
|
|
210
|
+
hostId: hello.hostId,
|
|
211
|
+
capabilities: hello.capabilities
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
const onMessage = (event) => {
|
|
215
|
+
const parsed = parseIncoming(event.data);
|
|
216
|
+
if (!parsed.success) return;
|
|
217
|
+
const message = parsed.data;
|
|
218
|
+
if (message.type === "hello") {
|
|
219
|
+
adoptHello(message);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (message.type === "response") {
|
|
223
|
+
const response = message;
|
|
224
|
+
settle(response.id, (call) => {
|
|
225
|
+
if (response.error) {
|
|
226
|
+
call.reject(new HostBridgeErrorClass(response.error.code, response.error.message));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
call.resolve(response.result);
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (message.type === "event") for (const listener of eventListeners) listener(message);
|
|
234
|
+
};
|
|
235
|
+
if (channel && !disposed) channel.addEventListener("message", onMessage);
|
|
236
|
+
const rawCall = (method, params, callOptions) => {
|
|
237
|
+
if (!channel || disposed) return Promise.reject(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.unsupported, `Host bridge method is unavailable: ${method}`));
|
|
238
|
+
const id = nextRequestId();
|
|
239
|
+
const timeoutMs = callOptions?.timeoutMs ?? 3e4;
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
const signal = callOptions?.signal;
|
|
242
|
+
if (signal?.aborted) {
|
|
243
|
+
reject(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.canceled, "canceled before send"));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const onAbort = () => settle(id, (call) => call.reject(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.canceled, "canceled")));
|
|
247
|
+
const timer = setTimeout(() => {
|
|
248
|
+
settle(id, (call) => call.reject(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.failed, `Host bridge timed out: ${method}`)));
|
|
249
|
+
}, timeoutMs);
|
|
250
|
+
pending.set(id, {
|
|
251
|
+
resolve,
|
|
252
|
+
reject,
|
|
253
|
+
timer,
|
|
254
|
+
signal,
|
|
255
|
+
onAbort
|
|
256
|
+
});
|
|
257
|
+
if (signal) signal.addEventListener("abort", onAbort, { once: true });
|
|
258
|
+
const request = {
|
|
259
|
+
type: "request",
|
|
260
|
+
id,
|
|
261
|
+
method,
|
|
262
|
+
...params === void 0 ? {} : { params }
|
|
263
|
+
};
|
|
264
|
+
try {
|
|
265
|
+
channel.postMessage(JSON.stringify(request));
|
|
266
|
+
} catch (error) {
|
|
267
|
+
settle(id, (call) => call.reject(error));
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
const describe = async () => {
|
|
272
|
+
if (!channel || disposed) return null;
|
|
273
|
+
try {
|
|
274
|
+
const described = await rawCall("host.describe", void 0, { timeoutMs: handshakeTimeoutMs });
|
|
275
|
+
const parsed = hostDescriptionSchema.safeParse(described);
|
|
276
|
+
if (!parsed.success) return null;
|
|
277
|
+
adoptHello(parsed.data);
|
|
278
|
+
return status;
|
|
279
|
+
} catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const failAll = (error) => {
|
|
284
|
+
for (const call of pending.values()) {
|
|
285
|
+
clearTimeout(call.timer);
|
|
286
|
+
if (call.signal && call.onAbort) call.signal.removeEventListener("abort", call.onAbort);
|
|
287
|
+
call.reject(error);
|
|
288
|
+
}
|
|
289
|
+
pending.clear();
|
|
290
|
+
};
|
|
291
|
+
return {
|
|
292
|
+
get status() {
|
|
293
|
+
return status;
|
|
294
|
+
},
|
|
295
|
+
get connected() {
|
|
296
|
+
return Boolean(channel) && status !== null && !disposed;
|
|
297
|
+
},
|
|
298
|
+
ready() {
|
|
299
|
+
handshake ??= describe();
|
|
300
|
+
return handshake;
|
|
301
|
+
},
|
|
302
|
+
supports(capability) {
|
|
303
|
+
return status?.capabilities.includes(capability) ?? false;
|
|
304
|
+
},
|
|
305
|
+
call(method, params, callOptions) {
|
|
306
|
+
if (!status) return Promise.reject(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.unsupported, `Host bridge is not connected; awaiting handshake before ${method}`));
|
|
307
|
+
return rawCall(method, params, callOptions);
|
|
308
|
+
},
|
|
309
|
+
onEvent(listener) {
|
|
310
|
+
eventListeners.add(listener);
|
|
311
|
+
return () => eventListeners.delete(listener);
|
|
312
|
+
},
|
|
313
|
+
dispose() {
|
|
314
|
+
disposed = true;
|
|
315
|
+
failAll(new HostBridgeErrorClass(HOST_BRIDGE_ERROR.failed, "Host bridge disposed"));
|
|
316
|
+
eventListeners.clear();
|
|
317
|
+
if (channel) channel.removeEventListener("message", onMessage);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
322
|
+
export { HostBridgeErrorClass, createHostBridge };
|