@neta-art/cohub 12.5.0 → 13.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 (76) hide show
  1. package/README.md +35 -13
  2. package/dist/board/editor/editor.d.ts +7 -5
  3. package/dist/board/editor/index.js +1 -1
  4. package/dist/board/editor/interaction.js +1 -1
  5. package/dist/board/export/index.d.ts +1 -6
  6. package/dist/board/export/index.js +16 -10
  7. package/dist/board/export/node/index.d.ts +2 -7
  8. package/dist/board/export/node/index.js +2 -5
  9. package/dist/board/export/scene.d.ts +3 -7
  10. package/dist/board/export/scene.js +22 -26
  11. package/dist/board/model/asset-key.d.ts +5 -2
  12. package/dist/board/model/asset-key.js +13 -5
  13. package/dist/board/model/index.d.ts +5 -4
  14. package/dist/board/model/index.js +4 -3
  15. package/dist/board/model/media-playback.d.ts +4 -6
  16. package/dist/board/model/media-playback.js +4 -4
  17. package/dist/board/model/read.d.ts +7 -0
  18. package/dist/board/model/read.js +18 -0
  19. package/dist/board/model/themes.d.ts +50 -0
  20. package/dist/board/model/themes.js +44 -6
  21. package/dist/board/render/backdrop-layer.d.ts +21 -0
  22. package/dist/board/render/backdrop-layer.js +227 -0
  23. package/dist/board/render/backdrop.d.ts +45 -0
  24. package/dist/board/render/backdrop.js +62 -0
  25. package/dist/board/render/index.d.ts +3 -2
  26. package/dist/board/render/index.js +7 -6
  27. package/dist/board/render/renderers/arrow-card-renderer.js +1 -1
  28. package/dist/board/render/renderers/audio-card-renderer.js +2 -2
  29. package/dist/board/render/renderers/file-card-renderer.js +1 -1
  30. package/dist/board/render/renderers/frame-card-renderer.js +1 -1
  31. package/dist/board/render/renderers/model-card-renderer.js +1 -1
  32. package/dist/board/render/renderers/shape-card-renderer.js +1 -1
  33. package/dist/board/render/renderers/task-card-renderer.js +2 -2
  34. package/dist/board/render/renderers/text-card-renderer.js +2 -2
  35. package/dist/board/render/renderers/video-card-renderer.js +1 -1
  36. package/dist/board/render/theme.d.ts +2 -0
  37. package/dist/board/render/theme.js +4 -2
  38. package/dist/board/replica/index.d.ts +3 -2
  39. package/dist/board/replica/index.js +34 -24
  40. package/dist/board/replica/indexeddb.js +14 -85
  41. package/dist/board/stage/assets.d.ts +4 -3
  42. package/dist/board/stage/assets.js +186 -83
  43. package/dist/board/stage/backdrop.d.ts +25 -0
  44. package/dist/board/stage/backdrop.js +104 -0
  45. package/dist/board/stage/index.d.ts +2 -1
  46. package/dist/board/stage/scene.js +12 -5
  47. package/dist/board/stage/stage.d.ts +7 -2
  48. package/dist/board/stage/stage.js +44 -24
  49. package/dist/board/stage/texture-pool.d.ts +1 -1
  50. package/dist/board/stage/texture-pool.js +4 -5
  51. package/dist/chunks/app-realtime.js +1 -1
  52. package/dist/chunks/environment.d.ts +34 -21
  53. package/dist/chunks/environment.js +4 -3
  54. package/dist/chunks/http-error.js +28 -5
  55. package/dist/chunks/http.d.ts +87 -18
  56. package/dist/chunks/http.js +1855 -59
  57. package/dist/chunks/websocket.d.ts +9 -1
  58. package/dist/http.d.ts +2 -2
  59. package/dist/http.js +1 -1
  60. package/dist/index.d.ts +9 -3
  61. package/dist/index.js +6 -1080
  62. package/dist/indexeddb.js +79 -0
  63. package/dist/media.d.ts +18 -32
  64. package/dist/media.js +81 -67
  65. package/dist/protocol/dist/board-model.d.ts +33 -26
  66. package/dist/protocol/dist/board-model.js +21 -18
  67. package/dist/protocol/dist/board.d.ts +21 -1
  68. package/dist/protocol/dist/board.js +5 -1
  69. package/dist/protocol/dist/core/media.d.ts +32 -9
  70. package/dist/protocol/dist/core/media.js +203 -48
  71. package/dist/protocol/dist/index.d.ts +3 -3
  72. package/dist/voice-input.js +1 -1
  73. package/dist/websocket.js +37 -5
  74. package/package.json +1 -1
  75. package/dist/board/render/board-background.d.ts +0 -18
  76. package/dist/board/render/board-background.js +0 -104
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { c as AppRtcError, i as WorkRoom, n as AppRoom, o as AppRoomRtc, r as WorkRealtimeApi, s as AppRtcChannel, t as AppRealtimeApi } from "./chunks/app-realtime.js";
2
- import { $ as isTerminalDesktopCommandStatus, A as BoardClient, B as CronJobsApi, C as SessionPatchReducer, Ct as isBuiltinItemType, D as connectDisplay, Dt as boardDefaultSize, E as DisplayConnection, F as PublicAssetsApi, G as DESKTOP_COMMAND_OPEN_TIMEOUT_MS, H as DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS, I as SkillsApi, J as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, K as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, L as PromptsApi, M as SessionAccessApi, N as ReferencesApi, P as SearchApi, Q as isDesktopCallMethod, R as ModelsApi, S as parseAssistantMessageCommit, St as boardItemSchema, T as SpaceDisplaysApi, Tt as normalizeBoardRemoteUrl, U as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, V as ChannelsApi, W as DESKTOP_COMMAND_MAX_TIMEOUT_MS, X as DESKTOP_COMMAND_VERSION, Y as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, Z as defaultDesktopCommandTimeoutMs, _ as PublicInviteApi, _t as BoardColorSchema, a as WorkCommerceApi, b as SessionGenerationStreamClient, bt as BoardTrackSchema, c as AppsApi, ct as normalizeDisplayPoint, d as UserApi, dt as BOARD_MIME_TYPE, et as parseDesktopCommand, f as TasksApi, ft as BOARD_CAMERA_TARGET, g as SpacesApi, gt as BoardAnimationSchema, h as SpaceWebhooksApi, ht as BoardAnimationHeaderSchema, i as AppCommerceApi, it as DISPLAY_ELEMENT_REF_PATTERN, j as ensureRealtimeConnected, l as ReferralsApi, lt as splitDisplayText, m as SpacePublicFilesApi, mt as BOARD_ITEM_TYPES, n as createHttpClient, nt as resolveOpenSurface, o as DesktopCommandsApi, ot as DISPLAY_INPUT_MAX_TEXT, p as SpaceClient, pt as BOARD_COLOR_TOKENS, q as DESKTOP_COMMAND_PENDING_TTL_SECONDS, r as RoomsApi, rt as NAVIGATION_ERROR_MESSAGE_MAX_LENGTH, s as UiCommandsApi, st as DISPLAY_SYSTEM_ACTIONS, t as CohubHttpClient, tt as parseDesktopCommandId, u as UsersApi, ut as BOARD_EXTENSION, v as buildSpaceInvitePath, vt as BoardPatchSchema, w as createSessionPatchReducer, wt as parseBoardItem, x as createSessionGenerationStreamClient, xt as BoardVec2Schema, y as buildSpacePath, yt as BoardSettingsSchema, z as GenerationsApi } from "./chunks/http.js";
2
+ import { $ as defaultDesktopCommandTimeoutMs, B as ModelsApi, C as createSessionGenerationStreamClient, D as SpaceDisplaysApi, E as createSessionPatchReducer, F as ReferencesApi, G as DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, H as CronJobsApi, I as SearchApi, J as DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, K as DESKTOP_COMMAND_MAX_TIMEOUT_MS, L as PublicAssetsApi, M as BoardClient, N as ensureRealtimeConnected, O as DisplayConnection, P as SessionAccessApi, Q as DESKTOP_COMMAND_VERSION, R as SkillsApi, S as SessionGenerationStreamClient, T as SessionPatchReducer, U as ChannelsApi, V as GenerationsApi, W as DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS, X as DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, Y as DESKTOP_COMMAND_PENDING_TTL_SECONDS, Z as DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, _ as createSpaceFileServices, _t as BoardAnimationSchema, a as WorkCommerceApi, at as NAVIGATION_ERROR_MESSAGE_MAX_LENGTH, b as buildSpaceInvitePath, bt as parseBoardItem, c as AppsApi, ct as DISPLAY_INPUT_MAX_TEXT, d as UserApi, dt as splitDisplayText, et as isDesktopCallMethod, f as TasksApi, ft as parseBoardDocument, g as SpacesApi, gt as BOARD_ITEM_TYPES, h as SpaceWebhooksApi, ht as BOARD_COLOR_TOKENS, i as AppCommerceApi, it as resolveOpenSurface, k as connectDisplay, l as ReferralsApi, lt as DISPLAY_SYSTEM_ACTIONS, m as SpacePublicFilesApi, mt as BOARD_MIME_TYPE, n as createHttpClient, nt as parseDesktopCommand, o as DesktopCommandsApi, ot as DISPLAY_ELEMENT_REF_PATTERN, p as SpaceClient, pt as BOARD_EXTENSION, q as DESKTOP_COMMAND_OPEN_TIMEOUT_MS, r as RoomsApi, rt as parseDesktopCommandId, s as UiCommandsApi, t as CohubHttpClient, tt as isTerminalDesktopCommandStatus, u as UsersApi, ut as normalizeDisplayPoint, v as clearFileUrlCache, vt as BoardPatchSchema, w as parseAssistantMessageCommit, x as buildSpacePath, xt as normalizeBoardRemoteUrl, y as PublicInviteApi, yt as BoardTrackSchema, z as PromptsApi } from "./chunks/http.js";
3
3
  import { A as isRequestSourceClientId, B as isUuidLike, D as COHUB_SOURCE_HEADER_NAMES, E as COHUB_SOURCE_HEADER, F as parseRequestSourceFromHeaders, I as readRequestSourceFromEnv, L as requestSourceToHeaders, M as isRequestSourceUuid, N as mergeRequestSourceIntoMeta, O as REQUEST_SOURCE_VIA_MAX_LENGTH, P as normalizeRequestSource, R as resolveRequestSourceChannel, V as isUuidOrShortUuid, a as isBillingAccessBlockedError, c as HttpError, d as matchesUnauthorizedErrorToken, f as sanitizeAccessToken, i as isBillingAccessBlockedCode, j as isRequestSourceEmpty, k as hasRequestSourceIdentity, l as HttpTransport, n as FEATURE_NOT_ENTITLED_ERROR_CODE, o as isFeatureNotEntitledError, r as extractBillingPayload, s as isHttpErrorCode, t as BILLING_ACCESS_BLOCKED_ERROR_CODE, u as joinApiUrl, z as isUuid } from "./chunks/http-error.js";
4
- import { a as normalizeWebsocketUrl, c as resolveExecutionAppId, d as resolveWebBaseUrl, f as resolveWebsocketUrl, g as scopeListHasPermission, h as isUserLevelPermission, i as normalizeVoiceInputWebsocketUrl, l as resolveExecutionToken, m as USER_LEVEL_PERMISSIONS, n as getCohubContext, o as resolveApiBaseUrl, p as PERMISSIONS, r as normalizeBaseUrl, s as resolveCohubEnvironment, t as COHUB_ENVIRONMENTS, u as resolveVoiceInputWebsocketUrl } from "./chunks/environment.js";
4
+ import { _ as scopeListHasPermission, a as normalizeVoiceInputWebsocketUrl, c as resolveCohubEnvironment, d as resolveVoiceInputWebsocketUrl, f as resolveWebBaseUrl, g as isUserLevelPermission, h as USER_LEVEL_PERMISSIONS, i as normalizeBaseUrl, l as resolveExecutionAppId, m as PERMISSIONS, o as normalizeWebsocketUrl, p as resolveWebsocketUrl, r as getCohubContext, s as resolveApiBaseUrl, t as COHUB_ENVIRONMENTS, u as resolveExecutionToken } from "./chunks/environment.js";
5
5
  import { WebsocketClient, createWebsocketClient } from "./websocket.js";
6
6
  import { VoiceApi, VoiceInputClient, createVoiceInputClient } from "./voice-input.js";
7
7
  import { filterSpacePickerItems, normalizeSpacePickerQuery, orderSpacePickerItems, selectSpacePickerItems } from "./space-picker.js";
@@ -182,21 +182,6 @@ function batchUploadEntries(entries, sizeOf, options = {}) {
182
182
  return batches;
183
183
  }
184
184
  //#endregion
185
- //#region ../protocol/dist/board-json.js
186
- /** Structural equality for JSON-shaped semantic values. */
187
- function boardJsonEquals(a, b) {
188
- if (a === b) return true;
189
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return Number.isNaN(a) && Number.isNaN(b);
190
- const aArray = Array.isArray(a);
191
- const bArray = Array.isArray(b);
192
- if (aArray !== bArray) return false;
193
- if (aArray && bArray) return a.length === b.length && a.every((value, index) => boardJsonEquals(value, b[index]));
194
- const aRecord = a;
195
- const bRecord = b;
196
- const keys = Object.keys(aRecord);
197
- return keys.length === Object.keys(bRecord).length && keys.every((key) => Object.hasOwn(bRecord, key) && boardJsonEquals(aRecord[key], bRecord[key]));
198
- }
199
- //#endregion
200
185
  //#region ../protocol/dist/generation/blocks.js
201
186
  const EXCERPT_LIMIT = 240;
202
187
  function asRecord(value) {
@@ -319,1067 +304,6 @@ function generationCovers(blocks) {
319
304
  return covers;
320
305
  }
321
306
  //#endregion
322
- //#region ../protocol/dist/board-layout.js
323
- const IDENTITY_MATRIX = [
324
- 1,
325
- 0,
326
- 0,
327
- 1,
328
- 0,
329
- 0
330
- ];
331
- function multiplyMatrix(parent, child) {
332
- const [a1, b1, c1, d1, tx1, ty1] = parent;
333
- const [a2, b2, c2, d2, tx2, ty2] = child;
334
- return [
335
- a1 * a2 + c1 * b2,
336
- b1 * a2 + d1 * b2,
337
- a1 * c2 + c1 * d2,
338
- b1 * c2 + d1 * d2,
339
- a1 * tx2 + c1 * ty2 + tx1,
340
- b1 * tx2 + d1 * ty2 + ty1
341
- ];
342
- }
343
- function invertMatrix(matrix) {
344
- const [a, b, c, d, tx, ty] = matrix;
345
- const det = a * d - b * c || 1e-12;
346
- return [
347
- d / det,
348
- -b / det,
349
- -c / det,
350
- a / det,
351
- (c * ty - d * tx) / det,
352
- (b * tx - a * ty) / det
353
- ];
354
- }
355
- function applyMatrix(matrix, point) {
356
- const [a, b, c, d, tx, ty] = matrix;
357
- return {
358
- x: a * point.x + c * point.y + tx,
359
- y: b * point.x + d * point.y + ty
360
- };
361
- }
362
- function scaleVector(scale) {
363
- return typeof scale === "number" ? {
364
- x: scale,
365
- y: scale
366
- } : scale;
367
- }
368
- function isWideCharacter(code) {
369
- return code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129791 || code >= 131072 && code <= 262141;
370
- }
371
- function estimateTextWidth(text, fontSize) {
372
- let ems = 0;
373
- for (const character of text) {
374
- const code = character.codePointAt(0) ?? 0;
375
- if (character === " ") ems += .28;
376
- else if (isWideCharacter(code)) ems += 1;
377
- else if (/[il.,:;'|!]/.test(character)) ems += .3;
378
- else if (/[mwMW@]/.test(character)) ems += .85;
379
- else if (/[A-Z0-9]/.test(character)) ems += .64;
380
- else ems += .55;
381
- }
382
- return ems * fontSize;
383
- }
384
- function estimateLines(text, { fontSize }, maxWidth) {
385
- const measure = (line) => estimateTextWidth(line, fontSize);
386
- const paragraphs = text.split("\n");
387
- const lines = maxWidth ? paragraphs.flatMap((paragraph) => wrapParagraph(paragraph, maxWidth, measure)) : paragraphs;
388
- return {
389
- lines,
390
- width: lines.reduce((max, line) => Math.max(max, measure(line)), 0)
391
- };
392
- }
393
- function wrapParagraph(paragraph, width, measure) {
394
- if (!paragraph) return [""];
395
- const tokens = paragraph.match(/[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\uff00-\uff60]|[^\s\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\uff00-\uff60]+|\s+/g) ?? [paragraph];
396
- const lines = [];
397
- let line = "";
398
- for (const token of tokens) {
399
- const candidate = line + token;
400
- if (!line || measure(candidate.trimEnd()) <= width) {
401
- line = candidate;
402
- continue;
403
- }
404
- lines.push(line.trimEnd());
405
- line = /^\s+$/.test(token) ? "" : token;
406
- }
407
- lines.push(line.trimEnd());
408
- return lines;
409
- }
410
- const textSizeCache = /* @__PURE__ */ new Map();
411
- const TEXT_CACHE_LIMIT = 4096;
412
- function clampBoardFontSize(fontSize) {
413
- return Math.min(512, Math.max(2, fontSize));
414
- }
415
- function layoutBoardText(props) {
416
- const fontSize = clampBoardFontSize(props.fontSize ?? 24);
417
- const key = `${fontSize}|${props.fontWeight}|${props.font}|${props.lineHeight}|${props.width ?? ""}|${props.text}`;
418
- const cached = textSizeCache.get(key);
419
- if (cached) return cached;
420
- const font = {
421
- fontSize,
422
- fontWeight: props.fontWeight ?? 500,
423
- font: props.font ?? "sans"
424
- };
425
- const { lines, width } = estimateLines(props.text || "", font, props.width || void 0);
426
- const lineHeight = fontSize * (props.lineHeight ?? 4 / 3);
427
- const layout = {
428
- width: Math.max(props.width ?? 0, width, fontSize * .5),
429
- height: Math.max(1, lines.length) * lineHeight,
430
- lines,
431
- lineHeight
432
- };
433
- if (textSizeCache.size >= TEXT_CACHE_LIMIT) textSizeCache.clear();
434
- textSizeCache.set(key, layout);
435
- return layout;
436
- }
437
- function drawSampleRadius(strokeWidth, pressure) {
438
- const clamped = Math.min(1, Math.max(0, pressure));
439
- return Math.max(.5, strokeWidth / 2 * (.5 + clamped));
440
- }
441
- function drawPointsBounds(points, strokeWidth) {
442
- if (points.length === 0) return {
443
- x: 0,
444
- y: 0,
445
- width: 1,
446
- height: 1
447
- };
448
- let minX = Number.POSITIVE_INFINITY;
449
- let minY = Number.POSITIVE_INFINITY;
450
- let maxX = Number.NEGATIVE_INFINITY;
451
- let maxY = Number.NEGATIVE_INFINITY;
452
- for (const point of points) {
453
- const radius = drawSampleRadius(strokeWidth, point.p);
454
- minX = Math.min(minX, point.x - radius);
455
- minY = Math.min(minY, point.y - radius);
456
- maxX = Math.max(maxX, point.x + radius);
457
- maxY = Math.max(maxY, point.y + radius);
458
- }
459
- return {
460
- x: minX,
461
- y: minY,
462
- width: Math.max(1, maxX - minX),
463
- height: Math.max(1, maxY - minY)
464
- };
465
- }
466
- function pointsRect(points) {
467
- let minX = Number.POSITIVE_INFINITY;
468
- let minY = Number.POSITIVE_INFINITY;
469
- let maxX = Number.NEGATIVE_INFINITY;
470
- let maxY = Number.NEGATIVE_INFINITY;
471
- for (const point of points) {
472
- minX = Math.min(minX, point.x);
473
- minY = Math.min(minY, point.y);
474
- maxX = Math.max(maxX, point.x);
475
- maxY = Math.max(maxY, point.y);
476
- }
477
- if (!Number.isFinite(minX)) return {
478
- x: 0,
479
- y: 0,
480
- width: 1,
481
- height: 1
482
- };
483
- return {
484
- x: minX,
485
- y: minY,
486
- width: Math.max(1, maxX - minX),
487
- height: Math.max(1, maxY - minY)
488
- };
489
- }
490
- function isArrowBinding(end) {
491
- return typeof end.item === "string";
492
- }
493
- function arrowBindings(item) {
494
- if (item.type !== "arrow") return [];
495
- const props = item.props;
496
- const ids = [props.start, props.end].filter(isArrowBinding).map((end) => end.item);
497
- return [...new Set(ids)];
498
- }
499
- function bendControl(start, end, bend) {
500
- const mid = {
501
- x: (start.x + end.x) / 2,
502
- y: (start.y + end.y) / 2
503
- };
504
- if (!bend) return mid;
505
- const dx = end.x - start.x;
506
- const dy = end.y - start.y;
507
- const length = Math.hypot(dx, dy) || 1;
508
- const offset = bend * length;
509
- return {
510
- x: mid.x + -dy / length * offset,
511
- y: mid.y + dx / length * offset
512
- };
513
- }
514
- const MAX_DEPTH = 64;
515
- function createBoardLayout(initialLookup, dependents) {
516
- let lookup = initialLookup;
517
- const boxes = /* @__PURE__ */ new Map();
518
- const matrices = /* @__PURE__ */ new Map();
519
- const bounds = /* @__PURE__ */ new Map();
520
- function matrixOf(id, depth = 0) {
521
- const cached = matrices.get(id);
522
- if (cached) return cached;
523
- const item = lookup(id);
524
- if (!item) return IDENTITY_MATRIX;
525
- const matrix = multiplyMatrix(item.parent && depth < MAX_DEPTH && lookup(item.parent) ? matrixOf(item.parent, depth + 1) : IDENTITY_MATRIX, localMatrixOf(id));
526
- matrices.set(id, matrix);
527
- return matrix;
528
- }
529
- function localMatrixOf(id) {
530
- const item = lookup(id);
531
- if (!item) return IDENTITY_MATRIX;
532
- if (item.type === "arrow") return translate(item.position);
533
- const box = boxOf(id);
534
- const scale = scaleVector(item.scale);
535
- const radians = item.rotation * Math.PI / 180;
536
- const cos = Math.cos(radians);
537
- const sin = Math.sin(radians);
538
- const ox = box.x + item.origin.x * box.width;
539
- const oy = box.y + item.origin.y * box.height;
540
- const a = cos * scale.x;
541
- const b = sin * scale.x;
542
- const c = -sin * scale.y;
543
- const d = cos * scale.y;
544
- return [
545
- a,
546
- b,
547
- c,
548
- d,
549
- item.position.x + ox - (a * ox + c * oy),
550
- item.position.y + oy - (b * ox + d * oy)
551
- ];
552
- }
553
- function boxOf(id, depth = 0) {
554
- const cached = boxes.get(id);
555
- if (cached) return cached;
556
- const item = lookup(id);
557
- let box;
558
- if (!item) box = {
559
- x: 0,
560
- y: 0,
561
- width: 1,
562
- height: 1
563
- };
564
- else if ("size" in item && item.size) box = {
565
- x: 0,
566
- y: 0,
567
- width: item.size.width,
568
- height: item.size.height
569
- };
570
- else if (item.type === "text") {
571
- const layout = layoutBoardText(item.props);
572
- box = {
573
- x: 0,
574
- y: 0,
575
- width: layout.width,
576
- height: layout.height
577
- };
578
- } else if (item.type === "draw") box = drawPointsBounds(item.props.points, item.style.strokeWidth ?? 4);
579
- else if (item.type === "arrow") {
580
- const props = item.props;
581
- const toLocal = invertMatrix(multiplyMatrix(parentOf(item, depth), translate(item.position)));
582
- const start = isArrowBinding(props.start) ? applyMatrix(toLocal, resolveBoundEnd(item, props.start, props.end, depth)) : props.start;
583
- const end = isArrowBinding(props.end) ? applyMatrix(toLocal, resolveBoundEnd(item, props.end, props.start, depth)) : props.end;
584
- const points = [
585
- start,
586
- end,
587
- ...props.waypoints
588
- ];
589
- if (props.bend) points.push(bendControl(start, end, props.bend));
590
- const rect = pointsRect(points);
591
- const pad = Math.max(8, (item.style.strokeWidth ?? 2.5) * 2);
592
- box = {
593
- x: rect.x - pad,
594
- y: rect.y - pad,
595
- width: rect.width + pad * 2,
596
- height: rect.height + pad * 2
597
- };
598
- } else box = {
599
- x: 0,
600
- y: 0,
601
- ...boardDefaultSize(item.type)
602
- };
603
- boxes.set(id, box);
604
- return box;
605
- }
606
- function parentOf(item, depth) {
607
- return item.parent && depth < MAX_DEPTH && lookup(item.parent) ? matrixOf(item.parent, depth + 1) : IDENTITY_MATRIX;
608
- }
609
- function resolveBoundEnd(arrow, end, other, depth) {
610
- const target = lookup(end.item);
611
- const arrowSpace = multiplyMatrix(parentOf(arrow, depth), translate(arrow.position));
612
- if (!target || target.type === "arrow" || depth >= MAX_DEPTH) return applyMatrix(arrowSpace, {
613
- x: 0,
614
- y: 0
615
- });
616
- const frame = frameOf(end.item, depth + 1);
617
- const toward = isArrowBinding(other) ? lookup(other.item) && lookup(other.item)?.type !== "arrow" ? center(frameOf(other.item, depth + 1)) : center(frame) : applyMatrix(arrowSpace, other);
618
- return anchorPoint(frame, end.anchor, toward);
619
- }
620
- function frameOf(id, depth = 0) {
621
- boxOf(id, depth);
622
- const box = boxes.get(id);
623
- const matrix = matrixOf(id, depth);
624
- const [a, b, c, d] = matrix;
625
- const centerPoint = applyMatrix(matrix, {
626
- x: box.x + box.width / 2,
627
- y: box.y + box.height / 2
628
- });
629
- const width = box.width * Math.hypot(a, b);
630
- const height = box.height * Math.hypot(c, d);
631
- return {
632
- x: centerPoint.x - width / 2,
633
- y: centerPoint.y - height / 2,
634
- width,
635
- height,
636
- rotation: Math.atan2(b, a) * 180 / Math.PI
637
- };
638
- }
639
- function boundsOf(id) {
640
- const cached = bounds.get(id);
641
- if (cached) return cached;
642
- const box = boxOf(id);
643
- const matrix = matrixOf(id);
644
- const rect = pointsRect([
645
- applyMatrix(matrix, {
646
- x: box.x,
647
- y: box.y
648
- }),
649
- applyMatrix(matrix, {
650
- x: box.x + box.width,
651
- y: box.y
652
- }),
653
- applyMatrix(matrix, {
654
- x: box.x + box.width,
655
- y: box.y + box.height
656
- }),
657
- applyMatrix(matrix, {
658
- x: box.x,
659
- y: box.y + box.height
660
- })
661
- ]);
662
- bounds.set(id, rect);
663
- return rect;
664
- }
665
- function setLookup(nextLookup) {
666
- lookup = nextLookup;
667
- }
668
- function invalidate(ids) {
669
- if (!ids) {
670
- boxes.clear();
671
- matrices.clear();
672
- bounds.clear();
673
- return;
674
- }
675
- const queue = [...ids];
676
- const seen = /* @__PURE__ */ new Set();
677
- while (queue.length) {
678
- const id = queue.pop();
679
- if (seen.has(id)) continue;
680
- seen.add(id);
681
- boxes.delete(id);
682
- matrices.delete(id);
683
- bounds.delete(id);
684
- if (dependents) for (const next of dependents(id)) queue.push(next);
685
- }
686
- }
687
- return {
688
- has: (id) => lookup(id) !== void 0,
689
- box: (id) => boxOf(id),
690
- matrix: (id) => matrixOf(id),
691
- localMatrix: (id) => localMatrixOf(id),
692
- parentMatrix: (id) => {
693
- const item = lookup(id);
694
- return item ? parentOf(item, 0) : IDENTITY_MATRIX;
695
- },
696
- bounds: boundsOf,
697
- frame: (id) => frameOf(id),
698
- arrowEnd: (id, which) => {
699
- const item = lookup(id);
700
- if (item?.type !== "arrow") return {
701
- x: 0,
702
- y: 0
703
- };
704
- const arrow = item;
705
- const [end, other] = which === "start" ? [arrow.props.start, arrow.props.end] : [arrow.props.end, arrow.props.start];
706
- return isArrowBinding(end) ? resolveBoundEnd(arrow, end, other, 0) : applyMatrix(matrixOf(id), end);
707
- },
708
- invalidate,
709
- setLookup
710
- };
711
- }
712
- function translate(point) {
713
- return [
714
- 1,
715
- 0,
716
- 0,
717
- 1,
718
- point.x,
719
- point.y
720
- ];
721
- }
722
- function center(frame) {
723
- return {
724
- x: frame.x + frame.width / 2,
725
- y: frame.y + frame.height / 2
726
- };
727
- }
728
- function rotateAround(point, pivot, degrees) {
729
- if (!degrees) return point;
730
- const radians = degrees * Math.PI / 180;
731
- const cos = Math.cos(radians);
732
- const sin = Math.sin(radians);
733
- const dx = point.x - pivot.x;
734
- const dy = point.y - pivot.y;
735
- return {
736
- x: pivot.x + dx * cos - dy * sin,
737
- y: pivot.y + dx * sin + dy * cos
738
- };
739
- }
740
- function framePoint(frame, nx, ny) {
741
- return rotateAround({
742
- x: frame.x + nx * frame.width,
743
- y: frame.y + ny * frame.height
744
- }, center(frame), frame.rotation);
745
- }
746
- function autoAnchorSide(frame, toward) {
747
- const local = rotateAround(toward, center(frame), -frame.rotation);
748
- const c = center(frame);
749
- const dx = (local.x - c.x) / Math.max(frame.width / 2, 1e-4);
750
- const dy = (local.y - c.y) / Math.max(frame.height / 2, 1e-4);
751
- if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? "right" : "left";
752
- return dy >= 0 ? "bottom" : "top";
753
- }
754
- function sideAnchor(side, offset) {
755
- switch (side) {
756
- case "top": return {
757
- x: offset,
758
- y: 0
759
- };
760
- case "bottom": return {
761
- x: offset,
762
- y: 1
763
- };
764
- case "left": return {
765
- x: 0,
766
- y: offset
767
- };
768
- default: return {
769
- x: 1,
770
- y: offset
771
- };
772
- }
773
- }
774
- function anchorPoint(frame, anchor, toward) {
775
- if (anchor === "auto") {
776
- const normalized = sideAnchor(autoAnchorSide(frame, toward), .5);
777
- return framePoint(frame, normalized.x, normalized.y);
778
- }
779
- if ("side" in anchor) {
780
- const normalized = sideAnchor(anchor.side, anchor.offset);
781
- return framePoint(frame, normalized.x, normalized.y);
782
- }
783
- return framePoint(frame, anchor.x, anchor.y);
784
- }
785
- //#endregion
786
- //#region ../protocol/dist/board-patch.js
787
- function slot(map, key) {
788
- let value = map[key];
789
- if (!value) {
790
- value = {};
791
- map[key] = value;
792
- }
793
- return value;
794
- }
795
- function emptyDelta() {
796
- return {
797
- items: {},
798
- animations: {},
799
- tracks: {}
800
- };
801
- }
802
- function withoutEmpty(value) {
803
- for (const [key, entry] of Object.entries(value)) if (entry === void 0 || isRecord$6(entry) && Object.keys(entry).length === 0) delete value[key];
804
- return value;
805
- }
806
- function isRecord$6(value) {
807
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
808
- }
809
- function mergePatch(target, patch) {
810
- if (!isRecord$6(patch)) return patch;
811
- const result = isRecord$6(target) ? { ...target } : {};
812
- for (const [key, value] of Object.entries(patch)) if (value === null) delete result[key];
813
- else result[key] = mergePatch(result[key], value);
814
- return result;
815
- }
816
- function def(schema) {
817
- return schema._zod.def;
818
- }
819
- function unwrap(schema) {
820
- let current = schema;
821
- for (let guard = 0; guard < 16; guard += 1) {
822
- const d = def(current);
823
- if (d.type === "optional" || d.type === "default" || d.type === "nullable" || d.type === "readonly" || d.type === "prefault") current = d.innerType;
824
- else if (d.type === "pipe") current = d.in;
825
- else return current;
826
- }
827
- return current;
828
- }
829
- function unwrapObjectShape(schema) {
830
- const d = def(unwrap(schema));
831
- return d.type === "object" ? d.shape : null;
832
- }
833
- function schemaAtPath(schema, path) {
834
- let current = schema;
835
- for (const segment of path) {
836
- const d = def(unwrap(current));
837
- if (d.type === "object") {
838
- const shape = d.shape;
839
- if (!Object.hasOwn(shape, segment)) return null;
840
- current = shape[segment];
841
- } else if (d.type === "record") current = d.valueType;
842
- else if (d.type === "union") {
843
- const branch = d.options.find((option) => unwrapObjectShape(option)?.[segment]);
844
- if (!branch) return null;
845
- current = unwrapObjectShape(branch)[segment];
846
- } else return null;
847
- }
848
- return current;
849
- }
850
- const NOT_ANIMATABLE = /* @__PURE__ */ new Set([
851
- "type",
852
- "parent",
853
- "z",
854
- "locked",
855
- "metadata",
856
- "script"
857
- ]);
858
- const CAMERA_PROPERTIES = {
859
- focus: {
860
- kind: "discrete",
861
- schema: z.union([z.string().min(1), z.object({
862
- x: z.number().finite(),
863
- y: z.number().finite(),
864
- width: z.number().finite().positive(),
865
- height: z.number().finite().positive()
866
- }).strict()]).describe("Item id or world-space rect to fit with 48 px padding; holds until the next focus keyframe.")
867
- },
868
- zoom: {
869
- kind: "number",
870
- schema: z.number().finite().positive().describe("Pixels per world unit; overrides the zoom computed by focus."),
871
- multiplicative: true
872
- },
873
- center: {
874
- kind: "vec2",
875
- schema: BoardVec2Schema.describe("World-space viewport center; overrides focus positioning using the resolved zoom.")
876
- },
877
- shake: {
878
- kind: "number",
879
- schema: z.number().finite().nonnegative().describe("Shake amplitude in screen pixels, applied after focus, zoom and center.")
880
- }
881
- };
882
- const ANIMATION_PROPERTIES = { time: {
883
- kind: "number",
884
- schema: z.number().finite().nonnegative()
885
- } };
886
- function kindOf(schema, path) {
887
- const leaf = path[path.length - 1];
888
- if (path.length === 1 && leaf === "scale") return "scale";
889
- if (schema === BoardColorSchema || unwrap(schema) === BoardColorSchema) return "color";
890
- if (def(unwrap(schema)).type === "number") return "number";
891
- const shape = unwrapObjectShape(schema);
892
- if (shape && Object.keys(shape).length === 2 && "x" in shape && "y" in shape) return "vec2";
893
- return "discrete";
894
- }
895
- function resolveBoardProperty(targetType, property) {
896
- if (targetType === "camera") {
897
- const spec = CAMERA_PROPERTIES[property];
898
- return spec ? {
899
- ok: true,
900
- spec
901
- } : {
902
- ok: false,
903
- message: `camera has no property ${property}; expected ${Object.keys(CAMERA_PROPERTIES).join(", ")}`
904
- };
905
- }
906
- if (targetType === "animation") {
907
- const spec = ANIMATION_PROPERTIES[property];
908
- return spec ? {
909
- ok: true,
910
- spec
911
- } : {
912
- ok: false,
913
- message: `animations are driven through time`
914
- };
915
- }
916
- const path = property.split(".");
917
- if (path.some((segment) => !segment) || NOT_ANIMATABLE.has(path[0])) return {
918
- ok: false,
919
- message: `${property} cannot be animated`
920
- };
921
- const schema = boardItemSchema(targetType);
922
- if (!schema) return {
923
- ok: false,
924
- message: `unknown item type ${targetType}`
925
- };
926
- if (!isBuiltinItemType(targetType) && path[0] === "props") return {
927
- ok: true,
928
- spec: {
929
- kind: "discrete",
930
- schema: z.unknown()
931
- }
932
- };
933
- const leaf = schemaAtPath(schema, path);
934
- if (!leaf) return {
935
- ok: false,
936
- message: `${targetType} has no property ${property}`
937
- };
938
- const kind = kindOf(leaf, path);
939
- return {
940
- ok: true,
941
- spec: {
942
- kind,
943
- schema: unwrap(leaf),
944
- ...property === "opacity" || kind === "scale" ? { multiplicative: true } : {}
945
- }
946
- };
947
- }
948
- function lastKeyframeAt(trackPatches) {
949
- let end = 0;
950
- for (const track of trackPatches) {
951
- const keyframes = track?.keyframes;
952
- if (!Array.isArray(keyframes)) continue;
953
- for (const keyframe of keyframes) {
954
- const at = keyframe?.at;
955
- if (typeof at === "number" && Number.isFinite(at)) end = Math.max(end, at);
956
- }
957
- }
958
- return end > 0 ? end : void 0;
959
- }
960
- function trackReferences(track) {
961
- const refs = /* @__PURE__ */ new Set([track.target]);
962
- if (track.target === "camera" && track.property === "focus") {
963
- for (const keyframe of track.keyframes) if (typeof keyframe.value === "string") refs.add(keyframe.value);
964
- }
965
- refs.delete(BOARD_CAMERA_TARGET);
966
- return [...refs];
967
- }
968
- function pruneBoardTrack(track, deleted) {
969
- if (deleted.has(track.target)) return null;
970
- if (track.target !== "camera" || track.property !== "focus") return track;
971
- const keyframes = track.keyframes.filter((entry) => typeof entry.value !== "string" || !deleted.has(entry.value));
972
- if (keyframes.length === track.keyframes.length) return track;
973
- return keyframes.length ? {
974
- ...track,
975
- keyframes
976
- } : null;
977
- }
978
- function error(path, code, message) {
979
- return {
980
- severity: "error",
981
- code,
982
- path,
983
- message
984
- };
985
- }
986
- function stripId(value) {
987
- if (!("id" in value)) return value;
988
- const { id: _id, ...rest } = value;
989
- return rest;
990
- }
991
- function applyBoardPatch(state, input, options = {}) {
992
- const parsedPatch = BoardPatchSchema.safeParse(input);
993
- if (!parsedPatch.success) return {
994
- ok: false,
995
- diagnostics: parsedPatch.error.issues.map((issue) => error(issue.path.map(String).join("."), "INVALID_PATCH", issue.message))
996
- };
997
- const patch = parsedPatch.data;
998
- const diagnostics = [];
999
- const nextItems = /* @__PURE__ */ new Map();
1000
- const nextAnimations = /* @__PURE__ */ new Map();
1001
- const nextTracks = /* @__PURE__ */ new Map();
1002
- let nextSettings;
1003
- const item = (id) => nextItems.has(id) ? nextItems.get(id) ?? void 0 : state.item(id);
1004
- const animation = (id) => nextAnimations.has(id) ? nextAnimations.get(id) ?? void 0 : state.animation(id);
1005
- const setTrack = (animationId, trackId, value) => {
1006
- const tracks = nextTracks.get(animationId) ?? /* @__PURE__ */ new Map();
1007
- tracks.set(trackId, value);
1008
- nextTracks.set(animationId, tracks);
1009
- };
1010
- const track = (animationId, trackId) => {
1011
- const pending = nextTracks.get(animationId);
1012
- if (pending?.has(trackId)) return pending.get(trackId) ?? void 0;
1013
- return state.track(animationId, trackId);
1014
- };
1015
- if (patch.board !== void 0) {
1016
- const merged = patch.board === null ? {} : mergePatch(state.settings(), patch.board);
1017
- const parsed = BoardSettingsSchema.safeParse(merged);
1018
- if (parsed.success) nextSettings = parsed.data;
1019
- else for (const issue of parsed.error.issues) diagnostics.push(error(["board", ...issue.path.map(String)].join("."), "INVALID_BOARD", issue.message));
1020
- }
1021
- const zByParent = /* @__PURE__ */ new Map();
1022
- for (const [id, value] of Object.entries(patch.items ?? {})) {
1023
- if (value === null) {
1024
- if (state.item(id)) nextItems.set(id, null);
1025
- continue;
1026
- }
1027
- const current = state.item(id);
1028
- const body = stripId(value);
1029
- const candidate = !current || typeof body.type === "string" && body.type !== current.type ? { ...body } : mergePatch(current, body);
1030
- if (candidate.z === void 0) {
1031
- const parent = typeof candidate.parent === "string" ? candidate.parent : void 0;
1032
- const key = parent ?? "";
1033
- const top = zByParent.get(key) ?? state.maxZ(parent);
1034
- candidate.z = current?.z ?? top + 1;
1035
- if (!current) zByParent.set(key, top + 1);
1036
- }
1037
- const parsed = parseBoardItem(candidate, `items.${id}`);
1038
- if (!parsed.ok) {
1039
- diagnostics.push(...parsed.diagnostics);
1040
- continue;
1041
- }
1042
- if (current && boardJsonEquals(current, parsed.item)) continue;
1043
- nextItems.set(id, parsed.item);
1044
- }
1045
- for (const [id, value] of Object.entries(patch.animations ?? {})) {
1046
- if (value === null) {
1047
- if (!state.animation(id)) continue;
1048
- nextAnimations.set(id, null);
1049
- for (const trackId of state.trackIds(id)) setTrack(id, trackId, null);
1050
- continue;
1051
- }
1052
- const { tracks, ...headerPatch } = stripId(value);
1053
- if (state.item(id) || nextItems.get(id)) {
1054
- diagnostics.push(error(`animations.${id}`, "ID_CONFLICT", `${id} is already an item id`));
1055
- continue;
1056
- }
1057
- const current = state.animation(id);
1058
- if (!current || Object.keys(headerPatch).length > 0) {
1059
- const header = current ? mergePatch(current, headerPatch) : {
1060
- ...headerPatch,
1061
- ...headerPatch.duration === void 0 ? { duration: lastKeyframeAt([...Object.values(tracks ?? {}), ...state.trackIds(id).map((trackId) => state.track(id, trackId))]) } : {}
1062
- };
1063
- const parsed = BoardAnimationHeaderSchema.safeParse(header);
1064
- if (!parsed.success) {
1065
- for (const issue of parsed.error.issues) diagnostics.push(error([
1066
- "animations",
1067
- id,
1068
- ...issue.path.map(String)
1069
- ].join("."), "INVALID_ANIMATION", issue.message));
1070
- continue;
1071
- }
1072
- if (!current || !boardJsonEquals(current, parsed.data)) nextAnimations.set(id, parsed.data);
1073
- }
1074
- for (const [trackId, trackPatch] of Object.entries(tracks ?? {})) {
1075
- const existing = state.track(id, trackId);
1076
- if (trackPatch === null) {
1077
- if (existing) setTrack(id, trackId, null);
1078
- continue;
1079
- }
1080
- const parsed = BoardTrackSchema.safeParse(existing ? mergePatch(existing, trackPatch) : trackPatch);
1081
- if (!parsed.success) {
1082
- for (const issue of parsed.error.issues) diagnostics.push(error([
1083
- "animations",
1084
- id,
1085
- "tracks",
1086
- trackId,
1087
- ...issue.path.map(String)
1088
- ].join("."), "INVALID_TRACK", issue.message));
1089
- continue;
1090
- }
1091
- if (!existing || !boardJsonEquals(existing, parsed.data)) setTrack(id, trackId, parsed.data);
1092
- }
1093
- }
1094
- for (const id of nextItems.keys()) if (nextItems.get(id) && animation(id)) diagnostics.push(error(`items.${id}`, "ID_CONFLICT", `${id} is already an animation id`));
1095
- if (diagnostics.length) return {
1096
- ok: false,
1097
- diagnostics
1098
- };
1099
- const deletedItems = [...nextItems].filter(([, value]) => value === null).map(([id]) => id);
1100
- const deletionLayout = createBoardLayout((id) => item(id) ?? state.item(id));
1101
- const pendingDeletes = [...deletedItems];
1102
- while (pendingDeletes.length) {
1103
- const id = pendingDeletes.pop();
1104
- for (const child of state.children(id)) {
1105
- if (nextItems.has(child) && nextItems.get(child)?.parent !== id) continue;
1106
- if (options.cascade) {
1107
- if (nextItems.get(child) !== null) {
1108
- nextItems.set(child, null);
1109
- pendingDeletes.push(child);
1110
- }
1111
- } else diagnostics.push(error(`items.${id}`, "ITEM_REFERENCED", `${id} still has child ${child}; delete it too or use cascade`));
1112
- }
1113
- for (const arrowId of state.binders(id)) {
1114
- const arrow = item(arrowId);
1115
- if (arrow?.type !== "arrow") continue;
1116
- const props = { ...arrow.props };
1117
- for (const which of ["start", "end"]) {
1118
- const end = props[which];
1119
- if (!isArrowBinding(end) || end.item !== id) continue;
1120
- const world = options.resolveArrowEnd?.(arrowId, which) ?? deletionLayout.arrowEnd(arrowId, which);
1121
- props[which] = applyMatrix(invertMatrix(deletionLayout.matrix(arrowId)), world);
1122
- }
1123
- nextItems.set(arrowId, {
1124
- ...arrow,
1125
- props
1126
- });
1127
- }
1128
- for (const ref of state.trackRefs(id)) {
1129
- const current = track(ref.animation, ref.track);
1130
- if (!current || !trackReferences(current).includes(id)) continue;
1131
- if (options.cascade) setTrack(ref.animation, ref.track, pruneBoardTrack(current, /* @__PURE__ */ new Set([id])));
1132
- else diagnostics.push(error(`items.${id}`, "ITEM_REFERENCED", `${id} is animated by ${ref.animation}.${ref.track}; delete the track or use cascade`));
1133
- }
1134
- }
1135
- for (const [id, value] of nextAnimations) {
1136
- if (value !== null) continue;
1137
- for (const ref of state.trackRefs(id)) {
1138
- if (ref.animation === id || track(ref.animation, ref.track) === void 0) continue;
1139
- if (options.cascade) setTrack(ref.animation, ref.track, null);
1140
- else diagnostics.push(error(`animations.${id}`, "ANIMATION_REFERENCED", `${id} is nested in ${ref.animation}.${ref.track}; delete the track or use cascade`));
1141
- }
1142
- }
1143
- const validationState = {
1144
- ...state,
1145
- trackIds: (id) => [.../* @__PURE__ */ new Set([...state.trackIds(id), ...nextTracks.get(id)?.keys() ?? []])]
1146
- };
1147
- const validationItems = new Map(nextItems);
1148
- const descendants = [];
1149
- for (const [id, value] of nextItems) if (value && value.parent !== state.item(id)?.parent) descendants.push(...state.children(id));
1150
- const seenDescendants = /* @__PURE__ */ new Set();
1151
- while (descendants.length) {
1152
- const id = descendants.pop();
1153
- if (seenDescendants.has(id)) continue;
1154
- seenDescendants.add(id);
1155
- validationItems.set(id, item(id) ?? null);
1156
- descendants.push(...state.children(id));
1157
- }
1158
- for (const [id, value] of validationItems) {
1159
- if (!value) continue;
1160
- if (value.parent !== void 0) {
1161
- const parent = item(value.parent);
1162
- if (!parent) diagnostics.push(error(`items.${id}.parent`, "PARENT_NOT_FOUND", `parent ${value.parent} does not exist`));
1163
- else if (parent.type !== "frame") diagnostics.push(error(`items.${id}.parent`, "INVALID_PARENT", `parent ${value.parent} is a ${parent.type}; only frames contain items`));
1164
- else {
1165
- let cursor = value.parent;
1166
- for (let depth = 0; cursor; depth += 1) {
1167
- if (depth >= 64) {
1168
- diagnostics.push(error(`items.${id}.parent`, "PARENT_DEPTH", "parent hierarchy exceeds 64 levels"));
1169
- break;
1170
- }
1171
- if (cursor === id) {
1172
- diagnostics.push(error(`items.${id}.parent`, "PARENT_CYCLE", `${id} cannot be inside itself`));
1173
- break;
1174
- }
1175
- cursor = item(cursor)?.parent;
1176
- }
1177
- }
1178
- }
1179
- for (const bound of arrowBindings(value)) {
1180
- const target = item(bound);
1181
- if (!target) diagnostics.push(error(`items.${id}.props`, "BINDING_NOT_FOUND", `bound item ${bound} does not exist`));
1182
- else if (bound === id || target.type === "arrow") diagnostics.push(error(`items.${id}.props`, "INVALID_BINDING", "arrows bind to items other than arrows"));
1183
- }
1184
- }
1185
- for (const [id, value] of nextItems) {
1186
- if (!value || !state.item(id) || state.item(id)?.type === value.type) continue;
1187
- if (value.type !== "frame") {
1188
- for (const child of state.children(id)) if (item(child)?.parent === id) diagnostics.push(error(`items.${child}.parent`, "INVALID_PARENT", `${id} is not a frame`));
1189
- }
1190
- if (value.type === "arrow") for (const binder of state.binders(id)) {
1191
- const arrow = item(binder);
1192
- if (arrow && arrowBindings(arrow).includes(id)) diagnostics.push(error(`items.${binder}.props`, "INVALID_BINDING", "arrows cannot bind to arrows"));
1193
- }
1194
- for (const ref of state.trackRefs(id)) {
1195
- const current = track(ref.animation, ref.track);
1196
- if (current) diagnostics.push(...validateTrack(`animations.${ref.animation}.tracks.${ref.track}`, ref.animation, current, item, animation, track, validationState));
1197
- }
1198
- }
1199
- for (const [animationId, tracks] of nextTracks) {
1200
- if (!animation(animationId)) {
1201
- for (const [trackId, value] of tracks) if (value) diagnostics.push(error(`animations.${animationId}.tracks.${trackId}`, "ANIMATION_NOT_FOUND", `animation ${animationId} does not exist`));
1202
- continue;
1203
- }
1204
- for (const [trackId, value] of tracks) {
1205
- if (!value) continue;
1206
- diagnostics.push(...validateTrack(`animations.${animationId}.tracks.${trackId}`, animationId, value, item, animation, track, validationState));
1207
- }
1208
- }
1209
- if (diagnostics.length) return {
1210
- ok: false,
1211
- diagnostics
1212
- };
1213
- const before = emptyDelta();
1214
- const after = emptyDelta();
1215
- if (nextSettings && !boardJsonEquals(nextSettings, state.settings())) {
1216
- before.board = state.settings();
1217
- after.board = nextSettings;
1218
- }
1219
- for (const [id, value] of nextItems) {
1220
- const previous = state.item(id) ?? null;
1221
- if (boardJsonEquals(previous, value)) continue;
1222
- before.items[id] = previous;
1223
- after.items[id] = value;
1224
- }
1225
- for (const [id, value] of nextAnimations) {
1226
- before.animations[id] = state.animation(id) ?? null;
1227
- after.animations[id] = value;
1228
- }
1229
- for (const [animationId, tracks] of nextTracks) for (const [trackId, value] of tracks) {
1230
- const previous = state.track(animationId, trackId) ?? null;
1231
- if (boardJsonEquals(previous, value)) continue;
1232
- slot(before.tracks, animationId)[trackId] = previous;
1233
- slot(after.tracks, animationId)[trackId] = value;
1234
- }
1235
- return {
1236
- ok: true,
1237
- before: withoutEmpty(before),
1238
- after: withoutEmpty(after),
1239
- diagnostics: []
1240
- };
1241
- }
1242
- function validateTrack(path, animationId, value, item, animation, track, state) {
1243
- const diagnostics = [];
1244
- let targetType;
1245
- if (value.target === "camera") targetType = BOARD_CAMERA_TARGET;
1246
- else if (item(value.target)) targetType = item(value.target).type;
1247
- else if (animation(value.target)) {
1248
- targetType = "animation";
1249
- if (value.target === animationId || nestsInto(value.target, animationId, animation, track, state)) return [error(`${path}.target`, "ANIMATION_CYCLE", `${animationId} cannot contain itself through ${value.target}`)];
1250
- } else return [error(`${path}.target`, "TARGET_NOT_FOUND", `target ${value.target} is not an item, animation or camera`)];
1251
- const resolved = resolveBoardProperty(targetType, value.property);
1252
- if (!resolved.ok) return [error(`${path}.property`, "INVALID_PROPERTY", resolved.message)];
1253
- for (const [index, keyframe] of value.keyframes.entries()) {
1254
- const parsed = resolved.spec.schema.safeParse(keyframe.value);
1255
- if (!parsed.success) {
1256
- diagnostics.push(error(`${path}.keyframes.${index}.value`, "INVALID_KEYFRAME", parsed.error.issues[0]?.message ?? "invalid value"));
1257
- continue;
1258
- }
1259
- if (value.property === "focus" && typeof keyframe.value === "string" && !item(keyframe.value)) diagnostics.push(error(`${path}.keyframes.${index}.value`, "TARGET_NOT_FOUND", `focus item ${keyframe.value} does not exist`));
1260
- }
1261
- if (value.interpolation === "spline" && resolved.spec.kind !== "vec2") diagnostics.push(error(`${path}.interpolation`, "INVALID_TRACK", "spline interpolation needs a position-like property"));
1262
- return diagnostics;
1263
- }
1264
- function nestsInto(from, into, animation, track, state) {
1265
- const seen = /* @__PURE__ */ new Set();
1266
- const stack = [from];
1267
- while (stack.length) {
1268
- const current = stack.pop();
1269
- if (current === into) return true;
1270
- if (seen.has(current) || !animation(current)) continue;
1271
- seen.add(current);
1272
- for (const trackId of state.trackIds(current)) {
1273
- const value = track(current, trackId);
1274
- if (value && value.property === "time" && animation(value.target)) stack.push(value.target);
1275
- }
1276
- }
1277
- return false;
1278
- }
1279
- function createDocumentState(document) {
1280
- let children = null;
1281
- let binders = null;
1282
- let refs = null;
1283
- const index = () => {
1284
- if (children) return;
1285
- children = /* @__PURE__ */ new Map();
1286
- binders = /* @__PURE__ */ new Map();
1287
- refs = /* @__PURE__ */ new Map();
1288
- for (const [id, value] of Object.entries(document.items)) {
1289
- if (value.parent) children.set(value.parent, [...children.get(value.parent) ?? [], id]);
1290
- for (const bound of arrowBindings(value)) binders.set(bound, [...binders.get(bound) ?? [], id]);
1291
- }
1292
- for (const [animationId, value] of Object.entries(document.animations)) for (const [trackId, track] of Object.entries(value.tracks)) for (const ref of trackReferences(track)) refs.set(ref, [...refs.get(ref) ?? [], {
1293
- animation: animationId,
1294
- track: trackId
1295
- }]);
1296
- };
1297
- return {
1298
- settings: () => document.board,
1299
- item: (id) => document.items[id],
1300
- animation: (id) => {
1301
- const value = document.animations[id];
1302
- if (!value) return void 0;
1303
- const { tracks: _tracks, ...header } = value;
1304
- return header;
1305
- },
1306
- track: (animationId, trackId) => document.animations[animationId]?.tracks[trackId],
1307
- trackIds: (animationId) => Object.keys(document.animations[animationId]?.tracks ?? {}),
1308
- children: (id) => {
1309
- index();
1310
- return children?.get(id) ?? [];
1311
- },
1312
- binders: (id) => {
1313
- index();
1314
- return binders?.get(id) ?? [];
1315
- },
1316
- trackRefs: (id) => {
1317
- index();
1318
- return refs?.get(id) ?? [];
1319
- },
1320
- maxZ: (parent) => {
1321
- let max = 0;
1322
- for (const value of Object.values(document.items)) if (value.parent === parent && (value.z ?? 0) > max) max = value.z ?? 0;
1323
- return max;
1324
- }
1325
- };
1326
- }
1327
- function applyBoardDelta(document, delta) {
1328
- const items = delta.items ? { ...document.items } : document.items;
1329
- for (const [id, value] of Object.entries(delta.items ?? {})) if (value) items[id] = value;
1330
- else delete items[id];
1331
- const animations = delta.animations || delta.tracks ? { ...document.animations } : document.animations;
1332
- for (const [id, value] of Object.entries(delta.animations ?? {})) if (value) animations[id] = {
1333
- ...value,
1334
- tracks: animations[id]?.tracks ?? {}
1335
- };
1336
- else delete animations[id];
1337
- for (const [animationId, tracks] of Object.entries(delta.tracks ?? {})) {
1338
- const current = animations[animationId];
1339
- if (!current) continue;
1340
- const nextTracks = { ...current.tracks };
1341
- for (const [trackId, value] of Object.entries(tracks)) if (value) nextTracks[trackId] = value;
1342
- else delete nextTracks[trackId];
1343
- animations[animationId] = {
1344
- ...current,
1345
- tracks: nextTracks
1346
- };
1347
- }
1348
- return {
1349
- board: delta.board ?? document.board,
1350
- items,
1351
- animations
1352
- };
1353
- }
1354
- function applyBoardPatchToDocument(document, patch, options = {}) {
1355
- const result = applyBoardPatch(createDocumentState(document), patch, options);
1356
- return result.ok ? {
1357
- ok: true,
1358
- document: applyBoardDelta(document, result.after),
1359
- before: result.before,
1360
- after: result.after
1361
- } : result;
1362
- }
1363
- function parseBoardDocument(input) {
1364
- if (!isRecord$6(input)) return {
1365
- ok: false,
1366
- diagnostics: [error("", "INVALID_DOCUMENT", "expected an object")]
1367
- };
1368
- const result = applyBoardPatchToDocument({
1369
- board: BoardSettingsSchema.parse({}),
1370
- items: {},
1371
- animations: {}
1372
- }, {
1373
- ...isRecord$6(input.board) ? { board: input.board } : {},
1374
- ...isRecord$6(input.items) ? { items: input.items } : {},
1375
- ...isRecord$6(input.animations) ? { animations: input.animations } : {}
1376
- });
1377
- return result.ok ? {
1378
- ok: true,
1379
- document: result.document
1380
- } : result;
1381
- }
1382
- //#endregion
1383
307
  //#region ../protocol/dist/turn-origin.js
1384
308
  const record$1 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
1385
309
  const normalizeSessionTurnOrigin = (value) => {
@@ -5217,6 +4141,7 @@ var CohubClient = class {
5217
4141
  }
5218
4142
  transport;
5219
4143
  websocketClient;
4144
+ fileServices;
5220
4145
  appRuntime;
5221
4146
  appWindow;
5222
4147
  constructor(options = {}) {
@@ -5256,6 +4181,7 @@ var CohubClient = class {
5256
4181
  ...options.websocket,
5257
4182
  getAccessToken: options.websocket?.getAccessToken ?? getAccessToken
5258
4183
  });
4184
+ this.fileServices = createSpaceFileServices(this.transport, this.websocketClient);
5259
4185
  this.voice = new VoiceApi({
5260
4186
  env: options.voice?.env ?? options.env,
5261
4187
  url: options.voice?.url,
@@ -5396,7 +4322,7 @@ attach: attachAppEmbed },
5396
4322
  }
5397
4323
  };
5398
4324
  space(spaceId) {
5399
- return new SpaceClient(spaceId, this.transport, this.websocketClient);
4325
+ return new SpaceClient(spaceId, this.transport, this.websocketClient, this.fileServices);
5400
4326
  }
5401
4327
  onUserEvent(handler) {
5402
4328
  ensureRealtimeConnected(this.websocketClient);
@@ -7162,4 +6088,4 @@ function generationOutputSource(result, outputIndex) {
7162
6088
  return `data:${declared && MEDIA_TYPE_PATTERN.test(declared) ? declared : DEFAULT_MEDIA_TYPES[String(block.type)] ?? "application/octet-stream"};base64,${data}`;
7163
6089
  }
7164
6090
  //#endregion
7165
- export { APP_APPEARANCE_TOKENS, APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_DEVICES, APP_DOMAIN_PRICE_USD, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRoomRtc, AppRtcChannel, AppRtcError, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppRuntimeError, AppSurfaceApi, AppWindowApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_COLOR_TOKENS, BOARD_ITEM_TYPES, BillingApi, BoardAnimationSchema, BoardClient, BoardPatchSchema, BoardTrackSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_OPEN_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DISPLAY_ACTION_TYPES, DISPLAY_ELEMENT_REF_PATTERN, DISPLAY_SYSTEM_ACTIONS, DesktopCommandsApi, DisplayActionError, DisplayConnection, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HOME_SPACE_SLUG, HttpError, LOCAL_HARNESSES, LOCAL_HARNESS_SPECS, NATIVE_INGEST_MAX_TURNS, NATIVE_SYNC_SOURCE, OriginBrokerTransport, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, RoomsApi, SETTLED_TURN_STATUSES, SessionGenerationStreamClient, SessionPatchReducer, SpaceDisplaysApi, SpacePublicFilesApi, SpaceWebhooksApi, UPLOAD_ERROR_BATCH_TOO_LARGE, UPLOAD_ERROR_FILE_TOO_LARGE, UPLOAD_ERROR_RATE_LIMITED, UPLOAD_ERROR_TOO_MANY_FILES, UPLOAD_MAX_BATCH_BYTES, UPLOAD_MAX_BATCH_FILES, UPLOAD_MAX_FILE_BYTES, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, appAppearanceVar, applyAppAppearance, assertGenerationRequestAllowedByPolicy, assertHarnessAccess, attachAppEmbed, batchUploadEntries, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, buildViewportContentBlock, clearGrantedAppScopes, compileDisplayActions, connectDisplay, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createCohubClient, createHttpClient, createOriginAppResolver, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, defaultDesktopCommandTimeoutMs, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, fingerprintProjectionTurns, formatAppRef, formatWorkRef, generationOutputSource, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, harnessLabel, harnessSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isActiveGenerationTask, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isNativeClientTurn, isProjectionCompaction, isProjectionPendingGeneration, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isTextMime, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, localHarnessSchema, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, mimeTypeForPath, nativeIngestTurnSchema, nativeRuntimeEventSchema, nativeTurnCompleteSchema, nativeTurnProgressSchema, normalizeAppDomainHostname, normalizeBaseUrl, normalizeDisplayPoint, normalizeGenerationPolicy, normalizeRequestSource, normalizeSessionTurnOrigin, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardDocument, parseBoardItem, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseSpaceRef, parseWorkRef, projectNativeMessageMeta, projectNativeSession, readRequestSourceFromEnv, readSentTurns, readSessionTurnOrigin, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebBaseUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeNativeStopSchema, runtimeReadySchema, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, serializeProjection, serializeProjectionRecords, setGrantedAppScopes, toGenerationTaskView, trimProjectionTurnsToCompaction, validateArchiveBoundary };
6091
+ export { APP_APPEARANCE_TOKENS, APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_DEVICES, APP_DOMAIN_PRICE_USD, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRoomRtc, AppRtcChannel, AppRtcError, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppRuntimeError, AppSurfaceApi, AppWindowApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_COLOR_TOKENS, BOARD_ITEM_TYPES, BillingApi, BoardAnimationSchema, BoardClient, BoardPatchSchema, BoardTrackSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_OPEN_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DISPLAY_ACTION_TYPES, DISPLAY_ELEMENT_REF_PATTERN, DISPLAY_SYSTEM_ACTIONS, DesktopCommandsApi, DisplayActionError, DisplayConnection, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HOME_SPACE_SLUG, HttpError, LOCAL_HARNESSES, LOCAL_HARNESS_SPECS, NATIVE_INGEST_MAX_TURNS, NATIVE_SYNC_SOURCE, OriginBrokerTransport, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, RoomsApi, SETTLED_TURN_STATUSES, SessionGenerationStreamClient, SessionPatchReducer, SpaceDisplaysApi, SpacePublicFilesApi, SpaceWebhooksApi, UPLOAD_ERROR_BATCH_TOO_LARGE, UPLOAD_ERROR_FILE_TOO_LARGE, UPLOAD_ERROR_RATE_LIMITED, UPLOAD_ERROR_TOO_MANY_FILES, UPLOAD_MAX_BATCH_BYTES, UPLOAD_MAX_BATCH_FILES, UPLOAD_MAX_FILE_BYTES, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, appAppearanceVar, applyAppAppearance, assertGenerationRequestAllowedByPolicy, assertHarnessAccess, attachAppEmbed, batchUploadEntries, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, buildViewportContentBlock, clearFileUrlCache, clearGrantedAppScopes, compileDisplayActions, connectDisplay, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createCohubClient, createHttpClient, createOriginAppResolver, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, defaultDesktopCommandTimeoutMs, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, fingerprintProjectionTurns, formatAppRef, formatWorkRef, generationOutputSource, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, harnessLabel, harnessSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isActiveGenerationTask, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isNativeClientTurn, isProjectionCompaction, isProjectionPendingGeneration, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isTextMime, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, localHarnessSchema, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, mimeTypeForPath, nativeIngestTurnSchema, nativeRuntimeEventSchema, nativeTurnCompleteSchema, nativeTurnProgressSchema, normalizeAppDomainHostname, normalizeBaseUrl, normalizeDisplayPoint, normalizeGenerationPolicy, normalizeRequestSource, normalizeSessionTurnOrigin, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardDocument, parseBoardItem, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseSpaceRef, parseWorkRef, projectNativeMessageMeta, projectNativeSession, readRequestSourceFromEnv, readSentTurns, readSessionTurnOrigin, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebBaseUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeNativeStopSchema, runtimeReadySchema, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, serializeProjection, serializeProjectionRecords, setGrantedAppScopes, toGenerationTaskView, trimProjectionTurnsToCompaction, validateArchiveBoundary };