@neta-art/cohub 5.10.0 → 6.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 (36) hide show
  1. package/README.md +42 -46
  2. package/dist/board/animation.d.ts +156 -67
  3. package/dist/board/animation.js +161 -305
  4. package/dist/board/geometry.js +4 -4
  5. package/dist/board/index.d.ts +4 -4
  6. package/dist/board/index.js +3 -4
  7. package/dist/board/mutation.d.ts +5 -11
  8. package/dist/board/mutation.js +9 -46
  9. package/dist/chunks/http.d.ts +23 -12
  10. package/dist/chunks/http.js +674 -316
  11. package/dist/chunks/websocket.d.ts +2623 -201
  12. package/dist/http.d.ts +3 -3
  13. package/dist/index.d.ts +158 -299
  14. package/dist/index.js +164 -306
  15. package/dist/protocol/dist/board-authoring.d.ts +1 -0
  16. package/dist/protocol/dist/board-authoring.js +217 -0
  17. package/dist/protocol/dist/board-capability-registry.d.ts +2 -0
  18. package/dist/protocol/dist/board-capability-registry.js +74 -0
  19. package/dist/protocol/dist/board-codec.d.ts +2 -0
  20. package/dist/protocol/dist/board-codec.js +4 -0
  21. package/dist/protocol/dist/board-composition.d.ts +258 -0
  22. package/dist/protocol/dist/board-composition.js +318 -0
  23. package/dist/protocol/dist/board-constants.js +0 -25
  24. package/dist/protocol/dist/board-node.d.ts +1 -12
  25. package/dist/protocol/dist/board-node.js +1 -81
  26. package/dist/protocol/dist/board-upgrade.d.ts +1 -0
  27. package/dist/protocol/dist/board-upgrade.js +2 -0
  28. package/dist/protocol/dist/board.d.ts +19 -92
  29. package/dist/protocol/dist/board.js +18 -78
  30. package/dist/protocol/dist/index.d.ts +8 -3
  31. package/dist/protocol/dist/index.js +9 -4
  32. package/dist/types.d.ts +2 -1
  33. package/docs/work-runtime-guide.md +19 -3
  34. package/package.json +3 -2
  35. package/dist/board/nodes.d.ts +0 -113
  36. package/dist/board/nodes.js +0 -154
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as parseUsername, A as WORK_SURFACE_READY_TIMEOUT_MS, B as parseWorkSurfaceRequest, C as BoardInputError, D as WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, E as validateBoardNodes, F as buildWorkSurfaceRequest, G as UI_COMMAND_PENDING_TTL_SECONDS, H as UI_COMMAND_DEFAULT_TIMEOUT_MS, I as buildWorkSurfaceResponse, J as UI_COMMAND_VERSION, K as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, L as parseWorkComposerChipClear, M as buildWorkComposerChipClear, N as buildWorkComposerChipSet, O as WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, P as buildWorkSurfaceReady, Q as parseSpaceSlug, R as parseWorkComposerChipSet, S as createSessionPatchReducer, T as createBoardNode, U as UI_COMMAND_MAX_TIMEOUT_MS, V as parseWorkSurfaceResponse, W as UI_COMMAND_PAYLOAD_MAX_BYTES, X as isUiSurfaceMethod, Y as isTerminalUiCommandStatus, Z as parseUiCommand, _ as buildSpacePath, _t as PromptsApi, a as ReferralsApi, at as validateBoardNodeInput, b as parseAssistantMessageCommit, bt as CronJobsApi, c as UiCommandsApi, ct as parseBoardPlaybackPolicy, d as BoardTransactionError, dt as DEFAULT_BOARD_RENDER_LIMITS, et as BoardAwarenessClientPayloadSchema, f as SpaceClient, ft as SessionAccessApi, g as buildSpaceInvitePath, gt as SkillsApi, h as PublicInviteApi, ht as PublicAssetsApi, i as WorksApi, it as BOARD_NODE_CONTRACT, j as WORK_SURFACE_REQUEST_TIMEOUT_MS, k as WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, l as TasksApi, lt as ensureRealtimeConnected, m as SpacesApi, mt as SearchApi, n as createHttpClient, nt as BOARD_GEO_KINDS, o as UsersApi, ot as BoardCameraFocusParamsSchema, p as SpacePublicFilesApi, pt as ReferencesApi, q as UI_COMMAND_TERMINAL_TTL_SECONDS, r as WorkCommerceApi, rt as BOARD_NATIVE_NODE_TYPES, s as UserApi, st as BoardPlaybackPolicySchema, t as CohubHttpClient, tt as BOARD_COLOR_IDS, u as BoardClient, ut as BOARD_BUILTIN_CAPABILITIES, v as SessionGenerationStreamClient, vt as ModelsApi, w as assertBoardNodes, x as SessionPatchReducer, xt as ChannelsApi, y as createSessionGenerationStreamClient, yt as GenerationsApi, z as parseWorkSurfaceReady } from "./chunks/http.js";
1
+ import { $ as BOARD_GEO_KINDS, A as buildWorkComposerChipSet, B as UI_COMMAND_MAX_TIMEOUT_MS, C as ensureRealtimeConnected, Ct as SkillsApi, D as WORK_SURFACE_READY_TIMEOUT_MS, Dt as CronJobsApi, E as WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, Et as GenerationsApi, F as parseWorkComposerChipSet, G as UI_COMMAND_VERSION, H as UI_COMMAND_PENDING_TTL_SECONDS, I as parseWorkSurfaceReady, J as parseUiCommand, K as isTerminalUiCommandStatus, L as parseWorkSurfaceRequest, M as buildWorkSurfaceRequest, N as buildWorkSurfaceResponse, O as WORK_SURFACE_REQUEST_TIMEOUT_MS, Ot as ChannelsApi, P as parseWorkComposerChipClear, Q as BOARD_COLOR_IDS, R as parseWorkSurfaceResponse, S as createSessionPatchReducer, St as PublicAssetsApi, T as WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, Tt as ModelsApi, U as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, V as UI_COMMAND_PAYLOAD_MAX_BYTES, W as UI_COMMAND_TERMINAL_TTL_SECONDS, X as parseUsername, Y as parseSpaceSlug, Z as BoardAwarenessClientPayloadSchema, _ as buildSpacePath, _t as BOARD_BUILTIN_CAPABILITIES, a as ReferralsApi, at as BoardEffectSchema, b as parseAssistantMessageCommit, bt as ReferencesApi, c as UiCommandsApi, ct as BoardAuthoringItemSchema, d as BoardTransactionError, dt as BOARD_ANIMATION_CHANNEL_CAPABILITIES, et as BOARD_NATIVE_NODE_TYPES, f as SpaceClient, ft as BoardCompositionInputSchema, g as buildSpaceInvitePath, gt as parseBoardCompositionInput, h as PublicInviteApi, ht as BoardTrackSchema, i as WorksApi, it as validateBuiltinBoardClip, j as buildWorkSurfaceReady, k as buildWorkComposerChipClear, l as TasksApi, lt as BoardItemPatchSchema, m as SpacesApi, mt as BoardProceduralClipSchema, n as createHttpClient, nt as validateBoardNodeInput, o as UsersApi, ot as BoardPlaybackPolicySchema, p as SpacePublicFilesApi, pt as BoardCompositionSchema, q as isUiSurfaceMethod, r as WorkCommerceApi, rt as estimateBuiltinBoardClipCost, s as UserApi, st as parseBoardPlaybackPolicy, t as CohubHttpClient, tt as BOARD_NODE_CONTRACT, u as BoardClient, ut as BOARD_ANIMATION_CHANNELS, v as SessionGenerationStreamClient, vt as DEFAULT_BOARD_RENDER_LIMITS, w as WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, wt as PromptsApi, x as SessionPatchReducer, xt as SearchApi, y as createSessionGenerationStreamClient, yt as SessionAccessApi, z as UI_COMMAND_DEFAULT_TIMEOUT_MS } from "./chunks/http.js";
2
2
  import { _ as requestSourceToHeaders, a as sanitizeAccessToken, b as REALTIME_DOMAINS, c as REQUEST_SOURCE_VIA_MAX_LENGTH, d as isRequestSourceEmpty, f as isRequestSourceUuid, g as readRequestSourceFromEnv, h as parseRequestSourceFromHeaders, i as matchesUnauthorizedErrorToken, l as hasRequestSourceIdentity, m as normalizeRequestSource, n as HttpTransport, o as COHUB_SOURCE_HEADER, p as mergeRequestSourceIntoMeta, r as joinApiUrl, s as COHUB_SOURCE_HEADER_NAMES, t as HttpError, u as isRequestSourceClientId, v as resolveRequestSourceChannel, x as REALTIME_ROOM_EVENT_NAME_PATTERN, y as isUuid } 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";
@@ -1934,6 +1934,7 @@ function createWorkBridgeCore(config) {
1934
1934
  try {
1935
1935
  if (data.type === "cohub.work.context") {
1936
1936
  const workScopes = clonePermissionScopes(work.workScopes);
1937
+ const viewerUuid = await getViewerUuid();
1937
1938
  reply(data.requestId, {
1938
1939
  type: "cohub.work.context.result",
1939
1940
  context: {
@@ -1943,6 +1944,8 @@ function createWorkBridgeCore(config) {
1943
1944
  url: typeof location !== "undefined" ? location.href : ""
1944
1945
  },
1945
1946
  space: { id: work.spaceId },
1947
+ viewer: viewerUuid ? { userUuid: viewerUuid } : null,
1948
+ ...config.invocation ? { invocation: { ...config.invocation } } : {},
1946
1949
  permissions: {
1947
1950
  scopes: workScopes,
1948
1951
  workScopes,
@@ -2135,281 +2138,146 @@ function createWorkBridgeCore(config) {
2135
2138
  }
2136
2139
  //#endregion
2137
2140
  //#region src/board/animation.ts
2138
- const ZERO_COST = {
2139
- particles: 0,
2140
- vertices: 0,
2141
- dynamicVertices: 0,
2142
- drawCalls: 0,
2143
- filterPasses: 0,
2144
- renderTexturePixels: 0,
2145
- textureBytes: 0,
2146
- bufferBytes: 0,
2147
- simulationSteps: 0
2148
- };
2149
- const DEFAULT_BOARD_LIMITS = DEFAULT_BOARD_RENDER_LIMITS;
2150
- const BUILTIN_CAPABILITIES = BOARD_BUILTIN_CAPABILITIES;
2151
- function numericParam(params, key, fallback) {
2152
- const value = params[key];
2153
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2141
+ function track(input) {
2142
+ return BoardTrackSchema.parse(input);
2154
2143
  }
2155
- function builtinDefinition(capability) {
2156
- return {
2157
- ...capability,
2158
- validate(params) {
2159
- const diagnostics = [];
2160
- if (capability.id === "effects.particles") {
2161
- const count = params.count;
2162
- const bounds = params.bounds;
2163
- if (!Number.isSafeInteger(count) || count < 1 || count > DEFAULT_BOARD_LIMITS.particles) diagnostics.push({
2164
- severity: "error",
2165
- code: "INVALID_PARTICLE_COUNT",
2166
- message: `count must be an integer between 1 and ${DEFAULT_BOARD_LIMITS.particles}`,
2167
- path: "params.count"
2168
- });
2169
- if (!bounds || typeof bounds !== "object" || Array.isArray(bounds)) diagnostics.push({
2170
- severity: "error",
2171
- code: "PARTICLE_BOUNDS_REQUIRED",
2172
- message: "particles require finite world bounds",
2173
- path: "params.bounds",
2174
- coordinateSpace: "world"
2175
- });
2176
- else {
2177
- const value = bounds;
2178
- if (![
2179
- value.x,
2180
- value.y,
2181
- value.width,
2182
- value.height
2183
- ].every((item) => typeof item === "number" && Number.isFinite(item)) || value.width <= 0 || value.height <= 0) diagnostics.push({
2184
- severity: "error",
2185
- code: "INVALID_PARTICLE_BOUNDS",
2186
- message: "particle bounds must have a positive finite size",
2187
- path: "params.bounds",
2188
- coordinateSpace: "world"
2189
- });
2190
- }
2191
- }
2192
- if (capability.id === "motion.path") {
2193
- const points = params.points;
2194
- if (!Array.isArray(points) || points.length < 2 || points.length > 1e4) diagnostics.push({
2195
- severity: "error",
2196
- code: "INVALID_MOTION_PATH",
2197
- message: "motion path must contain 2 to 10000 world-offset points",
2198
- path: "params.points",
2199
- coordinateSpace: "world-offset"
2200
- });
2201
- }
2202
- if (capability.id === "camera.focus") {
2203
- const parsed = BoardCameraFocusParamsSchema.safeParse(params);
2204
- if (!parsed.success) diagnostics.push({
2205
- severity: "error",
2206
- code: "INVALID_CAMERA_FOCUS",
2207
- message: parsed.error.issues[0]?.message ?? "invalid camera focus",
2208
- path: "params",
2209
- coordinateSpace: "world"
2210
- });
2211
- }
2212
- return diagnostics;
2144
+ function proceduralClip(input) {
2145
+ return BoardProceduralClipSchema.parse({
2146
+ ...input,
2147
+ kindVersion: input.kindVersion ?? 1
2148
+ });
2149
+ }
2150
+ function composition(input) {
2151
+ return BoardCompositionSchema.parse(input);
2152
+ }
2153
+ function compileComposition(input) {
2154
+ return composition({
2155
+ id: input.id,
2156
+ name: input.name,
2157
+ timeline: {
2158
+ duration: input.duration,
2159
+ tracks: (input.tracks ?? []).map(track),
2160
+ clips: (input.clips ?? []).map(proceduralClip),
2161
+ markers: input.markers ?? []
2213
2162
  },
2214
- getBounds(params) {
2215
- const bounds = params.bounds;
2216
- if (!bounds || typeof bounds !== "object" || Array.isArray(bounds)) return null;
2217
- const value = bounds;
2218
- return [
2219
- value.x,
2220
- value.y,
2221
- value.width,
2222
- value.height
2223
- ].every((item) => typeof item === "number" && Number.isFinite(item)) ? value : null;
2163
+ playback: input.playback ?? {
2164
+ loop: false,
2165
+ endBehavior: "hold",
2166
+ reducedMotion: { mode: "base" }
2224
2167
  },
2225
- estimateCost(params) {
2226
- switch (capability.id) {
2227
- case "effects.particles": {
2228
- const particles = Math.max(0, Math.floor(numericParam(params, "count", 0)));
2229
- return {
2230
- particles,
2231
- vertices: particles * 4,
2232
- dynamicVertices: particles * 4,
2233
- drawCalls: 1,
2234
- bufferBytes: particles * 48,
2235
- simulationSteps: particles
2236
- };
2237
- }
2238
- case "effects.trail": return {
2239
- vertices: 32,
2240
- dynamicVertices: 32,
2241
- drawCalls: 1,
2242
- bufferBytes: 1024,
2243
- simulationSteps: 16
2244
- };
2245
- case "effects.impact":
2246
- case "effects.flash": return {
2247
- vertices: 64,
2248
- drawCalls: 1
2249
- };
2250
- case "effects.color": return {
2251
- drawCalls: 1,
2252
- filterPasses: 1
2253
- };
2254
- case "draw.reveal":
2255
- case "draw.handwrite": return {
2256
- drawCalls: 1,
2257
- dynamicVertices: 1
2258
- };
2259
- default: return {};
2260
- }
2261
- }
2262
- };
2168
+ metadata: input.metadata ?? {},
2169
+ revision: 0
2170
+ });
2263
2171
  }
2264
- function stableHash(value) {
2265
- let hash = 2166136261;
2266
- for (let index = 0; index < value.length; index += 1) {
2267
- hash ^= value.charCodeAt(index);
2268
- hash = Math.imul(hash, 16777619);
2172
+ const clamp01 = (value) => Math.max(0, Math.min(1, value));
2173
+ function sampleEasing(easing, value) {
2174
+ const t = clamp01(value);
2175
+ switch (easing) {
2176
+ case "ease-in-quad": return t * t;
2177
+ case "ease-out-quad": return 1 - (1 - t) ** 2;
2178
+ case "ease-in-out-quad": return t < .5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
2179
+ case "ease-in-cubic": return t ** 3;
2180
+ case "ease-out-cubic": return 1 - (1 - t) ** 3;
2181
+ case "ease-in-out-cubic": return t < .5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2;
2182
+ case "ease-out-quart": return 1 - (1 - t) ** 4;
2183
+ case "ease-out-expo": return t === 1 ? 1 : 1 - 2 ** (-10 * t);
2184
+ default: return t;
2269
2185
  }
2270
- return (hash >>> 0).toString(36).padStart(7, "0");
2271
- }
2272
- function stableId(prefix, seed, path) {
2273
- return `${prefix}_${stableHash(`${seed}:${path}`)}`;
2274
- }
2275
- function assertDuration(value, field) {
2276
- if (!Number.isFinite(value) || value < 0) throw new TypeError(`${field} must be a finite non-negative number`);
2277
2186
  }
2278
- const timeline = {
2279
- clip(clip) {
2280
- return {
2281
- type: "clip",
2282
- clip
2283
- };
2284
- },
2285
- parallel(...children) {
2286
- return {
2287
- type: "parallel",
2288
- children
2289
- };
2290
- },
2291
- sequence(...children) {
2292
- return {
2293
- type: "sequence",
2294
- children
2295
- };
2296
- },
2297
- stagger(each, ...children) {
2298
- assertDuration(each, "stagger each");
2299
- return {
2300
- type: "stagger",
2301
- each,
2302
- children
2303
- };
2304
- },
2305
- delay(duration, child) {
2306
- assertDuration(duration, "delay duration");
2307
- return {
2308
- type: "delay",
2309
- duration,
2310
- child
2311
- };
2312
- },
2313
- repeat(count, child) {
2314
- if (!Number.isSafeInteger(count) || count < 1 || count > 1e3) throw new TypeError("repeat count must be an integer between 1 and 1000");
2315
- return {
2316
- type: "repeat",
2317
- count,
2318
- child
2319
- };
2187
+ function interpolate(left, right, progress) {
2188
+ if (typeof left === "number" && typeof right === "number") return left + (right - left) * progress;
2189
+ if (left && right && typeof left === "object" && typeof right === "object" && !Array.isArray(left) && !Array.isArray(right)) {
2190
+ const a = left;
2191
+ const b = right;
2192
+ const result = {};
2193
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) result[key] = interpolate(a[key], b[key], progress);
2194
+ return result;
2320
2195
  }
2321
- };
2322
- function clip(input) {
2323
- assertDuration(input.duration, "clip duration");
2324
- if (input.duration === 0) throw new TypeError("clip duration must be greater than zero");
2325
- return timeline.clip({
2326
- id: input.id,
2327
- kind: input.kind,
2328
- kindVersion: input.kindVersion ?? 1,
2329
- target: input.target,
2330
- duration: input.duration,
2331
- layer: input.layer ?? "content",
2332
- fill: input.fill ?? "none",
2333
- easing: input.easing ?? "linear",
2334
- params: input.params ?? {},
2335
- keyframes: input.keyframes ?? [],
2336
- assetRefs: input.assetRefs ?? [],
2337
- seed: input.seed,
2338
- metadata: input.metadata ?? {}
2339
- });
2196
+ return progress < 1 ? left : right;
2340
2197
  }
2341
- function flatten(input, offset, sequenceSeed, path) {
2342
- if (input.type === "clip") {
2343
- const clipSeed = input.clip.seed ?? `${sequenceSeed}:${path}`;
2198
+ /** Deterministically sample one validated Track without touching scene state. */
2199
+ function sampleTrack(trackValue, time) {
2200
+ const frames = trackValue.keyframes;
2201
+ const first = frames[0];
2202
+ const last = frames.at(-1);
2203
+ if (!first || !last) return null;
2204
+ if (time < first.time) {
2205
+ if (trackValue.fill !== "backwards" && trackValue.fill !== "both") return null;
2344
2206
  return {
2345
- duration: input.clip.duration,
2346
- clips: [{
2347
- ...input.clip,
2348
- id: input.clip.id ?? stableId("clip", sequenceSeed, path),
2349
- start: offset,
2350
- seed: clipSeed
2351
- }]
2207
+ target: trackValue.target,
2208
+ channel: trackValue.channel,
2209
+ value: first.value
2352
2210
  };
2353
2211
  }
2354
- if (input.type === "delay") {
2355
- const nested = flatten(input.child, offset + input.duration, sequenceSeed, `${path}.child`);
2212
+ if (time > last.time) {
2213
+ if (trackValue.fill !== "forwards" && trackValue.fill !== "both") return null;
2356
2214
  return {
2357
- duration: input.duration + nested.duration,
2358
- clips: nested.clips
2215
+ target: trackValue.target,
2216
+ channel: trackValue.channel,
2217
+ value: last.value
2359
2218
  };
2360
2219
  }
2361
- if (input.type === "repeat") {
2362
- const clips = [];
2363
- let cursor = offset;
2364
- let total = 0;
2365
- for (let index = 0; index < input.count; index += 1) {
2366
- const nested = flatten(input.child, cursor, sequenceSeed, `${path}.${index}`);
2367
- clips.push(...nested.clips);
2368
- cursor += nested.duration;
2369
- total += nested.duration;
2370
- }
2371
- return {
2372
- duration: total,
2373
- clips
2374
- };
2375
- }
2376
- const clips = [];
2377
- let duration = 0;
2378
- for (const [index, child] of input.children.entries()) {
2379
- const childOffset = input.type === "sequence" ? duration : input.type === "stagger" ? input.each * index : 0;
2380
- const nested = flatten(child, offset + childOffset, sequenceSeed, `${path}.${index}`);
2381
- clips.push(...nested.clips);
2382
- duration = input.type === "sequence" ? duration + nested.duration : Math.max(duration, childOffset + nested.duration);
2383
- }
2220
+ let low = 0;
2221
+ let high = frames.length;
2222
+ while (low < high) {
2223
+ const middle = low + high >>> 1;
2224
+ if ((frames[middle]?.time ?? Number.POSITIVE_INFINITY) <= time) low = middle + 1;
2225
+ else high = middle;
2226
+ }
2227
+ const left = frames[Math.max(0, low - 1)] ?? first;
2228
+ const right = frames[Math.min(frames.length - 1, low)] ?? last;
2229
+ if (left === right || trackValue.interpolation === "step") return {
2230
+ target: trackValue.target,
2231
+ channel: trackValue.channel,
2232
+ value: left.value
2233
+ };
2234
+ const span = Math.max(Number.EPSILON, right.time - left.time);
2235
+ const progress = sampleEasing(right.easing ?? "linear", (time - left.time) / span);
2384
2236
  return {
2385
- duration,
2386
- clips
2237
+ target: trackValue.target,
2238
+ channel: trackValue.channel,
2239
+ value: interpolate(left.value, right.value, progress)
2387
2240
  };
2388
2241
  }
2389
- function compileSequence(input) {
2390
- const flattened = flatten(input.timeline, 0, input.seed, "root");
2391
- flattened.clips.sort((left, right) => left.start - right.start || left.id.localeCompare(right.id));
2392
- const refs = /* @__PURE__ */ new Map();
2393
- for (const item of flattened.clips) for (const ref of item.assetRefs) refs.set(`${ref.type}:${ref.ref}:${ref.digest ?? ""}`, ref);
2242
+ function sampleCompositionTracks(value, time) {
2243
+ const position = Math.max(0, Math.min(value.timeline.duration, time));
2244
+ return value.timeline.tracks.flatMap((item) => {
2245
+ const sampled = sampleTrack(item, position);
2246
+ return sampled ? [sampled] : [];
2247
+ });
2248
+ }
2249
+ const ZERO_COST = {
2250
+ particles: 0,
2251
+ vertices: 0,
2252
+ dynamicVertices: 0,
2253
+ drawCalls: 0,
2254
+ filterPasses: 0,
2255
+ renderTexturePixels: 0,
2256
+ textureBytes: 0,
2257
+ bufferBytes: 0,
2258
+ simulationSteps: 0
2259
+ };
2260
+ const DEFAULT_BOARD_LIMITS = DEFAULT_BOARD_RENDER_LIMITS;
2261
+ function builtinDefinition(capability) {
2394
2262
  return {
2395
- sequence: {
2396
- id: input.id,
2397
- name: input.name,
2398
- duration: flattened.duration,
2399
- seed: input.seed,
2400
- restPose: input.restPose ?? {},
2401
- metadata: input.metadata ?? {}
2263
+ ...capability,
2264
+ validate() {
2265
+ return [];
2402
2266
  },
2403
- clips: flattened.clips,
2404
- assetRefs: [...refs.values()]
2267
+ estimateCost(params) {
2268
+ return estimateBuiltinBoardClipCost({
2269
+ kind: capability.id,
2270
+ params
2271
+ });
2272
+ }
2405
2273
  };
2406
2274
  }
2407
- function addCost(target, source) {
2408
- for (const key of Object.keys(target)) target[key] += source[key] ?? 0;
2409
- }
2410
2275
  var BoardExtensionRegistry = class {
2411
2276
  #extensions = /* @__PURE__ */ new Map();
2412
2277
  #presets = /* @__PURE__ */ new Map();
2278
+ constructor() {
2279
+ for (const capability of BOARD_BUILTIN_CAPABILITIES) this.register(builtinDefinition(capability));
2280
+ }
2413
2281
  register(definition) {
2414
2282
  const key = `${definition.kind}:${definition.id}@${definition.version}`;
2415
2283
  const target = definition.kind === "preset" ? this.#presets : this.#extensions;
@@ -2418,9 +2286,15 @@ var BoardExtensionRegistry = class {
2418
2286
  return this;
2419
2287
  }
2420
2288
  capabilities() {
2421
- const extensions = [...this.#extensions.values()].map(({ validate: _validate, getBounds: _bounds, getAssetRefs: _refs, estimateCost: _cost, ...definition }) => definition);
2422
- const presets = [...this.#presets.values()].map(({ compile: _compile, ...definition }) => definition);
2423
- return [...extensions, ...presets];
2289
+ return [...this.#extensions.values(), ...this.#presets.values()].map((definition) => ({
2290
+ kind: definition.kind,
2291
+ id: definition.id,
2292
+ version: definition.version,
2293
+ ...definition.digest ? { digest: definition.digest } : {},
2294
+ ...definition.renderers ? { renderers: definition.renderers } : {},
2295
+ ...definition.fallbackId ? { fallbackId: definition.fallbackId } : {},
2296
+ ...definition.schema ? { schema: definition.schema } : {}
2297
+ }));
2424
2298
  }
2425
2299
  compilePreset(id, version, params) {
2426
2300
  const preset = this.#presets.get(`preset:${id}@${version}`);
@@ -2428,88 +2302,72 @@ var BoardExtensionRegistry = class {
2428
2302
  return preset.compile(params);
2429
2303
  }
2430
2304
  validate(input) {
2305
+ const parsed = BoardCompositionSchema.safeParse(input.composition);
2306
+ if (!parsed.success) return {
2307
+ valid: false,
2308
+ diagnostics: parsed.error.issues.map((issue) => ({
2309
+ severity: "error",
2310
+ code: "INVALID_COMPOSITION",
2311
+ message: issue.message,
2312
+ path: issue.path.join(".")
2313
+ })),
2314
+ peakCost: { ...ZERO_COST }
2315
+ };
2431
2316
  const diagnostics = [];
2317
+ const composition = parsed.data;
2318
+ const cost = { ...ZERO_COST };
2432
2319
  const events = [];
2433
- const profile = input.profile ?? "high";
2434
- const persistentCost = { ...ZERO_COST };
2435
- for (const [index, effect] of (input.effects ?? []).entries()) {
2436
- const definition = this.#extensions.get(`effect:${effect.kind}@${effect.kindVersion}`);
2437
- if (!definition) {
2438
- diagnostics.push({
2439
- severity: "warning",
2440
- code: "UNKNOWN_EFFECT",
2441
- message: `No renderer is registered for ${effect.kind}@${effect.kindVersion}`,
2442
- path: `effects.${index}`
2443
- });
2444
- continue;
2445
- }
2446
- diagnostics.push(...definition.validate?.(effect.params) ?? []);
2447
- addCost(persistentCost, definition.estimateCost(effect.params, profile));
2448
- }
2449
- for (const [index, clip] of input.clips.entries()) {
2320
+ for (const clip of composition.timeline.clips) {
2450
2321
  const definition = this.#extensions.get(`clip:${clip.kind}@${clip.kindVersion}`);
2451
2322
  if (!definition) {
2452
2323
  diagnostics.push({
2453
2324
  severity: "warning",
2454
2325
  code: "UNKNOWN_CLIP",
2455
- message: `No renderer is registered for ${clip.kind}@${clip.kindVersion}`,
2456
- path: `clips.${index}`
2326
+ message: `No renderer is registered for ${clip.kind}@${clip.kindVersion}`
2457
2327
  });
2458
2328
  continue;
2459
2329
  }
2330
+ diagnostics.push(...validateBuiltinBoardClip(clip, `composition.timeline.clips.${clip.id}`));
2460
2331
  diagnostics.push(...definition.validate?.(clip.params) ?? []);
2461
- const cost = { ...ZERO_COST };
2462
- addCost(cost, definition.estimateCost(clip.params, profile));
2332
+ const estimate = definition.estimateCost(clip.params, input.profile ?? "high");
2333
+ const clipCost = { ...ZERO_COST };
2334
+ for (const key of Object.keys(clipCost)) clipCost[key] = estimate[key] ?? 0;
2463
2335
  events.push({
2464
2336
  at: clip.start,
2465
2337
  direction: 1,
2466
- cost
2467
- }, {
2338
+ cost: clipCost
2339
+ });
2340
+ events.push({
2468
2341
  at: clip.start + clip.duration,
2469
2342
  direction: -1,
2470
- cost
2471
- });
2472
- }
2473
- const cameraFocusClips = input.clips.map((clip, index) => ({
2474
- clip,
2475
- index
2476
- })).filter(({ clip }) => clip.kind === "camera.focus").sort((left, right) => left.clip.start - right.clip.start);
2477
- for (let index = 1; index < cameraFocusClips.length; index += 1) {
2478
- const previous = cameraFocusClips[index - 1];
2479
- const currentFocus = cameraFocusClips[index];
2480
- if (previous && currentFocus && currentFocus.clip.start < previous.clip.start + previous.clip.duration) diagnostics.push({
2481
- severity: "error",
2482
- code: "OVERLAPPING_CAMERA_FOCUS",
2483
- message: "camera.focus clips must not overlap",
2484
- path: `clips.${currentFocus.index}.start`
2343
+ cost: clipCost
2485
2344
  });
2486
2345
  }
2487
2346
  events.sort((left, right) => left.at - right.at || left.direction - right.direction);
2488
- const current = { ...persistentCost };
2489
- const peak = { ...persistentCost };
2490
- for (const event of events) for (const key of Object.keys(current)) {
2491
- current[key] += event.cost[key] * event.direction;
2492
- peak[key] = Math.max(peak[key], current[key]);
2347
+ const active = { ...ZERO_COST };
2348
+ for (const event of events) for (const key of Object.keys(active)) {
2349
+ active[key] += event.cost[key] * event.direction;
2350
+ cost[key] = Math.max(cost[key], active[key]);
2493
2351
  }
2352
+ for (const effect of input.effects ?? []) if (effect.kind === "effects.pulse" || effect.kind === "effects.float") cost.drawCalls += 1;
2494
2353
  const limits = input.limits ?? DEFAULT_BOARD_LIMITS;
2495
- for (const key of Object.keys(limits)) if (peak[key] > limits[key]) diagnostics.push({
2354
+ for (const key of Object.keys(cost)) if (cost[key] > limits[key]) diagnostics.push({
2496
2355
  severity: "warning",
2497
2356
  code: "RENDER_BUDGET_EXCEEDED",
2498
- message: `${key} peaks at ${peak[key]}, above ${limits[key]}`,
2357
+ message: `${key} peaks at ${cost[key]}, above ${limits[key]}`,
2499
2358
  path: key,
2500
2359
  adaptation: { quality: "lower" }
2501
2360
  });
2502
2361
  return {
2503
2362
  valid: !diagnostics.some((item) => item.severity === "error"),
2504
2363
  diagnostics,
2505
- peakCost: peak
2364
+ peakCost: cost
2506
2365
  };
2507
2366
  }
2508
2367
  };
2509
- function createBoardExtensionRegistry(input = {}) {
2510
- const registry = new BoardExtensionRegistry();
2511
- if (input.builtins !== false) for (const capability of BUILTIN_CAPABILITIES) registry.register(builtinDefinition(capability));
2512
- return registry;
2368
+ function createBoardExtensionRegistry() {
2369
+ return new BoardExtensionRegistry();
2513
2370
  }
2371
+ const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
2514
2372
  //#endregion
2515
- export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BOARD_NATIVE_NODE_TYPES, BOARD_NODE_CONTRACT, BillingApi, BoardClient, BoardExtensionRegistry, BoardInputError, 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, SpacePublicFilesApi, UI_COMMAND_DEFAULT_TIMEOUT_MS, UI_COMMAND_MAX_TIMEOUT_MS, UI_COMMAND_PAYLOAD_MAX_BYTES, UI_COMMAND_PENDING_TTL_SECONDS, UI_COMMAND_SETTLEMENT_GRACE_SECONDS, UI_COMMAND_TERMINAL_TTL_SECONDS, UI_COMMAND_VERSION, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, WORK_SURFACE_READY_TIMEOUT_MS, WORK_SURFACE_REQUEST_TIMEOUT_MS, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkRuntimeApi, WorkSurfaceApi, WorksApi, assertBoardNodes, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, buildWorkSurfaceRequest, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createBoardNode, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, formatWorkRef, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalUiCommandStatus, isUiSurfaceMethod, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseUiCommand, parseWorkRef, parseWorkSurfaceReady, parseWorkSurfaceResponse, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline, validateBoardNodeInput, validateBoardNodes };
2373
+ export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BOARD_NATIVE_NODE_TYPES, BOARD_NODE_CONTRACT, BillingApi, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, 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, SpacePublicFilesApi, UI_COMMAND_DEFAULT_TIMEOUT_MS, UI_COMMAND_MAX_TIMEOUT_MS, UI_COMMAND_PAYLOAD_MAX_BYTES, UI_COMMAND_PENDING_TTL_SECONDS, UI_COMMAND_SETTLEMENT_GRACE_SECONDS, UI_COMMAND_TERMINAL_TTL_SECONDS, UI_COMMAND_VERSION, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH, WORK_SURFACE_READY_TIMEOUT_MS, WORK_SURFACE_REQUEST_TIMEOUT_MS, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkRuntimeApi, WorkSurfaceApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, buildWorkSurfaceRequest, clearGrantedWorkScopes, compileComposition, composition, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, formatWorkRef, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalUiCommandStatus, isUiSurfaceMethod, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseUiCommand, parseWorkRef, parseWorkSurfaceReady, parseWorkSurfaceResponse, proceduralClip, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, setGrantedWorkScopes, track, validateBoardNodeInput };
@@ -0,0 +1 @@
1
+ import { z } from "zod";