@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.
Files changed (159) hide show
  1. package/dist/board/animation.d.ts +57 -181
  2. package/dist/board/animation.js +374 -220
  3. package/dist/board/core/arrow-geometry.d.ts +33 -18
  4. package/dist/board/core/arrow-geometry.js +283 -52
  5. package/dist/board/core/draw-geometry.d.ts +22 -36
  6. package/dist/board/core/draw-geometry.js +148 -85
  7. package/dist/board/core/export-assets.d.ts +3 -6
  8. package/dist/board/core/export-assets.js +0 -2
  9. package/dist/board/core/export-plan.d.ts +12 -63
  10. package/dist/board/core/export-plan.js +55 -94
  11. package/dist/board/core/palette.d.ts +4 -21
  12. package/dist/board/core/palette.js +8 -13
  13. package/dist/board/core/scene.d.ts +33 -0
  14. package/dist/board/core/scene.js +302 -0
  15. package/dist/board/core/shape-definition.d.ts +12 -24
  16. package/dist/board/core/shape-definition.js +0 -19
  17. package/dist/board/core/shape-types.d.ts +6 -120
  18. package/dist/board/core/shape-types.js +5 -5
  19. package/dist/board/core/text-metrics.d.ts +8 -11
  20. package/dist/board/core/text-metrics.js +5 -34
  21. package/dist/board/core/tool-styles.d.ts +6 -16
  22. package/dist/board/core/tool-styles.js +17 -25
  23. package/dist/board/export/index.d.ts +10 -17
  24. package/dist/board/export/index.js +22 -21
  25. package/dist/board/export/scene.d.ts +10 -16
  26. package/dist/board/export/scene.js +17 -30
  27. package/dist/board/geometry.d.ts +27 -56
  28. package/dist/board/geometry.js +7 -67
  29. package/dist/board/headless/index.d.ts +6 -27
  30. package/dist/board/headless/index.js +4 -19
  31. package/dist/board/headless/sketch.d.ts +19 -0
  32. package/dist/board/headless/sketch.js +146 -0
  33. package/dist/board/image-key.d.ts +2 -12
  34. package/dist/board/image-key.js +3 -13
  35. package/dist/board/index.d.ts +18 -20
  36. package/dist/board/index.js +17 -17
  37. package/dist/board/media-playback.d.ts +3 -6
  38. package/dist/board/media-playback.js +6 -6
  39. package/dist/board/presets.d.ts +150 -0
  40. package/dist/board/presets.js +226 -0
  41. package/dist/board/render/board-background.d.ts +5 -3
  42. package/dist/board/render/board-background.js +8 -7
  43. package/dist/board/render/clip.d.ts +14 -0
  44. package/dist/board/render/clip.js +47 -0
  45. package/dist/board/render/index.d.ts +8 -5
  46. package/dist/board/render/index.js +7 -4
  47. package/dist/board/render/media-interaction.d.ts +4 -5
  48. package/dist/board/render/media-interaction.js +1 -2
  49. package/dist/board/render/palette.d.ts +6 -2
  50. package/dist/board/render/palette.js +7 -1
  51. package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
  52. package/dist/board/render/renderers/audio-card-renderer.js +3 -3
  53. package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
  54. package/dist/board/render/renderers/base-card-renderer.js +1 -24
  55. package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
  56. package/dist/board/render/renderers/board-renderer-registry.js +6 -7
  57. package/dist/board/render/renderers/draw-card-renderer.js +89 -56
  58. package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
  59. package/dist/board/render/renderers/effect-card-renderer.js +197 -0
  60. package/dist/board/render/renderers/far-plate.d.ts +4 -21
  61. package/dist/board/render/renderers/far-plate.js +15 -34
  62. package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
  63. package/dist/board/render/renderers/file-card-renderer.js +8 -58
  64. package/dist/board/render/renderers/frame-card-renderer.js +28 -28
  65. package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
  66. package/dist/board/render/renderers/image-card-renderer.js +0 -9
  67. package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
  68. package/dist/board/render/renderers/shape-card-renderer.js +240 -0
  69. package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
  70. package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
  71. package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
  72. package/dist/board/render/renderers/task-card-renderer.js +14 -16
  73. package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
  74. package/dist/board/render/renderers/text-card-renderer.js +56 -46
  75. package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
  76. package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
  77. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  78. package/dist/board/render/sketch-host.d.ts +14 -0
  79. package/dist/board/render/sketch-host.js +216 -0
  80. package/dist/board/render/stroke.d.ts +12 -0
  81. package/dist/board/render/stroke.js +90 -0
  82. package/dist/board/render/text-measurement.d.ts +6 -26
  83. package/dist/board/render/text-measurement.js +11 -30
  84. package/dist/board/replay.d.ts +6 -31
  85. package/dist/board/replay.js +45 -203
  86. package/dist/board/runtime/board-player.d.ts +24 -0
  87. package/dist/board/runtime/board-player.js +161 -0
  88. package/dist/board/task.d.ts +2 -10
  89. package/dist/board/task.js +1 -10
  90. package/dist/chunks/environment.d.ts +2291 -3864
  91. package/dist/chunks/host-bridge.d.ts +253 -0
  92. package/dist/chunks/http.d.ts +233 -58
  93. package/dist/chunks/http.js +1046 -1028
  94. package/dist/chunks/transport.js +113 -59
  95. package/dist/chunks/websocket.d.ts +1 -1
  96. package/dist/chunks/websocket.js +1 -1
  97. package/dist/host-bridge.d.ts +54 -0
  98. package/dist/host-bridge.js +322 -0
  99. package/dist/http.d.ts +3 -3
  100. package/dist/index.d.ts +74 -191
  101. package/dist/index.js +2032 -1186
  102. package/dist/protocol/dist/board-constants.d.ts +1 -29
  103. package/dist/protocol/dist/board-constants.js +2 -115
  104. package/dist/protocol/dist/board-layout.d.ts +59 -0
  105. package/dist/protocol/dist/board-layout.js +482 -0
  106. package/dist/protocol/dist/board-model.d.ts +2052 -0
  107. package/dist/protocol/dist/board-model.js +592 -0
  108. package/dist/protocol/dist/board-patch.d.ts +86 -0
  109. package/dist/protocol/dist/board-patch.js +734 -0
  110. package/dist/protocol/dist/board-schema.d.ts +33 -0
  111. package/dist/protocol/dist/board-schema.js +59 -0
  112. package/dist/protocol/dist/board-upgrade.d.ts +1 -0
  113. package/dist/protocol/dist/board-url.d.ts +1 -3
  114. package/dist/protocol/dist/board-url.js +2 -2
  115. package/dist/protocol/dist/board.d.ts +37 -232
  116. package/dist/protocol/dist/board.js +62 -160
  117. package/dist/protocol/dist/display/actions.d.ts +1 -0
  118. package/dist/protocol/dist/display/index.d.ts +2 -0
  119. package/dist/protocol/dist/index.d.ts +11 -12
  120. package/dist/protocol/dist/model/metrics.d.ts +2 -0
  121. package/dist/protocol/dist/model/session.d.ts +2 -1
  122. package/dist/protocol/dist/realtime/types.d.ts +2 -2
  123. package/dist/protocol/dist/runtime/index.d.ts +1 -0
  124. package/dist/space-picker.d.ts +1 -0
  125. package/dist/space-picker.js +1 -0
  126. package/dist/types.d.ts +2 -5
  127. package/package.json +9 -1
  128. package/dist/board/core/connections.d.ts +0 -108
  129. package/dist/board/core/connections.js +0 -455
  130. package/dist/board/mutation.d.ts +0 -6
  131. package/dist/board/mutation.js +0 -14
  132. package/dist/board/render/connection-layer.d.ts +0 -53
  133. package/dist/board/render/connection-layer.js +0 -375
  134. package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
  135. package/dist/board/render/renderers/geo-card-renderer.js +0 -127
  136. package/dist/board/semantic-document.d.ts +0 -37
  137. package/dist/board/semantic-document.js +0 -288
  138. package/dist/board/semantic-mutation.d.ts +0 -10
  139. package/dist/board/semantic-mutation.js +0 -209
  140. package/dist/protocol/dist/board-animation.js +0 -34
  141. package/dist/protocol/dist/board-authoring.d.ts +0 -1326
  142. package/dist/protocol/dist/board-authoring.js +0 -302
  143. package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
  144. package/dist/protocol/dist/board-capability-registry.js +0 -123
  145. package/dist/protocol/dist/board-codec.d.ts +0 -3
  146. package/dist/protocol/dist/board-codec.js +0 -383
  147. package/dist/protocol/dist/board-composition.d.ts +0 -383
  148. package/dist/protocol/dist/board-composition.js +0 -310
  149. package/dist/protocol/dist/board-connection.d.ts +0 -317
  150. package/dist/protocol/dist/board-connection.js +0 -219
  151. package/dist/protocol/dist/board-document.d.ts +0 -1566
  152. package/dist/protocol/dist/board-document.js +0 -456
  153. package/dist/protocol/dist/board-effect.d.ts +0 -96
  154. package/dist/protocol/dist/board-effect.js +0 -54
  155. package/dist/protocol/dist/board-geometry.d.ts +0 -15
  156. package/dist/protocol/dist/board-geometry.js +0 -137
  157. package/dist/protocol/dist/board-node.d.ts +0 -8
  158. package/dist/protocol/dist/board-node.js +0 -252
  159. /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
@@ -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 = asUuid(record.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
- ...toolCallId ? { toolCallId } : {},
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 (!retryResponse.ok) {
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 (!response.ok) {
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 { normalizeRealtimeRooms as A, WS_COMPACT_STREAM_CAPABILITY as C, getRealtimeSpaceRoom as D, getRealtimeBoardRoom as E, getSessionTurnPatchStreamKey as O, WS_BOARD_AWARENESS_CAPABILITY as S, WS_ROOM_SUBSCRIPTION_CAPABILITY as T, requestSourceToHeaders as _, sanitizeAccessToken as a, REALTIME_ROOM_EVENT_NAME_PATTERN as b, REQUEST_SOURCE_VIA_MAX_LENGTH as c, isRequestSourceEmpty as d, isRequestSourceUuid as f, readRequestSourceFromEnv as g, parseRequestSourceFromHeaders as h, matchesUnauthorizedErrorToken as i, isRealtimeDomain as k, hasRequestSourceIdentity as l, normalizeRequestSource as m, HttpTransport as n, COHUB_SOURCE_HEADER as o, mergeRequestSourceIntoMeta as p, joinApiUrl as r, COHUB_SOURCE_HEADER_NAMES as s, HttpError as t, isRequestSourceClientId as u, resolveRequestSourceChannel as v, WS_REALTIME_ROOM_CAPABILITY as w, REALTIME_ROOM_MAX_PAYLOAD_BYTES as x, isUuid as y };
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 { Bo as BoardAwarenessUpdate, J as BillingResponsePayload, Wa as ChannelEnvelope, Ya as RealtimeRoom, dc as ModelThinkingLevel, hc as ContentBlock, r as CohubEnvironment } from "./environment.js";
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;
@@ -1,4 +1,4 @@
1
- import { A as normalizeRealtimeRooms, C as WS_COMPACT_STREAM_CAPABILITY, D as getRealtimeSpaceRoom, O as getSessionTurnPatchStreamKey, S as WS_BOARD_AWARENESS_CAPABILITY, T as WS_ROOM_SUBSCRIPTION_CAPABILITY, k as isRealtimeDomain, t as HttpError, w as WS_REALTIME_ROOM_CAPABILITY } from "./transport.js";
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 };