@neta-art/cohub 4.7.1 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { A as CronJobsApi, C as ReferencesApi, D as PromptsApi, E as SkillsApi, O as ModelsApi, S as SessionAccessApi, T as PublicAssetsApi, _ as createSessionGenerationStreamClient, a as ReferralsApi, b as createSessionPatchReducer, c as TasksApi, d as SpaceClient, f as SpacesApi, g as SessionGenerationStreamClient, h as buildSpacePath, i as WorksApi, j as ChannelsApi, k as GenerationsApi, l as BoardClient, m as buildSpaceInvitePath, n as createHttpClient, o as UsersApi, p as PublicInviteApi, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as BoardTransactionError, v as parseAssistantMessageCommit, w as SearchApi, x as ensureRealtimeConnected, y as SessionPatchReducer } from "./chunks/http.js";
2
- import { a as COHUB_SOURCE_HEADER, c as hasRequestSourceIdentity, d as mergeRequestSourceIntoMeta, f as normalizeRequestSource, g as resolveRequestSourceChannel, h as requestSourceToHeaders, i as sanitizeAccessToken, l as isRequestSourceEmpty, m as readRequestSourceFromEnv, n as HttpTransport, o as COHUB_SOURCE_HEADER_NAMES, p as parseRequestSourceFromHeaders, r as joinApiUrl, s as REQUEST_SOURCE_VIA_MAX_LENGTH, t as HttpError, u as isRequestSourceUuid } from "./chunks/transport.js";
2
+ import { _ as REALTIME_ROOM_EVENT_NAME_PATTERN, a as COHUB_SOURCE_HEADER, c as hasRequestSourceIdentity, d as mergeRequestSourceIntoMeta, f as normalizeRequestSource, g as resolveRequestSourceChannel, h as requestSourceToHeaders, i as sanitizeAccessToken, l as isRequestSourceEmpty, m as readRequestSourceFromEnv, n as HttpTransport, o as COHUB_SOURCE_HEADER_NAMES, p as parseRequestSourceFromHeaders, r as joinApiUrl, s as REQUEST_SOURCE_VIA_MAX_LENGTH, t as HttpError, u as isRequestSourceUuid } from "./chunks/transport.js";
3
3
  import { a as resolveApiBaseUrl, c as resolveWebsocketUrl, i as normalizeWebsocketUrl, n as normalizeBaseUrl, o as resolveCohubEnvironment, r as normalizeVoiceInputWebsocketUrl, s as resolveVoiceInputWebsocketUrl, t as COHUB_ENVIRONMENTS } from "./chunks/environment.js";
4
4
  import { a as extractBillingPayload, c as isFeatureNotEntitledError, i as FEATURE_NOT_ENTITLED_ERROR_CODE, l as isHttpErrorCode, n as createWebsocketClient, o as isBillingAccessBlockedCode, r as BILLING_ACCESS_BLOCKED_ERROR_CODE, s as isBillingAccessBlockedError, t as WebsocketClient } from "./chunks/websocket.js";
5
5
  import { VoiceApi, VoiceInputClient, createVoiceInputClient } from "./voice-input.js";
@@ -81,6 +81,779 @@ var ExploreApi = class {
81
81
  }
82
82
  };
83
83
  //#endregion
84
+ //#region ../protocol/dist/board-constants.js
85
+ const DEFAULT_BOARD_RENDER_LIMITS = {
86
+ particles: 2e4,
87
+ vertices: 5e5,
88
+ dynamicVertices: 15e4,
89
+ drawCalls: 400,
90
+ filterPasses: 24,
91
+ renderTexturePixels: 16777216,
92
+ textureBytes: 512 * 1024 * 1024,
93
+ bufferBytes: 256 * 1024 * 1024,
94
+ simulationSteps: 1e5
95
+ };
96
+ const BOARD_BUILTIN_CLIP_KINDS = [
97
+ "motion.keyframes",
98
+ "motion.path",
99
+ "draw.reveal",
100
+ "draw.handwrite",
101
+ "text.reveal",
102
+ "effects.particles",
103
+ "effects.trail",
104
+ "effects.impact",
105
+ "effects.flash",
106
+ "effects.color",
107
+ "camera.pan",
108
+ "camera.zoom",
109
+ "camera.shake"
110
+ ];
111
+ const BOARD_BUILTIN_EFFECT_KINDS = ["effects.pulse", "effects.float"];
112
+ const BOARD_ARROW_STROKE_SIZE = 2.5;
113
+ const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => ({
114
+ kind: "clip",
115
+ id,
116
+ version: 1,
117
+ renderers: ["webgpu", "webgl"]
118
+ })), ...BOARD_BUILTIN_EFFECT_KINDS.map((id) => ({
119
+ kind: "effect",
120
+ id,
121
+ version: 1,
122
+ renderers: ["webgpu", "webgl"]
123
+ }))];
124
+ //#endregion
125
+ //#region ../protocol/dist/realtime/board-awareness.js
126
+ const idSchema$1 = z.string().min(1).max(160);
127
+ const finiteSchema$1 = z.number().finite();
128
+ const BoardAwarenessPointSchema = z.object({
129
+ x: finiteSchema$1,
130
+ y: finiteSchema$1
131
+ });
132
+ const BoardAwarenessDrawPointSchema = BoardAwarenessPointSchema.extend({ p: finiteSchema$1.min(0).max(1) });
133
+ const BoardAwarenessFrameSchema = z.object({
134
+ x: finiteSchema$1,
135
+ y: finiteSchema$1,
136
+ width: finiteSchema$1.positive(),
137
+ height: finiteSchema$1.positive(),
138
+ rotation: finiteSchema$1
139
+ });
140
+ const arrowEndpointSchema = z.union([z.object({
141
+ kind: z.literal("point"),
142
+ x: finiteSchema$1,
143
+ y: finiteSchema$1
144
+ }), z.object({
145
+ kind: z.literal("binding"),
146
+ target: idSchema$1,
147
+ nx: finiteSchema$1,
148
+ ny: finiteSchema$1,
149
+ precise: z.boolean()
150
+ })]);
151
+ const BoardAwarenessNodePreviewSchema = z.object({
152
+ nodeId: idSchema$1,
153
+ frame: BoardAwarenessFrameSchema,
154
+ arrow: z.object({
155
+ start: arrowEndpointSchema,
156
+ end: arrowEndpointSchema,
157
+ bend: finiteSchema$1
158
+ }).optional()
159
+ });
160
+ const BoardAwarenessStateUpdateSchema = z.object({
161
+ type: z.literal("state"),
162
+ client: z.object({ formFactor: z.enum(["desktop", "mobile"]) }).optional(),
163
+ cursor: BoardAwarenessPointSchema.extend({ pointerType: z.enum([
164
+ "mouse",
165
+ "pen",
166
+ "touch"
167
+ ]) }).nullable(),
168
+ tool: z.string().min(1).max(40),
169
+ selection: z.object({
170
+ ids: z.array(idSchema$1).max(64),
171
+ count: z.number().int().nonnegative().max(5e4),
172
+ bounds: BoardAwarenessFrameSchema.nullable()
173
+ }),
174
+ editingId: idSchema$1.nullable()
175
+ });
176
+ const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
177
+ z.object({
178
+ kind: z.literal("draw"),
179
+ id: idSchema$1,
180
+ nodeId: idSchema$1,
181
+ color: z.string().min(1).max(64),
182
+ size: finiteSchema$1.positive().max(256),
183
+ from: z.number().int().nonnegative().max(1e5),
184
+ points: z.array(BoardAwarenessDrawPointSchema).min(1).max(64)
185
+ }),
186
+ z.object({
187
+ kind: z.literal("arrow"),
188
+ id: idSchema$1,
189
+ nodeId: idSchema$1,
190
+ start: BoardAwarenessPointSchema,
191
+ current: BoardAwarenessPointSchema,
192
+ color: z.string().min(1).max(64),
193
+ size: finiteSchema$1.positive().max(256).default(BOARD_ARROW_STROKE_SIZE)
194
+ }),
195
+ z.object({
196
+ kind: z.literal("box"),
197
+ id: idSchema$1,
198
+ nodeId: idSchema$1,
199
+ shape: z.enum(["geo", "frame"]),
200
+ start: BoardAwarenessPointSchema,
201
+ current: BoardAwarenessPointSchema,
202
+ color: z.string().min(1).max(64),
203
+ geo: z.string().min(1).max(40)
204
+ }),
205
+ z.object({
206
+ kind: z.literal("transform"),
207
+ id: idSchema$1,
208
+ mode: z.enum([
209
+ "translate",
210
+ "resize",
211
+ "rotate",
212
+ "arrow"
213
+ ]),
214
+ nodes: z.array(BoardAwarenessNodePreviewSchema).max(64),
215
+ bounds: BoardAwarenessFrameSchema.nullable()
216
+ })
217
+ ]);
218
+ const BoardAwarenessUpdateSchema = z.discriminatedUnion("type", [
219
+ BoardAwarenessStateUpdateSchema,
220
+ z.object({
221
+ type: z.literal("gesture"),
222
+ gesture: BoardAwarenessGestureSchema
223
+ }),
224
+ z.object({
225
+ type: z.literal("gesture.end"),
226
+ gestureId: idSchema$1,
227
+ resultingNodeIds: z.array(idSchema$1).max(64)
228
+ }),
229
+ z.object({
230
+ type: z.literal("gesture.cancel"),
231
+ gestureId: idSchema$1
232
+ })
233
+ ]);
234
+ const BoardAwarenessClientPayloadSchema = z.object({
235
+ spaceId: z.string().uuid(),
236
+ boardId: z.string().uuid(),
237
+ seq: z.number().int().nonnegative(),
238
+ update: BoardAwarenessUpdateSchema
239
+ });
240
+ //#endregion
241
+ //#region ../protocol/dist/realtime/schema.js
242
+ const contentBlockMetaSchema = z.record(z.string(), z.unknown());
243
+ const realtimeRoomSchema = z.string().regex(/^(space|user|board|room):[^:]+$/);
244
+ const contentBlockSchema = z.discriminatedUnion("type", [
245
+ z.object({
246
+ type: z.literal("text"),
247
+ text: z.string(),
248
+ _meta: contentBlockMetaSchema.optional()
249
+ }),
250
+ z.object({
251
+ type: z.literal("thinking"),
252
+ thinking: z.string(),
253
+ signature: z.string().optional(),
254
+ _meta: contentBlockMetaSchema.optional()
255
+ }),
256
+ z.object({
257
+ type: z.literal("image"),
258
+ source: z.union([z.object({
259
+ type: z.literal("url"),
260
+ url: z.string().url()
261
+ }), z.object({
262
+ type: z.literal("base64"),
263
+ media_type: z.string(),
264
+ data: z.string()
265
+ })]),
266
+ _meta: contentBlockMetaSchema.optional()
267
+ }),
268
+ z.object({
269
+ type: z.literal("shell_command"),
270
+ command: z.string(),
271
+ rawText: z.string(),
272
+ _meta: contentBlockMetaSchema.optional()
273
+ }),
274
+ z.object({
275
+ type: z.literal("tool_use"),
276
+ id: z.string(),
277
+ name: z.string(),
278
+ input: z.record(z.string(), z.unknown()),
279
+ _meta: contentBlockMetaSchema.optional()
280
+ }),
281
+ z.object({
282
+ type: z.literal("tool_result"),
283
+ tool_use_id: z.string(),
284
+ content: z.union([z.string(), z.array(z.unknown())]),
285
+ is_error: z.boolean().optional(),
286
+ _meta: contentBlockMetaSchema.optional()
287
+ }),
288
+ z.object({
289
+ type: z.literal("system_note"),
290
+ note_type: z.enum([
291
+ "session_created",
292
+ "forked",
293
+ "compacted",
294
+ "info"
295
+ ]),
296
+ text: z.string(),
297
+ _meta: contentBlockMetaSchema.optional()
298
+ })
299
+ ]);
300
+ z.discriminatedUnion("type", [
301
+ z.object({
302
+ type: z.literal("auth"),
303
+ requestId: z.string().optional(),
304
+ payload: z.object({
305
+ token: z.string().min(1),
306
+ capabilities: z.array(z.string().min(1)).optional()
307
+ })
308
+ }),
309
+ z.object({
310
+ type: z.literal("subscribe"),
311
+ requestId: z.string().optional(),
312
+ payload: z.object({ rooms: z.array(realtimeRoomSchema).min(1) })
313
+ }),
314
+ z.object({
315
+ type: z.literal("unsubscribe"),
316
+ requestId: z.string().optional(),
317
+ payload: z.object({ rooms: z.array(realtimeRoomSchema).min(1) })
318
+ }),
319
+ z.object({
320
+ type: z.literal("session.message.create"),
321
+ requestId: z.string().optional(),
322
+ payload: z.object({
323
+ spaceId: z.string().uuid(),
324
+ sessionId: z.string().uuid(),
325
+ clientMessageId: z.string().optional(),
326
+ content: z.array(contentBlockSchema).min(1),
327
+ model: z.string().optional(),
328
+ provider: z.string().optional(),
329
+ thinkingLevel: z.enum([
330
+ "off",
331
+ "minimal",
332
+ "low",
333
+ "medium",
334
+ "high",
335
+ "xhigh",
336
+ "max"
337
+ ]).optional()
338
+ })
339
+ }),
340
+ z.object({
341
+ type: z.literal("presence.update"),
342
+ requestId: z.string().optional(),
343
+ payload: z.object({
344
+ spaceId: z.string().uuid(),
345
+ meta: z.record(z.string(), z.unknown()).nullable().optional()
346
+ })
347
+ }),
348
+ z.object({
349
+ type: z.literal("board.awareness.update"),
350
+ requestId: z.string().optional(),
351
+ payload: BoardAwarenessClientPayloadSchema
352
+ }),
353
+ z.object({
354
+ type: z.literal("realtime.room.join"),
355
+ requestId: z.string().optional(),
356
+ payload: z.object({
357
+ roomId: z.string().uuid(),
358
+ ticket: z.string().min(1)
359
+ })
360
+ }),
361
+ z.object({
362
+ type: z.literal("realtime.room.publish"),
363
+ requestId: z.string().optional(),
364
+ payload: z.object({
365
+ roomId: z.string().uuid(),
366
+ event: z.string().regex(REALTIME_ROOM_EVENT_NAME_PATTERN),
367
+ data: z.unknown(),
368
+ clientEventId: z.string().max(128).optional()
369
+ })
370
+ }),
371
+ z.object({
372
+ type: z.literal("realtime.room.leave"),
373
+ requestId: z.string().optional(),
374
+ payload: z.object({ roomId: z.string().uuid() })
375
+ }),
376
+ z.object({
377
+ type: z.literal("realtime.room.presence.update"),
378
+ requestId: z.string().optional(),
379
+ payload: z.object({
380
+ roomId: z.string().uuid(),
381
+ presence: z.record(z.string(), z.unknown()).nullable()
382
+ })
383
+ }),
384
+ z.object({
385
+ type: z.literal("ping"),
386
+ requestId: z.string().optional(),
387
+ payload: z.record(z.string(), z.unknown()).optional()
388
+ }),
389
+ z.object({
390
+ type: z.literal("ack"),
391
+ requestId: z.string().optional(),
392
+ payload: z.object({ eventId: z.string().optional() }).optional()
393
+ })
394
+ ]);
395
+ z.object({
396
+ id: z.string(),
397
+ timestamp: z.number(),
398
+ domain: z.enum([
399
+ "system",
400
+ "session",
401
+ "space",
402
+ "label",
403
+ "room"
404
+ ]),
405
+ type: z.string(),
406
+ requestId: z.string().nullable().optional(),
407
+ spaceId: z.string().nullable().optional(),
408
+ sessionId: z.string().nullable().optional(),
409
+ rooms: z.array(realtimeRoomSchema).optional(),
410
+ payload: z.record(z.string(), z.unknown())
411
+ });
412
+ z.discriminatedUnion("t", [z.object({
413
+ t: z.literal("d"),
414
+ sid: z.string().min(1),
415
+ s: z.number().int().nonnegative(),
416
+ b: z.number().int().nonnegative(),
417
+ v: z.unknown()
418
+ }), z.object({
419
+ t: z.literal("p"),
420
+ sid: z.string().min(1),
421
+ s: z.number().int().nonnegative(),
422
+ b: z.number().int().nonnegative(),
423
+ o: z.enum([
424
+ "append",
425
+ "replace",
426
+ "add",
427
+ "merge",
428
+ "remove"
429
+ ]),
430
+ p: z.string().min(1),
431
+ v: z.unknown().optional()
432
+ })]);
433
+ //#endregion
434
+ //#region src/apis/work-realtime.ts
435
+ const randomId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
436
+ const textEncoder = new TextEncoder();
437
+ const byteLength = (text) => textEncoder.encode(text).byteLength;
438
+ /**
439
+ * Ceiling on deltas held during a join, so a stalled handshake cannot buffer without
440
+ * limit. Dropping the tail needs no extra signal: the gap detection below reports it.
441
+ */
442
+ const WORK_ROOM_MAX_JOIN_BUFFER = 512;
443
+ /** Room events that mutate state, so they must not be applied before the join snapshot. */
444
+ const WORK_ROOM_DELTA_EVENTS = /* @__PURE__ */ new Set([
445
+ "realtime.room.event",
446
+ "realtime.room.member.joined",
447
+ "realtime.room.member.left",
448
+ "realtime.room.presence.updated"
449
+ ]);
450
+ const requestError = (event) => {
451
+ const payload = event.payload;
452
+ return new Error(typeof payload.message === "string" ? payload.message : typeof payload.code === "string" ? payload.code : "room request failed");
453
+ };
454
+ const isRoomEvent = (event, roomId) => {
455
+ if (event.domain !== "room") return false;
456
+ return event.payload.roomId === roomId || event.rooms?.includes(`room:${roomId}`);
457
+ };
458
+ var WorkRoom = class {
459
+ websocket;
460
+ admission;
461
+ id;
462
+ code;
463
+ createdAt;
464
+ expiresAt;
465
+ maxParticipants;
466
+ seatPerUser;
467
+ /** Opaque, stable identity of this viewer inside the room. */
468
+ userKey;
469
+ _participantId;
470
+ _state = "connecting";
471
+ _members = [];
472
+ joinBuffer = null;
473
+ lastSequence = 0;
474
+ hasJoined = false;
475
+ explicitLeave = false;
476
+ expiryTimer = null;
477
+ pending = /* @__PURE__ */ new Map();
478
+ handlers = /* @__PURE__ */ new Map();
479
+ allHandlers = /* @__PURE__ */ new Set();
480
+ stateHandlers = /* @__PURE__ */ new Set();
481
+ membersHandlers = /* @__PURE__ */ new Set();
482
+ outOfSyncHandlers = /* @__PURE__ */ new Set();
483
+ sendErrorHandlers = /* @__PURE__ */ new Set();
484
+ offEvent;
485
+ offOpen;
486
+ offClose;
487
+ offReconnecting;
488
+ constructor(websocket, admission) {
489
+ this.websocket = websocket;
490
+ this.admission = admission;
491
+ this.id = admission.room.id;
492
+ this.code = admission.room.code;
493
+ this.createdAt = admission.room.createdAt;
494
+ this.expiresAt = admission.room.expiresAt;
495
+ this.maxParticipants = admission.room.maxParticipants;
496
+ this.seatPerUser = admission.room.seatPerUser === true;
497
+ this.userKey = admission.userKey;
498
+ this._participantId = admission.participantId;
499
+ this.offEvent = websocket.on("event", (event) => this.handleEvent(event));
500
+ this.offOpen = websocket.on("open", () => {
501
+ if (!this.hasJoined || this.explicitLeave || this._state === "expired" || this._state === "closed") return;
502
+ this.rejoin();
503
+ });
504
+ this.offReconnecting = websocket.on("reconnecting", () => {
505
+ if (this.hasJoined && !this.explicitLeave) this.setState("reconnecting");
506
+ });
507
+ this.offClose = websocket.on("close", ({ willReconnect }) => {
508
+ this.rejectPending(/* @__PURE__ */ new Error("room connection closed"));
509
+ if (this.explicitLeave || !willReconnect) {
510
+ if (this._state !== "expired") this.setState("closed");
511
+ this.hasJoined = false;
512
+ this.dispose();
513
+ } else if (this.hasJoined) this.setState("reconnecting");
514
+ });
515
+ const delay = Math.max(0, Date.parse(this.expiresAt) - Date.now());
516
+ this.expiryTimer = setTimeout(() => this.expire(), Math.min(delay, 2147e6));
517
+ }
518
+ get state() {
519
+ return this._state;
520
+ }
521
+ /**
522
+ * Identity of this connection inside the room. A `seatPerUser` join can take over a
523
+ * seat the viewer already holds, so this may differ from the id issued at admission.
524
+ */
525
+ get participantId() {
526
+ return this._participantId;
527
+ }
528
+ get members() {
529
+ return this._members.slice();
530
+ }
531
+ async connect() {
532
+ await this.joinOverWebsocket(false);
533
+ return this;
534
+ }
535
+ subscribe(type, handler) {
536
+ const handlers = this.handlers.get(type) ?? /* @__PURE__ */ new Set();
537
+ handlers.add(handler);
538
+ this.handlers.set(type, handlers);
539
+ return () => handlers.delete(handler);
540
+ }
541
+ subscribeAll(handler) {
542
+ this.allHandlers.add(handler);
543
+ return () => this.allHandlers.delete(handler);
544
+ }
545
+ onStateChange(handler) {
546
+ this.stateHandlers.add(handler);
547
+ return () => this.stateHandlers.delete(handler);
548
+ }
549
+ onMembersChanged(handler) {
550
+ this.membersHandlers.add(handler);
551
+ return () => this.membersHandlers.delete(handler);
552
+ }
553
+ onOutOfSync(handler) {
554
+ this.outOfSyncHandlers.add(handler);
555
+ return () => this.outOfSyncHandlers.delete(handler);
556
+ }
557
+ /** Reports failures of {@link send}, which has no ack to reject. */
558
+ onSendError(handler) {
559
+ this.sendErrorHandlers.add(handler);
560
+ return () => this.sendErrorHandlers.delete(handler);
561
+ }
562
+ /**
563
+ * Publishes without waiting for the server ack, for high-frequency traffic such as
564
+ * input frames. Awaiting {@link publish} instead caps a loop at `1000 / rtt` events
565
+ * per second. Ordering still holds, but failures surface through
566
+ * {@link onSendError} rather than a rejected promise, and calls are ignored while
567
+ * the room is not joined; watch {@link onStateChange} to know when it resumes.
568
+ */
569
+ send(type, data) {
570
+ if (this._state !== "joined" || !this.hasJoined) return;
571
+ const failure = this.validateSend(type, data);
572
+ if (failure) {
573
+ for (const handler of this.sendErrorHandlers) handler(failure);
574
+ return;
575
+ }
576
+ this.websocket.publishRealtimeRoom({
577
+ roomId: this.id,
578
+ event: type,
579
+ data
580
+ }).catch((error) => {
581
+ const reason = error instanceof Error ? error : new Error(String(error));
582
+ for (const handler of this.sendErrorHandlers) handler(reason);
583
+ });
584
+ }
585
+ validateSend(type, data) {
586
+ if (!REALTIME_ROOM_EVENT_NAME_PATTERN.test(type)) return /* @__PURE__ */ new Error(`invalid room event name: ${type}`);
587
+ let encoded;
588
+ try {
589
+ encoded = JSON.stringify(data);
590
+ } catch {
591
+ return /* @__PURE__ */ new Error("room event data is not serializable");
592
+ }
593
+ if (encoded === void 0) return /* @__PURE__ */ new Error("room event data is not serializable");
594
+ if (byteLength(encoded) > 16384) return /* @__PURE__ */ new Error("room event payload is too large");
595
+ return null;
596
+ }
597
+ async publish(type, data, options) {
598
+ this.assertJoined();
599
+ const failure = this.validateSend(type, data);
600
+ if (failure) throw failure;
601
+ const requestId = randomId();
602
+ const response = await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.publishRealtimeRoom({
603
+ roomId: this.id,
604
+ event: type,
605
+ data,
606
+ clientEventId: options?.clientEventId,
607
+ requestId
608
+ }));
609
+ const payload = response.payload;
610
+ return {
611
+ eventId: typeof payload.eventId === "string" ? payload.eventId : response.id,
612
+ sequence: typeof payload.sequence === "number" ? payload.sequence : 0,
613
+ clientEventId: typeof payload.clientEventId === "string" ? payload.clientEventId : null
614
+ };
615
+ }
616
+ async setPresence(presence) {
617
+ this.assertJoined();
618
+ const requestId = randomId();
619
+ await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.updateRealtimeRoomPresence({
620
+ roomId: this.id,
621
+ presence,
622
+ requestId
623
+ }));
624
+ }
625
+ async leave() {
626
+ if (this.explicitLeave || this._state === "closed" || this._state === "expired") return;
627
+ this.explicitLeave = true;
628
+ try {
629
+ if (this.hasJoined && this.websocket.state === "open") {
630
+ const requestId = randomId();
631
+ await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.leaveRealtimeRoom({
632
+ roomId: this.id,
633
+ requestId
634
+ }));
635
+ }
636
+ } finally {
637
+ this.hasJoined = false;
638
+ this.setState("closed");
639
+ this.dispose();
640
+ }
641
+ }
642
+ async rejoin() {
643
+ if (this.explicitLeave || this._state === "expired" || this._state === "closed") return;
644
+ try {
645
+ await this.joinOverWebsocket(true);
646
+ } catch (error) {
647
+ if (this.state === "expired") return;
648
+ this.setState("closed");
649
+ this.dispose();
650
+ console.warn("[Cohub WorkRoom] failed to rejoin room", error);
651
+ }
652
+ }
653
+ async joinOverWebsocket(isReconnect) {
654
+ if (!this.websocket.supportsCapability("realtime.room.v1") && this.websocket.state === "open") throw new Error("Realtime rooms are not supported by the Gateway");
655
+ this.setState(isReconnect ? "reconnecting" : "connecting");
656
+ const requestId = randomId();
657
+ this.joinBuffer = [];
658
+ try {
659
+ const payload = (await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.joined"]), () => this.websocket.joinRealtimeRoom({
660
+ roomId: this.id,
661
+ ticket: this.admission.ticket,
662
+ requestId
663
+ }))).payload;
664
+ if (typeof payload.participantId !== "string" || !Array.isArray(payload.members)) throw new Error("invalid room join response");
665
+ this._participantId = payload.participantId;
666
+ this._members = payload.members;
667
+ this.lastSequence = typeof payload.sequence === "number" ? payload.sequence : 0;
668
+ } catch (error) {
669
+ this.joinBuffer = null;
670
+ throw error;
671
+ }
672
+ this.hasJoined = true;
673
+ this.setState("joined");
674
+ this.notifyMembers();
675
+ const buffered = this.joinBuffer ?? [];
676
+ this.joinBuffer = null;
677
+ for (const item of buffered) {
678
+ const sequence = item.payload.sequence;
679
+ if (typeof sequence === "number" && sequence <= this.lastSequence) continue;
680
+ this.handleEvent(item);
681
+ }
682
+ }
683
+ request(requestId, expected, send) {
684
+ return new Promise((resolve, reject) => {
685
+ const timer = setTimeout(() => {
686
+ this.pending.delete(requestId);
687
+ reject(/* @__PURE__ */ new Error("room request timed out"));
688
+ }, 2e4);
689
+ this.pending.set(requestId, {
690
+ expected,
691
+ resolve,
692
+ reject,
693
+ timer
694
+ });
695
+ send().catch((error) => {
696
+ this.pending.delete(requestId);
697
+ clearTimeout(timer);
698
+ reject(error instanceof Error ? error : new Error(String(error)));
699
+ });
700
+ });
701
+ }
702
+ /** Detaches the entry awaiting this requestId, so the caller can settle it. */
703
+ takePending(requestId) {
704
+ const pending = requestId ? this.pending.get(requestId) : void 0;
705
+ if (!pending || !requestId) return null;
706
+ this.pending.delete(requestId);
707
+ clearTimeout(pending.timer);
708
+ return pending;
709
+ }
710
+ handleEvent(event) {
711
+ if (!isRoomEvent(event, this.id)) {
712
+ if (event.type === "system.request.error") this.takePending(event.requestId)?.reject(requestError(event));
713
+ return;
714
+ }
715
+ if (event.type === "realtime.room.request.error") {
716
+ const failure = requestError(event);
717
+ if (event.requestId) {
718
+ this.takePending(event.requestId)?.reject(failure);
719
+ return;
720
+ }
721
+ for (const handler of this.sendErrorHandlers) handler(failure);
722
+ return;
723
+ }
724
+ const awaiting = event.requestId ? this.pending.get(event.requestId) : void 0;
725
+ if (awaiting?.expected.has(event.type)) {
726
+ this.takePending(event.requestId);
727
+ awaiting.resolve(event);
728
+ }
729
+ if (this.joinBuffer && WORK_ROOM_DELTA_EVENTS.has(event.type)) {
730
+ if (this.joinBuffer.length < WORK_ROOM_MAX_JOIN_BUFFER) this.joinBuffer.push(event);
731
+ return;
732
+ }
733
+ const payload = event.payload;
734
+ const sequence = typeof payload.sequence === "number" ? payload.sequence : null;
735
+ if (sequence !== null) this.observeSequence(sequence);
736
+ if (event.type === "realtime.room.member.joined" || event.type === "realtime.room.member.left" || event.type === "realtime.room.presence.updated") {
737
+ const member = payload.member;
738
+ if (member?.participantId) {
739
+ const members = new Map(this._members.map((item) => [item.participantId, item]));
740
+ if (event.type === "realtime.room.member.left") members.delete(member.participantId);
741
+ else members.set(member.participantId, member);
742
+ this._members = [...members.values()];
743
+ this.notifyMembers();
744
+ }
745
+ return;
746
+ }
747
+ if (event.type === "realtime.room.closed") {
748
+ this.hasJoined = false;
749
+ if (payload.reason === "expired") this.setState("expired");
750
+ else this.setState("closed");
751
+ this.rejectPending(/* @__PURE__ */ new Error(`room closed: ${typeof payload.reason === "string" ? payload.reason : "unknown"}`));
752
+ this.dispose();
753
+ return;
754
+ }
755
+ if (event.type !== "realtime.room.event") return;
756
+ const roomEvent = event;
757
+ const roomPayload = roomEvent.payload;
758
+ const item = {
759
+ id: roomEvent.id,
760
+ timestamp: roomEvent.timestamp,
761
+ roomId: roomPayload.roomId,
762
+ sequence: roomPayload.sequence,
763
+ type: roomPayload.event,
764
+ data: roomPayload.data,
765
+ clientEventId: roomPayload.clientEventId,
766
+ sender: roomPayload.sender,
767
+ self: roomPayload.sender.participantId === this.participantId
768
+ };
769
+ this.handlers.get(item.type)?.forEach((handler) => {
770
+ handler(item);
771
+ });
772
+ this.allHandlers.forEach((handler) => {
773
+ handler(item);
774
+ });
775
+ }
776
+ observeSequence(sequence) {
777
+ if (this.lastSequence > 0 && sequence > this.lastSequence + 1) for (const handler of this.outOfSyncHandlers) handler(this.lastSequence + 1, sequence);
778
+ if (sequence > this.lastSequence) this.lastSequence = sequence;
779
+ }
780
+ assertJoined() {
781
+ if (this._state !== "joined" || !this.hasJoined) throw new Error("room is not joined");
782
+ }
783
+ setState(state) {
784
+ if (this._state === state) return;
785
+ this._state = state;
786
+ for (const handler of this.stateHandlers) handler(state);
787
+ }
788
+ notifyMembers() {
789
+ const members = this.members;
790
+ for (const handler of this.membersHandlers) handler(members);
791
+ }
792
+ rejectPending(error) {
793
+ for (const [requestId, pending] of this.pending) {
794
+ this.pending.delete(requestId);
795
+ clearTimeout(pending.timer);
796
+ pending.reject(error);
797
+ }
798
+ }
799
+ expire() {
800
+ if (this.explicitLeave || this._state === "closed" || this._state === "expired") return;
801
+ this.hasJoined = false;
802
+ this.setState("expired");
803
+ this.rejectPending(/* @__PURE__ */ new Error("room expired"));
804
+ this.dispose();
805
+ }
806
+ dispose() {
807
+ this.offEvent();
808
+ this.offOpen();
809
+ this.offClose();
810
+ this.offReconnecting();
811
+ if (this.expiryTimer) clearTimeout(this.expiryTimer);
812
+ this.expiryTimer = null;
813
+ }
814
+ };
815
+ var WorkRealtimeApi = class {
816
+ transport;
817
+ websocket;
818
+ getContext;
819
+ constructor(transport, websocket, getContext) {
820
+ this.transport = transport;
821
+ this.websocket = websocket;
822
+ this.getContext = getContext;
823
+ }
824
+ async createRoom(input = {}) {
825
+ const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms`, {
826
+ method: "POST",
827
+ headers: { "Content-Type": "application/json" },
828
+ body: JSON.stringify(input)
829
+ });
830
+ return this.openRoom(admission);
831
+ }
832
+ async joinRoom(input) {
833
+ const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms/join`, {
834
+ method: "POST",
835
+ headers: { "Content-Type": "application/json" },
836
+ body: JSON.stringify(input)
837
+ });
838
+ return this.openRoom(admission);
839
+ }
840
+ async requireWorkId() {
841
+ const context = await this.getContext();
842
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
843
+ return context.work.id;
844
+ }
845
+ async openRoom(admission) {
846
+ const room = new WorkRoom(this.websocket, admission);
847
+ try {
848
+ await room.connect();
849
+ return room;
850
+ } catch (error) {
851
+ await room.leave().catch(() => void 0);
852
+ throw error;
853
+ }
854
+ }
855
+ };
856
+ //#endregion
84
857
  //#region src/work-runtime.ts
85
858
  const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
86
859
  const hasParent = () => isBrowser$1() && window.parent !== window;
@@ -94,7 +867,7 @@ const getParentOrigin = () => {
94
867
  return null;
95
868
  }
96
869
  };
97
- const generateRequestId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
870
+ const generateRequestId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
98
871
  /**
99
872
  * Bridge-mode transport: posts messages to `window.parent` (the Cohub host
100
873
  * embedding the work in an iframe) and listens for the matching reply.
@@ -400,9 +1173,11 @@ var WorkRuntimeApi = class {
400
1173
  return Boolean(this.token);
401
1174
  }
402
1175
  async purchase(input) {
1176
+ const purchaseAttemptId = input.purchaseAttemptId?.trim() || generateRequestId();
403
1177
  return (await this.transport.request({
404
1178
  type: "cohub.work.purchase",
405
- productKey: input.productKey
1179
+ productKey: input.productKey,
1180
+ purchaseAttemptId
406
1181
  }, { timeoutMs: 12e4 }))?.checkout ?? null;
407
1182
  }
408
1183
  async checkoutState() {
@@ -538,41 +1313,45 @@ var CohubClient = class {
538
1313
  this.referrals = new ReferralsApi(this.transport);
539
1314
  this.works = new WorksApi(this.transport);
540
1315
  this.workCommerce = new WorkCommerceApi(this.transport);
1316
+ this.work.realtime = new WorkRealtimeApi(this.transport, this.websocketClient, () => this.workRuntime.context());
541
1317
  }
542
1318
  context() {
543
1319
  return this.workRuntime.context();
544
1320
  }
545
1321
  auth = { request: (input) => this.workRuntime.requestAuthorization(input) };
546
- work = { commerce: {
547
- resolveProducts: async (input) => {
548
- const context = await this.workRuntime.context();
549
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
550
- return this.workCommerce.resolveProducts(context.work.id, input);
551
- },
552
- getEntitlements: async () => {
553
- const context = await this.workRuntime.context();
554
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
555
- return this.workCommerce.getEntitlements(context.work.id);
556
- },
557
- consumeCredits: async (input) => {
558
- const context = await this.workRuntime.context();
559
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
560
- return this.workCommerce.consumeCredits(context.work.id, input);
561
- },
562
- purchase: async (input) => this.workRuntime.purchase(input),
563
- getCheckoutState: async () => {
564
- const result = await this.workRuntime.checkoutState();
565
- return {
566
- status: result?.status ?? null,
567
- orderId: result?.orderId ?? null
568
- };
569
- },
570
- getOrder: async (orderId) => {
571
- const context = await this.workRuntime.context();
572
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
573
- return this.workCommerce.getOrder(context.work.id, orderId);
1322
+ work = {
1323
+ realtime: null,
1324
+ commerce: {
1325
+ resolveProducts: async (input) => {
1326
+ const context = await this.workRuntime.context();
1327
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1328
+ return this.workCommerce.resolveProducts(context.work.id, input);
1329
+ },
1330
+ getEntitlements: async () => {
1331
+ const context = await this.workRuntime.context();
1332
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1333
+ return this.workCommerce.getEntitlements(context.work.id);
1334
+ },
1335
+ consumeCredits: async (input) => {
1336
+ const context = await this.workRuntime.context();
1337
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1338
+ return this.workCommerce.consumeCredits(context.work.id, input);
1339
+ },
1340
+ purchase: async (input) => this.workRuntime.purchase(input),
1341
+ getCheckoutState: async () => {
1342
+ const result = await this.workRuntime.checkoutState();
1343
+ return {
1344
+ status: result?.status ?? null,
1345
+ orderId: result?.orderId ?? null
1346
+ };
1347
+ },
1348
+ getOrder: async (orderId) => {
1349
+ const context = await this.workRuntime.context();
1350
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1351
+ return this.workCommerce.getOrder(context.work.id, orderId);
1352
+ }
574
1353
  }
575
- } };
1354
+ };
576
1355
  space(spaceId) {
577
1356
  return new SpaceClient(spaceId, this.transport, this.websocketClient);
578
1357
  }
@@ -830,7 +1609,7 @@ function createWorkBridgeCore(config) {
830
1609
  sessionStorage.removeItem(pendingPurchaseStorageKey);
831
1610
  } catch {}
832
1611
  }
833
- async function createPurchase(productKey) {
1612
+ async function createPurchase(productKey, purchaseAttemptId) {
834
1613
  const userToken = await getAccessToken();
835
1614
  if (!userToken) {
836
1615
  await config.requestSignIn(typeof location !== "undefined" ? location.pathname + location.search + location.hash : "/");
@@ -842,7 +1621,10 @@ function createWorkBridgeCore(config) {
842
1621
  Authorization: `Bearer ${userToken}`,
843
1622
  "Content-Type": "application/json"
844
1623
  },
845
- body: JSON.stringify({ productKey })
1624
+ body: JSON.stringify({
1625
+ productKey,
1626
+ purchaseAttemptId
1627
+ })
846
1628
  });
847
1629
  if (!response.ok) throw new Error((await response.json().catch(() => null))?.message ?? "Purchase failed.");
848
1630
  return (await response.json()).checkout ?? null;
@@ -897,9 +1679,18 @@ function createWorkBridgeCore(config) {
897
1679
  });
898
1680
  return;
899
1681
  }
1682
+ const purchaseAttemptId = (typeof data.purchaseAttemptId === "string" ? data.purchaseAttemptId.trim() : "") || data.requestId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
1683
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(purchaseAttemptId)) {
1684
+ reply(data.requestId, {
1685
+ type: "cohub.work.error",
1686
+ message: "Purchase attempt id is invalid."
1687
+ });
1688
+ return;
1689
+ }
900
1690
  state.pendingPurchase = {
901
1691
  requestId: data.requestId,
902
- productKey
1692
+ productKey,
1693
+ purchaseAttemptId
903
1694
  };
904
1695
  state.purchaseError = null;
905
1696
  state.purchaseOpen = true;
@@ -982,7 +1773,7 @@ function createWorkBridgeCore(config) {
982
1773
  state.purchaseError = null;
983
1774
  notify();
984
1775
  try {
985
- const checkout = await createPurchase(state.pendingPurchase.productKey);
1776
+ const checkout = await createPurchase(state.pendingPurchase.productKey, state.pendingPurchase.purchaseAttemptId);
986
1777
  reply(state.pendingPurchase.requestId, {
987
1778
  type: "cohub.work.purchase.result",
988
1779
  checkout
@@ -1276,53 +2067,12 @@ function filterGenerationDeclarationsByPolicy(declarations, policy) {
1276
2067
  });
1277
2068
  }
1278
2069
  //#endregion
1279
- //#region ../protocol/dist/board-constants.js
1280
- const DEFAULT_BOARD_RENDER_LIMITS = {
1281
- particles: 2e4,
1282
- vertices: 5e5,
1283
- dynamicVertices: 15e4,
1284
- drawCalls: 400,
1285
- filterPasses: 24,
1286
- renderTexturePixels: 16777216,
1287
- textureBytes: 512 * 1024 * 1024,
1288
- bufferBytes: 256 * 1024 * 1024,
1289
- simulationSteps: 1e5
1290
- };
1291
- const BOARD_BUILTIN_CLIP_KINDS = [
1292
- "motion.keyframes",
1293
- "motion.path",
1294
- "draw.reveal",
1295
- "draw.handwrite",
1296
- "text.reveal",
1297
- "effects.particles",
1298
- "effects.trail",
1299
- "effects.impact",
1300
- "effects.flash",
1301
- "effects.color",
1302
- "camera.pan",
1303
- "camera.zoom",
1304
- "camera.shake"
1305
- ];
1306
- const BOARD_BUILTIN_EFFECT_KINDS = ["effects.pulse", "effects.float"];
1307
- const BOARD_ARROW_STROKE_SIZE = 2.5;
1308
- const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => ({
1309
- kind: "clip",
1310
- id,
1311
- version: 1,
1312
- renderers: ["webgpu", "webgl"]
1313
- })), ...BOARD_BUILTIN_EFFECT_KINDS.map((id) => ({
1314
- kind: "effect",
1315
- id,
1316
- version: 1,
1317
- renderers: ["webgpu", "webgl"]
1318
- }))];
1319
- //#endregion
1320
2070
  //#region ../protocol/dist/board.js
1321
2071
  const BOARD_MANIFEST_KIND = "cohub.board.manifest";
1322
- const idSchema$1 = z.string().min(1).max(160);
2072
+ const idSchema = z.string().min(1).max(160);
1323
2073
  const extensionIdSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
1324
2074
  const jsonObjectSchema = z.record(z.string(), z.unknown());
1325
- const finiteSchema$1 = z.number().finite();
2075
+ const finiteSchema = z.number().finite();
1326
2076
  z.object({
1327
2077
  kind: z.literal(BOARD_MANIFEST_KIND),
1328
2078
  version: z.literal(1),
@@ -1332,11 +2082,11 @@ z.object({
1332
2082
  const BoardTargetSchema = z.discriminatedUnion("type", [
1333
2083
  z.object({
1334
2084
  type: z.literal("node"),
1335
- nodeId: idSchema$1
2085
+ nodeId: idSchema
1336
2086
  }),
1337
2087
  z.object({
1338
2088
  type: z.literal("effect"),
1339
- effectId: idSchema$1
2089
+ effectId: idSchema
1340
2090
  }),
1341
2091
  z.object({ type: z.literal("board") }),
1342
2092
  z.object({ type: z.literal("camera") })
@@ -1347,18 +2097,18 @@ const BoardAssetRefSchema = z.object({
1347
2097
  digest: z.string().min(16).max(160).optional()
1348
2098
  });
1349
2099
  const BoardKeyframeSchema = z.object({
1350
- at: finiteSchema$1.nonnegative(),
2100
+ at: finiteSchema.nonnegative(),
1351
2101
  value: z.unknown(),
1352
2102
  easing: z.string().min(1).max(80).optional()
1353
2103
  });
1354
2104
  const BoardClipSchema = z.object({
1355
- id: idSchema$1,
1356
- sequenceId: idSchema$1,
2105
+ id: idSchema,
2106
+ sequenceId: idSchema,
1357
2107
  kind: extensionIdSchema,
1358
2108
  kindVersion: z.number().int().positive(),
1359
2109
  target: BoardTargetSchema,
1360
- start: finiteSchema$1.nonnegative(),
1361
- duration: finiteSchema$1.positive(),
2110
+ start: finiteSchema.nonnegative(),
2111
+ duration: finiteSchema.positive(),
1362
2112
  layer: z.enum([
1363
2113
  "behind",
1364
2114
  "content",
@@ -1379,11 +2129,11 @@ const BoardClipSchema = z.object({
1379
2129
  metadata: jsonObjectSchema.default({})
1380
2130
  });
1381
2131
  const BoardEffectSchema = z.object({
1382
- id: idSchema$1,
2132
+ id: idSchema,
1383
2133
  boardId: z.string().uuid(),
1384
2134
  target: z.discriminatedUnion("type", [z.object({
1385
2135
  type: z.literal("node"),
1386
- nodeId: idSchema$1
2136
+ nodeId: idSchema
1387
2137
  }), z.object({ type: z.literal("board") })]),
1388
2138
  kind: extensionIdSchema,
1389
2139
  kindVersion: z.number().int().positive(),
@@ -1410,25 +2160,25 @@ const BoardEffectSchema = z.object({
1410
2160
  revision: z.number().int().nonnegative()
1411
2161
  });
1412
2162
  const BoardSequenceSchema = z.object({
1413
- id: idSchema$1,
2163
+ id: idSchema,
1414
2164
  boardId: z.string().uuid(),
1415
2165
  name: z.string().min(1).max(255),
1416
- duration: finiteSchema$1.nonnegative(),
2166
+ duration: finiteSchema.nonnegative(),
1417
2167
  seed: z.string().min(1).max(160),
1418
2168
  restPose: jsonObjectSchema.default({}),
1419
2169
  metadata: jsonObjectSchema.default({}),
1420
2170
  revision: z.number().int().nonnegative()
1421
2171
  });
1422
2172
  const BoardNodeInputSchema = z.object({
1423
- nodeId: idSchema$1,
2173
+ nodeId: idSchema,
1424
2174
  type: z.string().min(1).max(40),
1425
- parentId: idSchema$1.nullable(),
2175
+ parentId: idSchema.nullable(),
1426
2176
  orderKey: z.string().max(4096).nullable(),
1427
- x: finiteSchema$1,
1428
- y: finiteSchema$1,
1429
- width: finiteSchema$1.positive(),
1430
- height: finiteSchema$1.positive(),
1431
- rotation: finiteSchema$1,
2177
+ x: finiteSchema,
2178
+ y: finiteSchema,
2179
+ width: finiteSchema.positive(),
2180
+ height: finiteSchema.positive(),
2181
+ rotation: finiteSchema,
1432
2182
  refKind: z.string().max(40).nullable(),
1433
2183
  refPath: z.string().max(4096).nullable(),
1434
2184
  refUrl: z.string().max(4096).nullable(),
@@ -1464,17 +2214,17 @@ z.object({
1464
2214
  "playback"
1465
2215
  ])).optional(),
1466
2216
  viewport: z.object({
1467
- x: finiteSchema$1,
1468
- y: finiteSchema$1,
1469
- width: finiteSchema$1.positive(),
1470
- height: finiteSchema$1.positive()
2217
+ x: finiteSchema,
2218
+ y: finiteSchema,
2219
+ width: finiteSchema.positive(),
2220
+ height: finiteSchema.positive()
1471
2221
  }).optional()
1472
2222
  });
1473
2223
  /** Persisted on `boards.metadata.playback`: how a Board plays when opened. */
1474
2224
  const BoardPlaybackPolicySchema = z.object({
1475
- sequenceId: idSchema$1,
2225
+ sequenceId: idSchema,
1476
2226
  /** Delay before the first local playback after opening the Board, in milliseconds. */
1477
- delayMs: finiteSchema$1.nonnegative().default(0),
2227
+ delayMs: finiteSchema.nonnegative().default(0),
1478
2228
  loop: z.boolean().default(false)
1479
2229
  });
1480
2230
  function parseBoardPlaybackPolicy(metadata) {
@@ -1483,148 +2233,32 @@ function parseBoardPlaybackPolicy(metadata) {
1483
2233
  }
1484
2234
  z.discriminatedUnion("type", [
1485
2235
  z.object({
1486
- commandId: idSchema$1,
2236
+ commandId: idSchema,
1487
2237
  type: z.literal("play"),
1488
- sequenceId: idSchema$1,
1489
- position: finiteSchema$1.nonnegative().optional(),
1490
- timeScale: finiteSchema$1.positive().max(4).optional(),
2238
+ sequenceId: idSchema,
2239
+ position: finiteSchema.nonnegative().optional(),
2240
+ timeScale: finiteSchema.positive().max(4).optional(),
1491
2241
  shared: z.boolean().optional(),
1492
- seed: idSchema$1.optional()
2242
+ seed: idSchema.optional()
1493
2243
  }),
1494
2244
  z.object({
1495
- commandId: idSchema$1,
2245
+ commandId: idSchema,
1496
2246
  type: z.literal("pause"),
1497
2247
  playbackId: z.string().uuid()
1498
2248
  }),
1499
2249
  z.object({
1500
- commandId: idSchema$1,
2250
+ commandId: idSchema,
1501
2251
  type: z.literal("seek"),
1502
2252
  playbackId: z.string().uuid(),
1503
- position: finiteSchema$1.nonnegative()
2253
+ position: finiteSchema.nonnegative()
1504
2254
  }),
1505
2255
  z.object({
1506
- commandId: idSchema$1,
2256
+ commandId: idSchema,
1507
2257
  type: z.literal("stop"),
1508
2258
  playbackId: z.string().uuid()
1509
2259
  })
1510
2260
  ]);
1511
2261
  //#endregion
1512
- //#region ../protocol/dist/realtime/board-awareness.js
1513
- const idSchema = z.string().min(1).max(160);
1514
- const finiteSchema = z.number().finite();
1515
- const BoardAwarenessPointSchema = z.object({
1516
- x: finiteSchema,
1517
- y: finiteSchema
1518
- });
1519
- const BoardAwarenessDrawPointSchema = BoardAwarenessPointSchema.extend({ p: finiteSchema.min(0).max(1) });
1520
- const BoardAwarenessFrameSchema = z.object({
1521
- x: finiteSchema,
1522
- y: finiteSchema,
1523
- width: finiteSchema.positive(),
1524
- height: finiteSchema.positive(),
1525
- rotation: finiteSchema
1526
- });
1527
- const arrowEndpointSchema = z.union([z.object({
1528
- kind: z.literal("point"),
1529
- x: finiteSchema,
1530
- y: finiteSchema
1531
- }), z.object({
1532
- kind: z.literal("binding"),
1533
- target: idSchema,
1534
- nx: finiteSchema,
1535
- ny: finiteSchema,
1536
- precise: z.boolean()
1537
- })]);
1538
- const BoardAwarenessNodePreviewSchema = z.object({
1539
- nodeId: idSchema,
1540
- frame: BoardAwarenessFrameSchema,
1541
- arrow: z.object({
1542
- start: arrowEndpointSchema,
1543
- end: arrowEndpointSchema,
1544
- bend: finiteSchema
1545
- }).optional()
1546
- });
1547
- const BoardAwarenessStateUpdateSchema = z.object({
1548
- type: z.literal("state"),
1549
- client: z.object({ formFactor: z.enum(["desktop", "mobile"]) }).optional(),
1550
- cursor: BoardAwarenessPointSchema.extend({ pointerType: z.enum([
1551
- "mouse",
1552
- "pen",
1553
- "touch"
1554
- ]) }).nullable(),
1555
- tool: z.string().min(1).max(40),
1556
- selection: z.object({
1557
- ids: z.array(idSchema).max(64),
1558
- count: z.number().int().nonnegative().max(5e4),
1559
- bounds: BoardAwarenessFrameSchema.nullable()
1560
- }),
1561
- editingId: idSchema.nullable()
1562
- });
1563
- const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
1564
- z.object({
1565
- kind: z.literal("draw"),
1566
- id: idSchema,
1567
- nodeId: idSchema,
1568
- color: z.string().min(1).max(64),
1569
- size: finiteSchema.positive().max(256),
1570
- from: z.number().int().nonnegative().max(1e5),
1571
- points: z.array(BoardAwarenessDrawPointSchema).min(1).max(64)
1572
- }),
1573
- z.object({
1574
- kind: z.literal("arrow"),
1575
- id: idSchema,
1576
- nodeId: idSchema,
1577
- start: BoardAwarenessPointSchema,
1578
- current: BoardAwarenessPointSchema,
1579
- color: z.string().min(1).max(64),
1580
- size: finiteSchema.positive().max(256).default(BOARD_ARROW_STROKE_SIZE)
1581
- }),
1582
- z.object({
1583
- kind: z.literal("box"),
1584
- id: idSchema,
1585
- nodeId: idSchema,
1586
- shape: z.enum(["geo", "frame"]),
1587
- start: BoardAwarenessPointSchema,
1588
- current: BoardAwarenessPointSchema,
1589
- color: z.string().min(1).max(64),
1590
- geo: z.string().min(1).max(40)
1591
- }),
1592
- z.object({
1593
- kind: z.literal("transform"),
1594
- id: idSchema,
1595
- mode: z.enum([
1596
- "translate",
1597
- "resize",
1598
- "rotate",
1599
- "arrow"
1600
- ]),
1601
- nodes: z.array(BoardAwarenessNodePreviewSchema).max(64),
1602
- bounds: BoardAwarenessFrameSchema.nullable()
1603
- })
1604
- ]);
1605
- const BoardAwarenessUpdateSchema = z.discriminatedUnion("type", [
1606
- BoardAwarenessStateUpdateSchema,
1607
- z.object({
1608
- type: z.literal("gesture"),
1609
- gesture: BoardAwarenessGestureSchema
1610
- }),
1611
- z.object({
1612
- type: z.literal("gesture.end"),
1613
- gestureId: idSchema,
1614
- resultingNodeIds: z.array(idSchema).max(64)
1615
- }),
1616
- z.object({
1617
- type: z.literal("gesture.cancel"),
1618
- gestureId: idSchema
1619
- })
1620
- ]);
1621
- z.object({
1622
- spaceId: z.string().uuid(),
1623
- boardId: z.string().uuid(),
1624
- seq: z.number().int().nonnegative(),
1625
- update: BoardAwarenessUpdateSchema
1626
- });
1627
- //#endregion
1628
2262
  //#region ../protocol/dist/public-identifiers.js
1629
2263
  /**
1630
2264
  * Platform-owned path segments that must not be newly assigned to public
@@ -2012,4 +2646,4 @@ function createBoardExtensionRegistry(input = {}) {
2012
2646
  return registry;
2013
2647
  }
2014
2648
  //#endregion
2015
- export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
2649
+ export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRoom, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };