@neta-art/cohub 5.9.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 (55) hide show
  1. package/README.md +42 -46
  2. package/dist/board/animation.d.ts +156 -67
  3. package/dist/board/animation.js +162 -277
  4. package/dist/board/codec.js +1 -1
  5. package/dist/board/export/index.d.ts +6 -0
  6. package/dist/board/export/index.js +6 -3
  7. package/dist/board/export/scene.d.ts +6 -0
  8. package/dist/board/export/scene.js +26 -2
  9. package/dist/board/geometry.d.ts +27 -1
  10. package/dist/board/geometry.js +92 -1
  11. package/dist/board/headless/index.d.ts +7 -1
  12. package/dist/board/headless/index.js +6 -2
  13. package/dist/board/index.d.ts +8 -6
  14. package/dist/board/index.js +7 -6
  15. package/dist/board/mutation.d.ts +15 -0
  16. package/dist/board/mutation.js +67 -0
  17. package/dist/board/render/css-color.d.ts +4 -0
  18. package/dist/board/render/css-color.js +36 -0
  19. package/dist/board/render/index.d.ts +2 -1
  20. package/dist/board/render/index.js +2 -1
  21. package/dist/board/render/renderers/base-card-renderer.js +3 -20
  22. package/dist/board/render/themes/clean-theme.js +6 -3
  23. package/dist/chunks/http.d.ts +30 -7
  24. package/dist/chunks/http.js +785 -289
  25. package/dist/chunks/websocket.d.ts +2675 -187
  26. package/dist/http.d.ts +3 -3
  27. package/dist/index.d.ts +158 -299
  28. package/dist/index.js +164 -279
  29. package/dist/protocol/dist/board-authoring.d.ts +1 -0
  30. package/dist/protocol/dist/board-authoring.js +217 -0
  31. package/dist/protocol/dist/board-capability-registry.d.ts +2 -0
  32. package/dist/protocol/dist/board-capability-registry.js +74 -0
  33. package/dist/protocol/dist/board-codec.d.ts +2 -0
  34. package/dist/protocol/dist/board-codec.js +4 -0
  35. package/dist/protocol/dist/board-composition.d.ts +258 -0
  36. package/dist/protocol/dist/board-composition.js +318 -0
  37. package/dist/protocol/dist/board-constants.d.ts +2 -1
  38. package/dist/protocol/dist/board-constants.js +40 -10
  39. package/dist/protocol/dist/board-document.d.ts +28 -2
  40. package/dist/protocol/dist/board-document.js +16 -3
  41. package/dist/protocol/dist/board-node.d.ts +1 -11
  42. package/dist/protocol/dist/board-node.js +1 -81
  43. package/dist/protocol/dist/board-upgrade.d.ts +1 -0
  44. package/dist/protocol/dist/board-upgrade.js +2 -0
  45. package/dist/protocol/dist/board-url.d.ts +2 -1
  46. package/dist/protocol/dist/board-url.js +8 -3
  47. package/dist/protocol/dist/board.d.ts +135 -76
  48. package/dist/protocol/dist/board.js +50 -69
  49. package/dist/protocol/dist/index.d.ts +9 -4
  50. package/dist/protocol/dist/index.js +9 -4
  51. package/dist/types.d.ts +3 -2
  52. package/docs/work-runtime-guide.md +19 -3
  53. package/package.json +3 -2
  54. package/dist/board/nodes.d.ts +0 -113
  55. 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 ModelsApi, a as ReferralsApi, at as validateBoardNodeInput, b as parseAssistantMessageCommit, bt as ChannelsApi, c as UiCommandsApi, ct as ensureRealtimeConnected, d as BoardTransactionError, dt as SessionAccessApi, et as BoardAwarenessClientPayloadSchema, f as SpaceClient, ft as ReferencesApi, g as buildSpaceInvitePath, gt as PromptsApi, h as PublicInviteApi, ht as SkillsApi, 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 BOARD_BUILTIN_CAPABILITIES, m as SpacesApi, mt as PublicAssetsApi, n as createHttpClient, nt as BOARD_GEO_KINDS, o as UsersApi, ot as BoardPlaybackPolicySchema, p as SpacePublicFilesApi, pt as SearchApi, q as UI_COMMAND_TERMINAL_TTL_SECONDS, r as WorkCommerceApi, rt as BOARD_NATIVE_NODE_TYPES, s as UserApi, st as parseBoardPlaybackPolicy, t as CohubHttpClient, tt as BOARD_COLOR_IDS, u as BoardClient, ut as DEFAULT_BOARD_RENDER_LIMITS, v as SessionGenerationStreamClient, vt as GenerationsApi, w as assertBoardNodes, x as SessionPatchReducer, y as createSessionGenerationStreamClient, yt as CronJobsApi, 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,268 +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 bounds",
2173
- path: "params.bounds"
2174
- });
2175
- else {
2176
- const value = bounds;
2177
- if (![
2178
- value.x,
2179
- value.y,
2180
- value.width,
2181
- value.height
2182
- ].every((item) => typeof item === "number" && Number.isFinite(item)) || value.width <= 0 || value.height <= 0) diagnostics.push({
2183
- severity: "error",
2184
- code: "INVALID_PARTICLE_BOUNDS",
2185
- message: "particle bounds must have a positive finite size",
2186
- path: "params.bounds"
2187
- });
2188
- }
2189
- }
2190
- if (capability.id === "motion.path") {
2191
- const points = params.points;
2192
- if (!Array.isArray(points) || points.length < 2 || points.length > 1e4) diagnostics.push({
2193
- severity: "error",
2194
- code: "INVALID_MOTION_PATH",
2195
- message: "motion path must contain 2 to 10000 points",
2196
- path: "params.points"
2197
- });
2198
- }
2199
- 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 ?? []
2200
2162
  },
2201
- getBounds(params) {
2202
- const bounds = params.bounds;
2203
- if (!bounds || typeof bounds !== "object" || Array.isArray(bounds)) return null;
2204
- const value = bounds;
2205
- return [
2206
- value.x,
2207
- value.y,
2208
- value.width,
2209
- value.height
2210
- ].every((item) => typeof item === "number" && Number.isFinite(item)) ? value : null;
2163
+ playback: input.playback ?? {
2164
+ loop: false,
2165
+ endBehavior: "hold",
2166
+ reducedMotion: { mode: "base" }
2211
2167
  },
2212
- estimateCost(params) {
2213
- switch (capability.id) {
2214
- case "effects.particles": {
2215
- const particles = Math.max(0, Math.floor(numericParam(params, "count", 0)));
2216
- return {
2217
- particles,
2218
- vertices: particles * 4,
2219
- dynamicVertices: particles * 4,
2220
- drawCalls: 1,
2221
- bufferBytes: particles * 48,
2222
- simulationSteps: particles
2223
- };
2224
- }
2225
- case "effects.trail": return {
2226
- vertices: 32,
2227
- dynamicVertices: 32,
2228
- drawCalls: 1,
2229
- bufferBytes: 1024,
2230
- simulationSteps: 16
2231
- };
2232
- case "effects.impact":
2233
- case "effects.flash": return {
2234
- vertices: 64,
2235
- drawCalls: 1
2236
- };
2237
- case "effects.color": return {
2238
- drawCalls: 1,
2239
- filterPasses: 1
2240
- };
2241
- case "draw.reveal":
2242
- case "draw.handwrite": return {
2243
- drawCalls: 1,
2244
- dynamicVertices: 1
2245
- };
2246
- default: return {};
2247
- }
2248
- }
2249
- };
2168
+ metadata: input.metadata ?? {},
2169
+ revision: 0
2170
+ });
2250
2171
  }
2251
- function stableHash(value) {
2252
- let hash = 2166136261;
2253
- for (let index = 0; index < value.length; index += 1) {
2254
- hash ^= value.charCodeAt(index);
2255
- 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;
2256
2185
  }
2257
- return (hash >>> 0).toString(36).padStart(7, "0");
2258
- }
2259
- function stableId(prefix, seed, path) {
2260
- return `${prefix}_${stableHash(`${seed}:${path}`)}`;
2261
- }
2262
- function assertDuration(value, field) {
2263
- if (!Number.isFinite(value) || value < 0) throw new TypeError(`${field} must be a finite non-negative number`);
2264
2186
  }
2265
- const timeline = {
2266
- clip(clip) {
2267
- return {
2268
- type: "clip",
2269
- clip
2270
- };
2271
- },
2272
- parallel(...children) {
2273
- return {
2274
- type: "parallel",
2275
- children
2276
- };
2277
- },
2278
- sequence(...children) {
2279
- return {
2280
- type: "sequence",
2281
- children
2282
- };
2283
- },
2284
- stagger(each, ...children) {
2285
- assertDuration(each, "stagger each");
2286
- return {
2287
- type: "stagger",
2288
- each,
2289
- children
2290
- };
2291
- },
2292
- delay(duration, child) {
2293
- assertDuration(duration, "delay duration");
2294
- return {
2295
- type: "delay",
2296
- duration,
2297
- child
2298
- };
2299
- },
2300
- repeat(count, child) {
2301
- if (!Number.isSafeInteger(count) || count < 1 || count > 1e3) throw new TypeError("repeat count must be an integer between 1 and 1000");
2302
- return {
2303
- type: "repeat",
2304
- count,
2305
- child
2306
- };
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;
2307
2195
  }
2308
- };
2309
- function clip(input) {
2310
- assertDuration(input.duration, "clip duration");
2311
- if (input.duration === 0) throw new TypeError("clip duration must be greater than zero");
2312
- return timeline.clip({
2313
- id: input.id,
2314
- kind: input.kind,
2315
- kindVersion: input.kindVersion ?? 1,
2316
- target: input.target,
2317
- duration: input.duration,
2318
- layer: input.layer ?? "content",
2319
- fill: input.fill ?? "none",
2320
- easing: input.easing ?? "linear",
2321
- params: input.params ?? {},
2322
- keyframes: input.keyframes ?? [],
2323
- assetRefs: input.assetRefs ?? [],
2324
- seed: input.seed,
2325
- metadata: input.metadata ?? {}
2326
- });
2196
+ return progress < 1 ? left : right;
2327
2197
  }
2328
- function flatten(input, offset, sequenceSeed, path) {
2329
- if (input.type === "clip") {
2330
- const clipSeed = input.clip.seed ?? `${sequenceSeed}:${path}`;
2331
- return {
2332
- duration: input.clip.duration,
2333
- clips: [{
2334
- ...input.clip,
2335
- id: input.clip.id ?? stableId("clip", sequenceSeed, path),
2336
- start: offset,
2337
- seed: clipSeed
2338
- }]
2339
- };
2340
- }
2341
- if (input.type === "delay") {
2342
- const nested = flatten(input.child, offset + input.duration, sequenceSeed, `${path}.child`);
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;
2343
2206
  return {
2344
- duration: input.duration + nested.duration,
2345
- clips: nested.clips
2207
+ target: trackValue.target,
2208
+ channel: trackValue.channel,
2209
+ value: first.value
2346
2210
  };
2347
2211
  }
2348
- if (input.type === "repeat") {
2349
- const clips = [];
2350
- let cursor = offset;
2351
- let total = 0;
2352
- for (let index = 0; index < input.count; index += 1) {
2353
- const nested = flatten(input.child, cursor, sequenceSeed, `${path}.${index}`);
2354
- clips.push(...nested.clips);
2355
- cursor += nested.duration;
2356
- total += nested.duration;
2357
- }
2212
+ if (time > last.time) {
2213
+ if (trackValue.fill !== "forwards" && trackValue.fill !== "both") return null;
2358
2214
  return {
2359
- duration: total,
2360
- clips
2215
+ target: trackValue.target,
2216
+ channel: trackValue.channel,
2217
+ value: last.value
2361
2218
  };
2362
2219
  }
2363
- const clips = [];
2364
- let duration = 0;
2365
- for (const [index, child] of input.children.entries()) {
2366
- const childOffset = input.type === "sequence" ? duration : input.type === "stagger" ? input.each * index : 0;
2367
- const nested = flatten(child, offset + childOffset, sequenceSeed, `${path}.${index}`);
2368
- clips.push(...nested.clips);
2369
- duration = input.type === "sequence" ? duration + nested.duration : Math.max(duration, childOffset + nested.duration);
2370
- }
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);
2371
2236
  return {
2372
- duration,
2373
- clips
2237
+ target: trackValue.target,
2238
+ channel: trackValue.channel,
2239
+ value: interpolate(left.value, right.value, progress)
2374
2240
  };
2375
2241
  }
2376
- function compileSequence(input) {
2377
- const flattened = flatten(input.timeline, 0, input.seed, "root");
2378
- flattened.clips.sort((left, right) => left.start - right.start || left.id.localeCompare(right.id));
2379
- const refs = /* @__PURE__ */ new Map();
2380
- 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) {
2381
2262
  return {
2382
- sequence: {
2383
- id: input.id,
2384
- name: input.name,
2385
- duration: flattened.duration,
2386
- seed: input.seed,
2387
- restPose: input.restPose ?? {},
2388
- metadata: input.metadata ?? {}
2263
+ ...capability,
2264
+ validate() {
2265
+ return [];
2389
2266
  },
2390
- clips: flattened.clips,
2391
- assetRefs: [...refs.values()]
2267
+ estimateCost(params) {
2268
+ return estimateBuiltinBoardClipCost({
2269
+ kind: capability.id,
2270
+ params
2271
+ });
2272
+ }
2392
2273
  };
2393
2274
  }
2394
- function addCost(target, source) {
2395
- for (const key of Object.keys(target)) target[key] += source[key] ?? 0;
2396
- }
2397
2275
  var BoardExtensionRegistry = class {
2398
2276
  #extensions = /* @__PURE__ */ new Map();
2399
2277
  #presets = /* @__PURE__ */ new Map();
2278
+ constructor() {
2279
+ for (const capability of BOARD_BUILTIN_CAPABILITIES) this.register(builtinDefinition(capability));
2280
+ }
2400
2281
  register(definition) {
2401
2282
  const key = `${definition.kind}:${definition.id}@${definition.version}`;
2402
2283
  const target = definition.kind === "preset" ? this.#presets : this.#extensions;
@@ -2405,9 +2286,15 @@ var BoardExtensionRegistry = class {
2405
2286
  return this;
2406
2287
  }
2407
2288
  capabilities() {
2408
- const extensions = [...this.#extensions.values()].map(({ validate: _validate, getBounds: _bounds, getAssetRefs: _refs, estimateCost: _cost, ...definition }) => definition);
2409
- const presets = [...this.#presets.values()].map(({ compile: _compile, ...definition }) => definition);
2410
- 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
+ }));
2411
2298
  }
2412
2299
  compilePreset(id, version, params) {
2413
2300
  const preset = this.#presets.get(`preset:${id}@${version}`);
@@ -2415,74 +2302,72 @@ var BoardExtensionRegistry = class {
2415
2302
  return preset.compile(params);
2416
2303
  }
2417
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
+ };
2418
2316
  const diagnostics = [];
2317
+ const composition = parsed.data;
2318
+ const cost = { ...ZERO_COST };
2419
2319
  const events = [];
2420
- const profile = input.profile ?? "high";
2421
- const persistentCost = { ...ZERO_COST };
2422
- for (const [index, effect] of (input.effects ?? []).entries()) {
2423
- const definition = this.#extensions.get(`effect:${effect.kind}@${effect.kindVersion}`);
2424
- if (!definition) {
2425
- diagnostics.push({
2426
- severity: "warning",
2427
- code: "UNKNOWN_EFFECT",
2428
- message: `No renderer is registered for ${effect.kind}@${effect.kindVersion}`,
2429
- path: `effects.${index}`
2430
- });
2431
- continue;
2432
- }
2433
- diagnostics.push(...definition.validate?.(effect.params) ?? []);
2434
- addCost(persistentCost, definition.estimateCost(effect.params, profile));
2435
- }
2436
- for (const [index, clip] of input.clips.entries()) {
2320
+ for (const clip of composition.timeline.clips) {
2437
2321
  const definition = this.#extensions.get(`clip:${clip.kind}@${clip.kindVersion}`);
2438
2322
  if (!definition) {
2439
2323
  diagnostics.push({
2440
2324
  severity: "warning",
2441
2325
  code: "UNKNOWN_CLIP",
2442
- message: `No renderer is registered for ${clip.kind}@${clip.kindVersion}`,
2443
- path: `clips.${index}`
2326
+ message: `No renderer is registered for ${clip.kind}@${clip.kindVersion}`
2444
2327
  });
2445
2328
  continue;
2446
2329
  }
2330
+ diagnostics.push(...validateBuiltinBoardClip(clip, `composition.timeline.clips.${clip.id}`));
2447
2331
  diagnostics.push(...definition.validate?.(clip.params) ?? []);
2448
- const cost = { ...ZERO_COST };
2449
- 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;
2450
2335
  events.push({
2451
2336
  at: clip.start,
2452
2337
  direction: 1,
2453
- cost
2454
- }, {
2338
+ cost: clipCost
2339
+ });
2340
+ events.push({
2455
2341
  at: clip.start + clip.duration,
2456
2342
  direction: -1,
2457
- cost
2343
+ cost: clipCost
2458
2344
  });
2459
2345
  }
2460
2346
  events.sort((left, right) => left.at - right.at || left.direction - right.direction);
2461
- const current = { ...persistentCost };
2462
- const peak = { ...persistentCost };
2463
- for (const event of events) for (const key of Object.keys(current)) {
2464
- current[key] += event.cost[key] * event.direction;
2465
- 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]);
2466
2351
  }
2352
+ for (const effect of input.effects ?? []) if (effect.kind === "effects.pulse" || effect.kind === "effects.float") cost.drawCalls += 1;
2467
2353
  const limits = input.limits ?? DEFAULT_BOARD_LIMITS;
2468
- 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({
2469
2355
  severity: "warning",
2470
2356
  code: "RENDER_BUDGET_EXCEEDED",
2471
- message: `${key} peaks at ${peak[key]}, above ${limits[key]}`,
2357
+ message: `${key} peaks at ${cost[key]}, above ${limits[key]}`,
2472
2358
  path: key,
2473
2359
  adaptation: { quality: "lower" }
2474
2360
  });
2475
2361
  return {
2476
2362
  valid: !diagnostics.some((item) => item.severity === "error"),
2477
2363
  diagnostics,
2478
- peakCost: peak
2364
+ peakCost: cost
2479
2365
  };
2480
2366
  }
2481
2367
  };
2482
- function createBoardExtensionRegistry(input = {}) {
2483
- const registry = new BoardExtensionRegistry();
2484
- if (input.builtins !== false) for (const capability of BUILTIN_CAPABILITIES) registry.register(builtinDefinition(capability));
2485
- return registry;
2368
+ function createBoardExtensionRegistry() {
2369
+ return new BoardExtensionRegistry();
2486
2370
  }
2371
+ const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
2487
2372
  //#endregion
2488
- 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";