@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
@@ -1,7 +1,6 @@
1
1
  import { D as getRealtimeBoardRoom, O as getRealtimeSpaceRoom, n as HttpTransport, t as HttpError, y as isUuid } from "./transport.js";
2
2
  import { a as resolveApiBaseUrl } from "./environment.js";
3
3
  import { z } from "zod";
4
- import "perfect-freehand";
5
4
  //#region src/apis/channels.ts
6
5
  var ChannelsApi = class {
7
6
  transport;
@@ -454,7 +453,6 @@ const DEFAULT_BOARD_RENDER_LIMITS = {
454
453
  simulationSteps: 1e5
455
454
  };
456
455
  const BOARD_BUILTIN_CLIP_KINDS = [
457
- "motion.keyframes",
458
456
  "motion.path",
459
457
  "draw.reveal",
460
458
  "draw.handwrite",
@@ -463,9 +461,6 @@ const BOARD_BUILTIN_CLIP_KINDS = [
463
461
  "effects.trail",
464
462
  "effects.impact",
465
463
  "effects.flash",
466
- "effects.color",
467
- "camera.pan",
468
- "camera.zoom",
469
464
  "camera.focus",
470
465
  "camera.shake"
471
466
  ];
@@ -478,16 +473,6 @@ function clampBoardStrokeSize(size) {
478
473
  }
479
474
  function clipSchema(id) {
480
475
  switch (id) {
481
- case "motion.keyframes": return { params: {
482
- x: {
483
- coordinateSpace: "world-offset",
484
- unit: "board"
485
- },
486
- y: {
487
- coordinateSpace: "world-offset",
488
- unit: "board"
489
- }
490
- } };
491
476
  case "motion.path": return { params: { points: {
492
477
  coordinateSpace: "world-offset",
493
478
  unit: "board"
@@ -496,17 +481,6 @@ function clipSchema(id) {
496
481
  coordinateSpace: "world",
497
482
  unit: "board"
498
483
  } } };
499
- case "camera.pan": return { params: {
500
- x: {
501
- coordinateSpace: "screen-offset",
502
- unit: "css-px"
503
- },
504
- y: {
505
- coordinateSpace: "screen-offset",
506
- unit: "css-px"
507
- }
508
- } };
509
- case "camera.zoom": return { params: { scale: { unit: "ratio" } } };
510
484
  case "camera.focus": return { params: {
511
485
  focus: {
512
486
  coordinateSpace: "world",
@@ -693,13 +667,529 @@ function createBoardConnection(input) {
693
667
  };
694
668
  }
695
669
  //#endregion
696
- //#region src/realtime.ts
697
- function ensureRealtimeConnected(websocketClient) {
698
- if (websocketClient.state === "open" || websocketClient.state === "connecting" || websocketClient.state === "reconnecting") return;
699
- websocketClient.connect().catch((error) => {
700
- console.error("[CohubClient] Failed to connect realtime websocket:", error);
670
+ //#region ../protocol/dist/board-composition.js
671
+ const idSchema$3 = z.string().min(1).max(160);
672
+ const finiteSchema$3 = z.number().finite();
673
+ const jsonObjectSchema$2 = z.record(z.string(), z.unknown());
674
+ const BoardEasingSchema = z.enum([
675
+ "linear",
676
+ "ease-in-quad",
677
+ "ease-out-quad",
678
+ "ease-in-out-quad",
679
+ "ease-in-cubic",
680
+ "ease-out-cubic",
681
+ "ease-in-out-cubic",
682
+ "ease-out-quart",
683
+ "ease-out-expo"
684
+ ]);
685
+ const BoardAnimationTargetSchema = z.discriminatedUnion("type", [
686
+ z.object({
687
+ type: z.literal("item"),
688
+ itemId: idSchema$3
689
+ }).strict(),
690
+ z.object({
691
+ type: z.literal("effect"),
692
+ effectId: idSchema$3
693
+ }).strict(),
694
+ z.object({ type: z.literal("camera") }).strict(),
695
+ z.object({ type: z.literal("board") }).strict()
696
+ ]);
697
+ const vector2Schema = z.object({
698
+ x: finiteSchema$3,
699
+ y: finiteSchema$3
700
+ }).strict();
701
+ const scaleSchema = z.union([finiteSchema$3.positive(), z.object({
702
+ x: finiteSchema$3.positive(),
703
+ y: finiteSchema$3.positive()
704
+ }).strict()]);
705
+ const BOARD_ANIMATION_CHANNELS = {
706
+ "transform.translation": {
707
+ targets: ["item"],
708
+ value: vector2Schema,
709
+ interpolations: ["linear", "step"],
710
+ coordinateSpace: "world-offset",
711
+ unit: "board"
712
+ },
713
+ "transform.rotation": {
714
+ targets: ["item"],
715
+ value: finiteSchema$3,
716
+ interpolations: ["linear", "step"],
717
+ unit: "radian"
718
+ },
719
+ "transform.scale": {
720
+ targets: ["item"],
721
+ value: scaleSchema,
722
+ interpolations: ["linear", "step"],
723
+ unit: "ratio"
724
+ },
725
+ "style.opacity": {
726
+ targets: ["item"],
727
+ value: finiteSchema$3.min(0).max(1),
728
+ interpolations: ["linear", "step"],
729
+ unit: "ratio"
730
+ }
731
+ };
732
+ const BoardTrackKeyframeSchema = z.object({
733
+ time: finiteSchema$3.nonnegative(),
734
+ value: z.unknown(),
735
+ easing: BoardEasingSchema.optional()
736
+ }).strict();
737
+ const BoardTrackSchema = z.object({
738
+ id: idSchema$3,
739
+ target: BoardAnimationTargetSchema,
740
+ channel: z.string().min(1).max(160),
741
+ channelVersion: z.number().int().positive().default(1),
742
+ interpolation: z.enum(["linear", "step"]).default("linear"),
743
+ fill: z.enum([
744
+ "none",
745
+ "backwards",
746
+ "forwards",
747
+ "both"
748
+ ]).default("none"),
749
+ keyframes: z.array(BoardTrackKeyframeSchema).min(1).max(1e5),
750
+ metadata: jsonObjectSchema$2.default({})
751
+ }).strict().superRefine((track, context) => {
752
+ let previous = -1;
753
+ for (const [index, keyframe] of track.keyframes.entries()) {
754
+ if (keyframe.time <= previous) context.addIssue({
755
+ code: "custom",
756
+ message: "keyframe times must be strictly increasing",
757
+ path: [
758
+ "keyframes",
759
+ index,
760
+ "time"
761
+ ]
762
+ });
763
+ previous = keyframe.time;
764
+ }
765
+ const channel = BOARD_ANIMATION_CHANNELS[track.channel];
766
+ if (!channel) {
767
+ if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){2,}$/.test(track.channel)) context.addIssue({
768
+ code: "custom",
769
+ message: `unknown channel: ${track.channel}`,
770
+ path: ["channel"]
771
+ });
772
+ return;
773
+ }
774
+ if (!channel.targets.includes(track.target.type)) context.addIssue({
775
+ code: "custom",
776
+ message: `${track.channel} cannot target ${track.target.type}`,
777
+ path: ["target"]
778
+ });
779
+ if (!channel.interpolations.includes(track.interpolation)) context.addIssue({
780
+ code: "custom",
781
+ message: `${track.channel} does not support ${track.interpolation} interpolation`,
782
+ path: ["interpolation"]
783
+ });
784
+ for (const [index, keyframe] of track.keyframes.entries()) {
785
+ const parsed = channel.value.safeParse(keyframe.value);
786
+ if (!parsed.success) context.addIssue({
787
+ code: "custom",
788
+ message: parsed.error.issues[0]?.message ?? `invalid value for ${track.channel}`,
789
+ path: [
790
+ "keyframes",
791
+ index,
792
+ "value"
793
+ ]
794
+ });
795
+ }
796
+ });
797
+ const extensionKindSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
798
+ const BoardProceduralClipSchema = z.object({
799
+ id: idSchema$3,
800
+ kind: extensionKindSchema,
801
+ kindVersion: z.number().int().positive(),
802
+ target: BoardAnimationTargetSchema,
803
+ start: finiteSchema$3.nonnegative(),
804
+ duration: finiteSchema$3.positive(),
805
+ layer: z.enum([
806
+ "behind",
807
+ "content",
808
+ "front",
809
+ "screen"
810
+ ]).default("content"),
811
+ fill: z.enum([
812
+ "none",
813
+ "backwards",
814
+ "forwards",
815
+ "both"
816
+ ]).default("none"),
817
+ easing: BoardEasingSchema.default("linear"),
818
+ params: jsonObjectSchema$2.default({}),
819
+ assetRefs: z.array(z.object({
820
+ type: z.enum(["space-file", "extension"]),
821
+ ref: z.string().min(1).max(4096),
822
+ digest: z.string().min(16).max(160).optional()
823
+ }).strict()).default([]),
824
+ seed: idSchema$3,
825
+ metadata: jsonObjectSchema$2.default({})
826
+ }).strict();
827
+ const BoardTimelineMarkerSchema = z.object({
828
+ id: idSchema$3,
829
+ time: finiteSchema$3.nonnegative(),
830
+ duration: finiteSchema$3.nonnegative().optional(),
831
+ metadata: jsonObjectSchema$2.default({})
832
+ }).strict();
833
+ const BoardTimelineSchema = z.object({
834
+ duration: finiteSchema$3.nonnegative(),
835
+ tracks: z.array(BoardTrackSchema).max(5e4).default([]),
836
+ clips: z.array(BoardProceduralClipSchema).max(5e4).default([]),
837
+ markers: z.array(BoardTimelineMarkerSchema).max(1e4).default([])
838
+ }).strict().superRefine((timeline, context) => {
839
+ const ids = /* @__PURE__ */ new Set();
840
+ const channels = /* @__PURE__ */ new Set();
841
+ for (const [index, track] of timeline.tracks.entries()) {
842
+ if (ids.has(track.id)) context.addIssue({
843
+ code: "custom",
844
+ message: `duplicate timeline id: ${track.id}`,
845
+ path: [
846
+ "tracks",
847
+ index,
848
+ "id"
849
+ ]
850
+ });
851
+ ids.add(track.id);
852
+ const targetId = track.target.type === "item" ? track.target.itemId : track.target.type === "effect" ? track.target.effectId : track.target.type;
853
+ const key = `${track.target.type}:${targetId}:${track.channel}`;
854
+ if (channels.has(key)) context.addIssue({
855
+ code: "custom",
856
+ message: `multiple tracks control ${key}`,
857
+ path: [
858
+ "tracks",
859
+ index,
860
+ "channel"
861
+ ]
862
+ });
863
+ channels.add(key);
864
+ for (const [keyframeIndex, keyframe] of track.keyframes.entries()) if (keyframe.time > timeline.duration) context.addIssue({
865
+ code: "custom",
866
+ message: "keyframe exceeds timeline duration",
867
+ path: [
868
+ "tracks",
869
+ index,
870
+ "keyframes",
871
+ keyframeIndex,
872
+ "time"
873
+ ]
874
+ });
875
+ }
876
+ for (const [index, clip] of timeline.clips.entries()) {
877
+ if (ids.has(clip.id)) context.addIssue({
878
+ code: "custom",
879
+ message: `duplicate timeline id: ${clip.id}`,
880
+ path: [
881
+ "clips",
882
+ index,
883
+ "id"
884
+ ]
885
+ });
886
+ ids.add(clip.id);
887
+ if (clip.start + clip.duration > timeline.duration) context.addIssue({
888
+ code: "custom",
889
+ message: "clip exceeds timeline duration",
890
+ path: [
891
+ "clips",
892
+ index,
893
+ "duration"
894
+ ]
895
+ });
896
+ }
897
+ for (const [index, marker] of timeline.markers.entries()) {
898
+ if (ids.has(marker.id)) context.addIssue({
899
+ code: "custom",
900
+ message: `duplicate timeline id: ${marker.id}`,
901
+ path: [
902
+ "markers",
903
+ index,
904
+ "id"
905
+ ]
906
+ });
907
+ ids.add(marker.id);
908
+ if (marker.time + (marker.duration ?? 0) > timeline.duration) context.addIssue({
909
+ code: "custom",
910
+ message: "marker exceeds timeline duration",
911
+ path: ["markers", index]
912
+ });
913
+ }
914
+ });
915
+ const BoardCompositionPlaybackSchema = z.object({
916
+ loop: z.boolean().default(false),
917
+ endBehavior: z.enum(["hold", "reset"]).default("hold"),
918
+ reducedMotion: z.discriminatedUnion("mode", [
919
+ z.object({ mode: z.literal("base") }).strict(),
920
+ z.object({
921
+ mode: z.literal("time"),
922
+ time: finiteSchema$3.nonnegative()
923
+ }).strict(),
924
+ z.object({
925
+ mode: z.literal("marker"),
926
+ markerId: idSchema$3
927
+ }).strict()
928
+ ]).default({ mode: "base" })
929
+ }).strict();
930
+ const compositionFields = {
931
+ id: idSchema$3,
932
+ name: z.string().min(1).max(255),
933
+ timeline: BoardTimelineSchema,
934
+ playback: BoardCompositionPlaybackSchema.default({
935
+ loop: false,
936
+ endBehavior: "hold",
937
+ reducedMotion: { mode: "base" }
938
+ }),
939
+ metadata: jsonObjectSchema$2.default({})
940
+ };
941
+ function validateCompositionFallback(composition, context) {
942
+ const fallback = composition.playback.reducedMotion;
943
+ if (fallback.mode === "time" && fallback.time > composition.timeline.duration) context.addIssue({
944
+ code: "custom",
945
+ message: "reduced-motion time exceeds timeline duration",
946
+ path: [
947
+ "playback",
948
+ "reducedMotion",
949
+ "time"
950
+ ]
951
+ });
952
+ if (fallback.mode === "marker" && !composition.timeline.markers.some((marker) => marker.id === fallback.markerId)) context.addIssue({
953
+ code: "custom",
954
+ message: `marker does not exist: ${fallback.markerId}`,
955
+ path: [
956
+ "playback",
957
+ "reducedMotion",
958
+ "markerId"
959
+ ]
701
960
  });
702
961
  }
962
+ const BoardCompositionInputSchema = z.object(compositionFields).strict().superRefine(validateCompositionFallback);
963
+ const BoardCompositionSchema = z.object({
964
+ ...compositionFields,
965
+ revision: z.number().int().nonnegative().default(0)
966
+ }).strict().superRefine(validateCompositionFallback);
967
+ /** Accept inspect/get output as apply input while stripping server-owned revision. */
968
+ function parseBoardCompositionInput(value) {
969
+ if (value && typeof value === "object" && !Array.isArray(value)) {
970
+ const { revision: _revision, ...input } = value;
971
+ return BoardCompositionInputSchema.parse(input);
972
+ }
973
+ return BoardCompositionInputSchema.parse(value);
974
+ }
975
+ const BOARD_ANIMATION_CHANNEL_CAPABILITIES = Object.entries(BOARD_ANIMATION_CHANNELS).map(([id, definition]) => ({
976
+ id,
977
+ version: 1,
978
+ targets: definition.targets,
979
+ interpolations: definition.interpolations,
980
+ ..."coordinateSpace" in definition ? { coordinateSpace: definition.coordinateSpace } : {},
981
+ ..."unit" in definition ? { unit: definition.unit } : {},
982
+ valueSchema: z.toJSONSchema(definition.value)
983
+ }));
984
+ //#endregion
985
+ //#region ../protocol/dist/board-authoring.js
986
+ const BOARD_COLOR_IDS$1 = [
987
+ "brand",
988
+ "neutral",
989
+ "black",
990
+ "white",
991
+ "blue",
992
+ "green",
993
+ "amber",
994
+ "violet",
995
+ "rose"
996
+ ];
997
+ const BOARD_GEO_KINDS$1 = [
998
+ "rectangle",
999
+ "rounded",
1000
+ "ellipse",
1001
+ "diamond",
1002
+ "triangle"
1003
+ ];
1004
+ const idSchema$2 = z.string().min(1).max(160);
1005
+ const jsonObjectSchema$1 = z.record(z.string(), z.unknown());
1006
+ const finiteSchema$2 = z.number().finite();
1007
+ const extensionTypeSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
1008
+ const BoardAuthoringFrameSchema = z.object({
1009
+ x: finiteSchema$2,
1010
+ y: finiteSchema$2,
1011
+ width: finiteSchema$2.positive(),
1012
+ height: finiteSchema$2.positive(),
1013
+ rotation: finiteSchema$2.default(0)
1014
+ }).strict();
1015
+ const pointSchema$1 = z.object({
1016
+ x: finiteSchema$2,
1017
+ y: finiteSchema$2,
1018
+ p: finiteSchema$2.min(0).max(1).default(.5)
1019
+ }).strict();
1020
+ const worldPointSchema$1 = z.object({
1021
+ x: finiteSchema$2,
1022
+ y: finiteSchema$2
1023
+ }).strict();
1024
+ const cropSchema = z.object({
1025
+ x: finiteSchema$2.min(0).max(1),
1026
+ y: finiteSchema$2.min(0).max(1),
1027
+ w: finiteSchema$2.min(0).max(1),
1028
+ h: finiteSchema$2.min(0).max(1)
1029
+ }).strict();
1030
+ const BoardItemStyleSchema$1 = z.object({
1031
+ color: z.enum(BOARD_COLOR_IDS$1).optional(),
1032
+ strokeWidth: finiteSchema$2.min(1).max(64).optional(),
1033
+ fillOpacity: finiteSchema$2.min(0).max(1).optional()
1034
+ }).strict();
1035
+ const sourceSnapshotSchema = z.record(z.string(), z.unknown());
1036
+ function isSafeBoardSourcePath(value) {
1037
+ return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !value.startsWith("\\") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
1038
+ }
1039
+ const BoardItemSourceSchema = z.object({
1040
+ kind: z.literal("space-file"),
1041
+ path: z.string().refine(isSafeBoardSourcePath, "source path must be a safe relative Board file path"),
1042
+ snapshot: sourceSnapshotSchema.optional()
1043
+ }).strict();
1044
+ const baseFields = {
1045
+ id: idSchema$2,
1046
+ frame: BoardAuthoringFrameSchema,
1047
+ parentId: idSchema$2.nullable().optional(),
1048
+ locked: z.boolean().optional(),
1049
+ metadata: jsonObjectSchema$1.optional()
1050
+ };
1051
+ const styledBaseFields = {
1052
+ ...baseFields,
1053
+ style: BoardItemStyleSchema$1.optional()
1054
+ };
1055
+ const BoardTextAuthoringItemSchema = z.object({
1056
+ ...styledBaseFields,
1057
+ type: z.literal("text"),
1058
+ props: z.object({
1059
+ text: z.string().default(""),
1060
+ fontSize: finiteSchema$2.min(2).max(512).default(24)
1061
+ }).strict()
1062
+ }).strict();
1063
+ const BoardGeoAuthoringItemSchema = z.object({
1064
+ ...styledBaseFields,
1065
+ type: z.literal("geo"),
1066
+ props: z.object({
1067
+ shape: z.enum(BOARD_GEO_KINDS$1).default("rectangle"),
1068
+ text: z.string().default("")
1069
+ }).strict()
1070
+ }).strict();
1071
+ const BoardDrawAuthoringItemSchema = z.object({
1072
+ ...styledBaseFields,
1073
+ type: z.literal("draw"),
1074
+ props: z.object({ points: z.array(pointSchema$1).min(1) }).strict()
1075
+ }).strict();
1076
+ const BoardArrowAuthoringItemSchema = z.object({
1077
+ ...styledBaseFields,
1078
+ type: z.literal("arrow"),
1079
+ props: z.object({
1080
+ start: worldPointSchema$1,
1081
+ end: worldPointSchema$1,
1082
+ bend: finiteSchema$2.min(-.85).max(.85).default(0),
1083
+ arrowStart: z.boolean().default(false),
1084
+ arrowEnd: z.boolean().default(true),
1085
+ label: z.string().default("")
1086
+ }).strict()
1087
+ }).strict();
1088
+ const BoardFrameAuthoringItemSchema = z.object({
1089
+ ...styledBaseFields,
1090
+ type: z.literal("frame"),
1091
+ props: z.object({ label: z.string().default("Frame") }).strict()
1092
+ }).strict();
1093
+ const fileBackedFields = {
1094
+ ...baseFields,
1095
+ style: z.object({}).strict().optional(),
1096
+ source: BoardItemSourceSchema
1097
+ };
1098
+ const builtinItemSchemas = [
1099
+ BoardTextAuthoringItemSchema,
1100
+ BoardGeoAuthoringItemSchema,
1101
+ BoardDrawAuthoringItemSchema,
1102
+ BoardArrowAuthoringItemSchema,
1103
+ BoardFrameAuthoringItemSchema,
1104
+ z.object({
1105
+ ...fileBackedFields,
1106
+ type: z.literal("image"),
1107
+ props: z.object({ crop: cropSchema.optional() }).strict()
1108
+ }).strict(),
1109
+ z.object({
1110
+ ...fileBackedFields,
1111
+ type: z.literal("video"),
1112
+ props: z.object({}).strict()
1113
+ }).strict(),
1114
+ z.object({
1115
+ ...fileBackedFields,
1116
+ type: z.literal("audio"),
1117
+ props: z.object({}).strict()
1118
+ }).strict(),
1119
+ z.object({
1120
+ ...fileBackedFields,
1121
+ type: z.literal("file"),
1122
+ props: z.object({}).strict()
1123
+ }).strict(),
1124
+ z.object({
1125
+ ...baseFields,
1126
+ type: z.literal("task"),
1127
+ props: z.object({
1128
+ taskRunId: z.string().min(1),
1129
+ snapshot: z.record(z.string(), z.unknown())
1130
+ }).strict(),
1131
+ style: z.object({}).strict().optional()
1132
+ }).strict()
1133
+ ];
1134
+ const BoardBuiltinAuthoringItemSchema = z.discriminatedUnion("type", builtinItemSchemas);
1135
+ const BoardExtensionAuthoringItemSchema = z.object({
1136
+ ...baseFields,
1137
+ type: extensionTypeSchema,
1138
+ kindVersion: z.number().int().positive(),
1139
+ props: jsonObjectSchema$1,
1140
+ style: jsonObjectSchema$1.optional(),
1141
+ source: z.object({
1142
+ kind: z.string().min(1).max(80),
1143
+ ref: z.string().min(1).max(4096),
1144
+ snapshot: jsonObjectSchema$1.optional()
1145
+ }).strict().optional()
1146
+ }).strict();
1147
+ const BoardAuthoringItemSchema = z.union([BoardBuiltinAuthoringItemSchema, BoardExtensionAuthoringItemSchema]);
1148
+ const framePatchSchema = z.object({
1149
+ x: finiteSchema$2.optional(),
1150
+ y: finiteSchema$2.optional(),
1151
+ width: finiteSchema$2.positive().optional(),
1152
+ height: finiteSchema$2.positive().optional(),
1153
+ rotation: finiteSchema$2.optional()
1154
+ }).strict();
1155
+ /** JSON Merge Patch semantics, constrained to the stable Item envelope. */
1156
+ const BoardItemPatchSchema = z.object({
1157
+ frame: framePatchSchema.optional(),
1158
+ parentId: idSchema$2.nullable().optional(),
1159
+ locked: z.boolean().nullable().optional(),
1160
+ props: jsonObjectSchema$1.optional(),
1161
+ style: jsonObjectSchema$1.nullable().optional(),
1162
+ source: jsonObjectSchema$1.nullable().optional(),
1163
+ metadata: jsonObjectSchema$1.nullable().optional()
1164
+ }).strict().refine((patch) => Object.keys(patch).length > 0, "item patch is empty");
1165
+ const BoardSemanticCommandSchema = z.discriminatedUnion("type", [
1166
+ z.object({
1167
+ type: z.literal("item.create"),
1168
+ item: BoardAuthoringItemSchema
1169
+ }).strict(),
1170
+ z.object({
1171
+ type: z.literal("item.patch"),
1172
+ itemId: idSchema$2,
1173
+ patch: BoardItemPatchSchema
1174
+ }).strict(),
1175
+ z.object({
1176
+ type: z.literal("item.replace"),
1177
+ itemId: idSchema$2,
1178
+ item: BoardAuthoringItemSchema
1179
+ }).strict(),
1180
+ z.object({
1181
+ type: z.literal("item.delete"),
1182
+ itemId: idSchema$2,
1183
+ cascade: z.boolean().default(false)
1184
+ }).strict()
1185
+ ]);
1186
+ z.object({
1187
+ mutationId: idSchema$2,
1188
+ baseVersion: z.number().int().nonnegative(),
1189
+ clientId: idSchema$2.optional(),
1190
+ undoGroupId: idSchema$2.optional(),
1191
+ commands: z.array(BoardSemanticCommandSchema).min(1).max(5e4)
1192
+ }).strict();
703
1193
  //#endregion
704
1194
  //#region ../protocol/dist/board.js
705
1195
  const BOARD_DOCUMENT_KIND = "cohub.board";
@@ -714,18 +1204,6 @@ z.object({
714
1204
  boardId: z.string().uuid(),
715
1205
  title: z.string().min(1).max(255)
716
1206
  });
717
- const BoardTargetSchema = z.discriminatedUnion("type", [
718
- z.object({
719
- type: z.literal("node"),
720
- nodeId: idSchema$1
721
- }),
722
- z.object({
723
- type: z.literal("effect"),
724
- effectId: idSchema$1
725
- }),
726
- z.object({ type: z.literal("board") }),
727
- z.object({ type: z.literal("camera") })
728
- ]);
729
1207
  z.object({
730
1208
  centerX: finiteSchema$1,
731
1209
  centerY: finiteSchema$1,
@@ -742,12 +1220,12 @@ const BoardCameraFocusSchema = z.discriminatedUnion("type", [
742
1220
  })
743
1221
  }),
744
1222
  z.object({
745
- type: z.literal("node"),
746
- nodeId: idSchema$1
1223
+ type: z.literal("item"),
1224
+ itemId: idSchema$1
747
1225
  }),
748
1226
  z.object({
749
- type: z.literal("nodes"),
750
- nodeIds: z.array(idSchema$1).min(1).max(1e3)
1227
+ type: z.literal("items"),
1228
+ itemIds: z.array(idSchema$1).min(1).max(1e3)
751
1229
  }),
752
1230
  z.object({
753
1231
  type: z.literal("frame"),
@@ -772,45 +1250,13 @@ const BoardAssetRefSchema = z.object({
772
1250
  ref: z.string().min(1).max(4096),
773
1251
  digest: z.string().min(16).max(160).optional()
774
1252
  });
775
- const BoardKeyframeSchema = z.object({
776
- at: finiteSchema$1.nonnegative(),
777
- value: z.unknown(),
778
- easing: z.string().min(1).max(80).optional()
779
- });
780
- const BoardClipSchema = z.object({
781
- id: idSchema$1,
782
- sequenceId: idSchema$1,
783
- kind: extensionIdSchema,
784
- kindVersion: z.number().int().positive(),
785
- target: BoardTargetSchema,
786
- start: finiteSchema$1.nonnegative(),
787
- duration: finiteSchema$1.positive(),
788
- layer: z.enum([
789
- "behind",
790
- "content",
791
- "front",
792
- "screen"
793
- ]).default("content"),
794
- fill: z.enum([
795
- "none",
796
- "backwards",
797
- "forwards",
798
- "both"
799
- ]).default("none"),
800
- easing: z.string().min(1).max(80).default("linear"),
801
- params: jsonObjectSchema.default({}),
802
- keyframes: z.array(BoardKeyframeSchema).default([]),
803
- assetRefs: z.array(BoardAssetRefSchema).default([]),
804
- seed: z.string().min(1).max(160),
805
- metadata: jsonObjectSchema.default({})
806
- });
807
1253
  const BoardEffectSchema = z.object({
808
1254
  id: idSchema$1,
809
1255
  boardId: z.string().uuid(),
810
1256
  target: z.discriminatedUnion("type", [z.object({
811
- type: z.literal("node"),
812
- nodeId: idSchema$1
813
- }), z.object({ type: z.literal("board") })]),
1257
+ type: z.literal("item"),
1258
+ itemId: idSchema$1
1259
+ }).strict(), z.object({ type: z.literal("board") }).strict()]),
814
1260
  kind: extensionIdSchema,
815
1261
  kindVersion: z.number().int().positive(),
816
1262
  enabled: z.boolean().default(true),
@@ -835,17 +1281,7 @@ const BoardEffectSchema = z.object({
835
1281
  metadata: jsonObjectSchema.default({}),
836
1282
  revision: z.number().int().nonnegative()
837
1283
  });
838
- const BoardSequenceSchema = z.object({
839
- id: idSchema$1,
840
- boardId: z.string().uuid(),
841
- name: z.string().min(1).max(255),
842
- duration: finiteSchema$1.nonnegative(),
843
- seed: z.string().min(1).max(160),
844
- restPose: jsonObjectSchema.default({}),
845
- metadata: jsonObjectSchema.default({}),
846
- revision: z.number().int().nonnegative()
847
- });
848
- const BoardNodeInputSchema = z.object({
1284
+ z.object({
849
1285
  nodeId: idSchema$1,
850
1286
  type: z.string().min(1).max(40),
851
1287
  parentId: idSchema$1.nullable(),
@@ -868,27 +1304,20 @@ z.object({
868
1304
  mutationId: z.string().max(128).optional(),
869
1305
  title: z.string().min(1).max(255).optional(),
870
1306
  metadata: jsonObjectSchema.optional(),
871
- nodes: z.array(BoardNodeInputSchema).max(5e4).optional(),
1307
+ items: z.array(BoardAuthoringItemSchema).max(5e4).optional(),
872
1308
  connections: z.array(BoardConnectionSchema).max(5e4).optional(),
873
1309
  effects: z.array(BoardEffectSchema.omit({
874
1310
  boardId: true,
875
1311
  revision: true
876
1312
  })).optional(),
877
- sequences: z.array(z.object({
878
- sequence: BoardSequenceSchema.omit({
879
- boardId: true,
880
- revision: true
881
- }),
882
- clips: z.array(BoardClipSchema.omit({ sequenceId: true }))
883
- })).optional()
1313
+ compositions: z.array(BoardCompositionInputSchema).optional()
884
1314
  });
885
1315
  z.object({
886
1316
  include: z.array(z.enum([
887
1317
  "nodes",
888
1318
  "connections",
889
1319
  "effects",
890
- "sequences",
891
- "clips",
1320
+ "compositions",
892
1321
  "playback"
893
1322
  ])).optional(),
894
1323
  viewport: z.object({
@@ -900,10 +1329,9 @@ z.object({
900
1329
  });
901
1330
  /** Persisted on `boards.metadata.playback`: how a Board plays when opened. */
902
1331
  const BoardPlaybackPolicySchema = z.object({
903
- sequenceId: idSchema$1,
1332
+ compositionId: idSchema$1,
904
1333
  /** Delay before the first local playback after opening the Board, in milliseconds. */
905
- delayMs: finiteSchema$1.nonnegative().default(0),
906
- loop: z.boolean().default(false)
1334
+ delayMs: finiteSchema$1.nonnegative().default(0)
907
1335
  });
908
1336
  function parseBoardPlaybackPolicy(metadata) {
909
1337
  const parsed = BoardPlaybackPolicySchema.safeParse(metadata.playback);
@@ -913,7 +1341,7 @@ z.discriminatedUnion("type", [
913
1341
  z.object({
914
1342
  commandId: idSchema$1,
915
1343
  type: z.literal("play"),
916
- sequenceId: idSchema$1,
1344
+ compositionId: idSchema$1,
917
1345
  position: finiteSchema$1.nonnegative().optional(),
918
1346
  timeScale: finiteSchema$1.positive().max(4).optional(),
919
1347
  shared: z.boolean().optional(),
@@ -936,27 +1364,77 @@ z.discriminatedUnion("type", [
936
1364
  playbackId: z.string().uuid()
937
1365
  })
938
1366
  ]);
939
- const BoardContentKindSchema = z.enum([
940
- "text",
941
- "image",
942
- "video",
943
- "audio",
944
- "file",
945
- "json",
946
- "collection"
947
- ]);
948
- const BoardPortSchema = z.object({
949
- id: z.string().min(1).max(120),
950
- kind: BoardContentKindSchema,
951
- role: z.string().min(1).max(80).optional(),
952
- required: z.boolean().optional(),
953
- multiple: z.boolean().optional(),
954
- maxItems: z.number().int().positive().optional()
955
- }).strict();
956
- z.object({
957
- inputs: z.array(BoardPortSchema),
958
- outputs: z.array(BoardPortSchema)
959
- }).strict();
1367
+ //#endregion
1368
+ //#region ../protocol/dist/board-capability-registry.js
1369
+ const CLIPS = new Set(BOARD_BUILTIN_CLIP_KINDS);
1370
+ new Set(BOARD_BUILTIN_EFFECT_KINDS);
1371
+ const record = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
1372
+ const finite = (value) => typeof value === "number" && Number.isFinite(value);
1373
+ function validateBuiltinBoardClip(clip, path = "clip") {
1374
+ if (!CLIPS.has(clip.kind)) return [];
1375
+ const errors = [];
1376
+ const error = (message, suffix, coordinateSpace, code = "INVALID_BOARD_CLIP") => {
1377
+ errors.push({
1378
+ severity: "error",
1379
+ code,
1380
+ message,
1381
+ path: `${path}.${suffix}`,
1382
+ ...coordinateSpace ? { coordinateSpace } : {}
1383
+ });
1384
+ };
1385
+ if ((clip.kind.startsWith("motion.") || clip.kind.startsWith("draw.") || clip.kind === "text.reveal" || clip.kind === "effects.trail") && clip.target.type !== "item") error(`${clip.kind} must target an item`, "target");
1386
+ if (clip.kind.startsWith("camera.") && clip.target.type !== "camera") error(`${clip.kind} must target the camera`, "target");
1387
+ if (clip.kind === "motion.path") {
1388
+ const points = clip.params.points;
1389
+ if (!Array.isArray(points) || points.length < 2 || points.length > 1e4) error("motion path must contain 2 to 10000 world-offset points", "params.points", "world-offset");
1390
+ else for (const [index, point] of points.entries()) if (!record(point) || !finite(point.x) || !finite(point.y)) error("motion path point must contain finite x and y", `params.points.${index}`, "world-offset");
1391
+ }
1392
+ if (clip.kind === "effects.particles") {
1393
+ const count = clip.params.count;
1394
+ if (!Number.isSafeInteger(count) || count < 1 || count > DEFAULT_BOARD_RENDER_LIMITS.particles) error(`particle count must be an integer from 1 to ${DEFAULT_BOARD_RENDER_LIMITS.particles}`, "params.count", void 0, "INVALID_PARTICLE_COUNT");
1395
+ const bounds = clip.params.bounds;
1396
+ if (!record(bounds) || !finite(bounds.x) || !finite(bounds.y) || !finite(bounds.width) || !finite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) error("particles require positive finite world bounds", "params.bounds", "world", "PARTICLE_BOUNDS_REQUIRED");
1397
+ }
1398
+ if (clip.kind === "camera.focus") {
1399
+ const parsed = BoardCameraFocusParamsSchema.safeParse(clip.params);
1400
+ if (!parsed.success) error(parsed.error.issues[0]?.message ?? "invalid camera focus", "params", "world");
1401
+ }
1402
+ return errors;
1403
+ }
1404
+ function estimateBuiltinBoardClipCost(clip) {
1405
+ switch (clip.kind) {
1406
+ case "effects.particles": {
1407
+ const count = Number.isSafeInteger(clip.params.count) ? Math.max(0, clip.params.count) : 0;
1408
+ return {
1409
+ particles: count,
1410
+ vertices: count * 4,
1411
+ dynamicVertices: count * 4,
1412
+ drawCalls: 1,
1413
+ bufferBytes: count * 48,
1414
+ simulationSteps: count
1415
+ };
1416
+ }
1417
+ case "effects.trail": return {
1418
+ vertices: 32,
1419
+ dynamicVertices: 32,
1420
+ drawCalls: 1,
1421
+ bufferBytes: 1024,
1422
+ simulationSteps: 16
1423
+ };
1424
+ case "effects.impact":
1425
+ case "effects.flash": return {
1426
+ vertices: 64,
1427
+ drawCalls: 1
1428
+ };
1429
+ case "draw.reveal":
1430
+ case "draw.handwrite": return {
1431
+ drawCalls: 1,
1432
+ dynamicVertices: 1
1433
+ };
1434
+ case "motion.path": return { simulationSteps: Array.isArray(clip.params.points) ? clip.params.points.length : 0 };
1435
+ default: return {};
1436
+ }
1437
+ }
960
1438
  //#endregion
961
1439
  //#region ../protocol/dist/board-url.js
962
1440
  const BOARD_REMOTE_URL_MAX_LENGTH = 4096;
@@ -1687,6 +2165,27 @@ function validateBoardNodeInput(node, path = "node") {
1687
2165
  }
1688
2166
  return [];
1689
2167
  }
2168
+ const BoardContentKindSchema = z.enum([
2169
+ "text",
2170
+ "image",
2171
+ "video",
2172
+ "audio",
2173
+ "file",
2174
+ "json",
2175
+ "collection"
2176
+ ]);
2177
+ const BoardPortSchema = z.object({
2178
+ id: z.string().min(1).max(120),
2179
+ kind: BoardContentKindSchema,
2180
+ role: z.string().min(1).max(80).optional(),
2181
+ required: z.boolean().optional(),
2182
+ multiple: z.boolean().optional(),
2183
+ maxItems: z.number().int().positive().optional()
2184
+ }).strict();
2185
+ z.object({
2186
+ inputs: z.array(BoardPortSchema),
2187
+ outputs: z.array(BoardPortSchema)
2188
+ }).strict();
1690
2189
  //#endregion
1691
2190
  //#region ../protocol/dist/realtime/board-awareness.js
1692
2191
  const idSchema = z.string().min(1).max(160);
@@ -2147,186 +2646,12 @@ const buildWorkComposerChipClear = (key) => ({
2147
2646
  key
2148
2647
  });
2149
2648
  //#endregion
2150
- //#region src/board/core/draw-geometry.ts
2151
- /** Radius of a sample in world units given the stroke size and pressure. */
2152
- function sampleRadius(size, pressure) {
2153
- const clamped = Math.min(1, Math.max(0, pressure));
2154
- return Math.max(.5, size / 2 * (.5 + clamped));
2155
- }
2156
- /** Axis-aligned bounds of a stroke in its local space, padded by stroke width. */
2157
- function computeDrawBounds(points, size) {
2158
- if (points.length === 0) return {
2159
- x: 0,
2160
- y: 0,
2161
- width: 1,
2162
- height: 1
2163
- };
2164
- let minX = Number.POSITIVE_INFINITY;
2165
- let minY = Number.POSITIVE_INFINITY;
2166
- let maxX = Number.NEGATIVE_INFINITY;
2167
- let maxY = Number.NEGATIVE_INFINITY;
2168
- for (const point of points) {
2169
- const r = sampleRadius(size, point.p);
2170
- minX = Math.min(minX, point.x - r);
2171
- minY = Math.min(minY, point.y - r);
2172
- maxX = Math.max(maxX, point.x + r);
2173
- maxY = Math.max(maxY, point.y + r);
2174
- }
2175
- return {
2176
- x: minX,
2177
- y: minY,
2178
- width: Math.max(1, maxX - minX),
2179
- height: Math.max(1, maxY - minY)
2180
- };
2181
- }
2182
- //#endregion
2183
- //#region src/board/nodes.ts
2184
- var BoardInputError = class extends Error {
2185
- code = "INVALID_BOARD_NODE";
2186
- diagnostics;
2187
- body;
2188
- constructor(diagnostics) {
2189
- const message = diagnostics[0]?.message ?? "Invalid Board node";
2190
- super(message);
2191
- this.name = "BoardInputError";
2192
- this.diagnostics = diagnostics;
2193
- this.body = {
2194
- code: this.code,
2195
- message,
2196
- diagnostics
2197
- };
2198
- }
2199
- };
2200
- function baseNode(spec, frame) {
2201
- return {
2202
- nodeId: spec.id,
2203
- type: spec.type,
2204
- parentId: spec.parentId ?? null,
2205
- orderKey: spec.orderKey ?? null,
2206
- x: frame.x,
2207
- y: frame.y,
2208
- width: frame.width,
2209
- height: frame.height,
2210
- rotation: frame.rotation ?? 0,
2211
- refKind: null,
2212
- refPath: null,
2213
- refUrl: null,
2214
- view: {},
2215
- style: spec.style ?? {},
2216
- data: {}
2217
- };
2218
- }
2219
- function arrowFrame(start, end) {
2220
- const padding = 16;
2221
- return {
2222
- x: Math.min(start.x, end.x) - padding,
2223
- y: Math.min(start.y, end.y) - padding,
2224
- width: Math.max(1, Math.abs(end.x - start.x) + padding * 2),
2225
- height: Math.max(1, Math.abs(end.y - start.y) + padding * 2)
2226
- };
2227
- }
2228
- /**
2229
- * Create one validated wire node from semantic Board input.
2230
- *
2231
- * Box nodes take an explicit world-space frame. Draw samples and arrow endpoints
2232
- * take world coordinates; the builder derives their frame and local storage form.
2233
- */
2234
- function createBoardNode(spec) {
2235
- let node;
2236
- if (spec.type === "draw") {
2237
- const size = spec.size ?? 4;
2238
- const worldPoints = spec.points.map((point) => ({
2239
- x: point.x,
2240
- y: point.y,
2241
- p: point.p ?? .5
2242
- }));
2243
- const bounds = computeDrawBounds(worldPoints, size);
2244
- node = baseNode(spec, bounds);
2245
- node.data = {
2246
- points: worldPoints.map((point) => ({
2247
- x: point.x - bounds.x,
2248
- y: point.y - bounds.y,
2249
- p: point.p
2250
- })),
2251
- color: spec.color ?? "brand",
2252
- size
2253
- };
2254
- } else if (spec.type === "arrow") {
2255
- node = baseNode(spec, arrowFrame(spec.start, spec.end));
2256
- node.data = {
2257
- start: spec.start,
2258
- end: spec.end,
2259
- bend: spec.bend ?? 0,
2260
- color: spec.color ?? "brand",
2261
- size: spec.size ?? 2.5,
2262
- arrowStart: spec.arrowStart ?? false,
2263
- arrowEnd: spec.arrowEnd ?? true,
2264
- label: spec.label ?? ""
2265
- };
2266
- } else {
2267
- node = baseNode(spec, spec.frame);
2268
- switch (spec.type) {
2269
- case "text":
2270
- node.data = {
2271
- text: spec.text ?? "",
2272
- color: spec.color ?? "neutral",
2273
- fontSize: spec.fontSize ?? 24
2274
- };
2275
- break;
2276
- case "geo":
2277
- node.data = {
2278
- geo: spec.geo ?? "rectangle",
2279
- text: spec.text ?? "",
2280
- color: spec.color ?? "brand",
2281
- fillOpacity: spec.fillOpacity ?? 0
2282
- };
2283
- break;
2284
- case "frame":
2285
- node.data = {
2286
- label: spec.label ?? "Frame",
2287
- color: spec.color ?? "neutral"
2288
- };
2289
- break;
2290
- case "image":
2291
- node.refKind = "space_file";
2292
- node.refPath = spec.path;
2293
- node.view = spec.snapshot ?? {};
2294
- node.data = spec.crop ? { crop: spec.crop } : {};
2295
- break;
2296
- case "video":
2297
- case "audio":
2298
- node.refKind = "space_file";
2299
- node.refPath = spec.path;
2300
- node.view = spec.snapshot ?? {};
2301
- break;
2302
- case "file":
2303
- node.refKind = "space_file";
2304
- node.refPath = spec.path;
2305
- node.view = spec.snapshot ?? {};
2306
- break;
2307
- case "task":
2308
- node.view = spec.snapshot;
2309
- node.data = { taskRunId: spec.taskRunId };
2310
- break;
2311
- }
2312
- }
2313
- assertBoardNodes([node]);
2314
- return node;
2315
- }
2316
- function validateBoardNodes(nodes, path = "nodes") {
2317
- return nodes.flatMap((node, index) => validateBoardNodeInput(node, `${path}.${index}`));
2318
- }
2319
- function assertBoardNodes(nodes, path = "nodes") {
2320
- const diagnostics = validateBoardNodes(nodes, path);
2321
- if (diagnostics.length > 0) throw new BoardInputError(diagnostics);
2322
- }
2323
- function assertBoardTransactionNodeCreates(operations) {
2324
- const diagnostics = operations.flatMap((operation, index) => {
2325
- if (operation.type !== "node.create") return [];
2326
- const payload = operation.payload;
2327
- return payload.node ? validateBoardNodeInput(payload.node, `operations.${index}.payload.node`) : [];
2649
+ //#region src/realtime.ts
2650
+ function ensureRealtimeConnected(websocketClient) {
2651
+ if (websocketClient.state === "open" || websocketClient.state === "connecting" || websocketClient.state === "reconnecting") return;
2652
+ websocketClient.connect().catch((error) => {
2653
+ console.error("[CohubClient] Failed to connect realtime websocket:", error);
2328
2654
  });
2329
- if (diagnostics.length > 0) throw new BoardInputError(diagnostics);
2330
2655
  }
2331
2656
  //#endregion
2332
2657
  //#region src/session-patch-reducer.ts
@@ -4382,19 +4707,43 @@ var BoardClient = class {
4382
4707
  summary(customFetch) {
4383
4708
  return this.boards.summary(this.id, customFetch);
4384
4709
  }
4710
+ authoring(customFetch) {
4711
+ return this.boards.authoring(this.id, customFetch);
4712
+ }
4713
+ mutateSemantic(input) {
4714
+ return this.boards.mutateSemantic(this.id, {
4715
+ ...input,
4716
+ mutationId: input.mutationId ?? randomBoardId()
4717
+ });
4718
+ }
4385
4719
  async mutate(input) {
4386
4720
  const retries = input.retries ?? 1;
4387
4721
  if (!Number.isSafeInteger(retries) || retries < 0 || retries > 3) throw new RangeError("Board mutation retries must be an integer from 0 to 3");
4388
4722
  for (let attempt = 0;; attempt += 1) {
4389
4723
  const current = await this.inspect({ include: input.include ?? [] });
4390
4724
  const operations = await input.build(current);
4391
- if (operations.length === 0) return current;
4725
+ if (operations.length === 0) return {
4726
+ mutationId: randomBoardId(),
4727
+ status: "validated",
4728
+ replayed: false,
4729
+ board: {
4730
+ id: current.board.id,
4731
+ version: current.board.version
4732
+ },
4733
+ changed: {
4734
+ items: [],
4735
+ connections: [],
4736
+ effects: [],
4737
+ compositions: [],
4738
+ board: false
4739
+ }
4740
+ };
4392
4741
  try {
4393
4742
  return await this.apply({
4394
4743
  txId: randomBoardId(),
4395
4744
  baseVersion: current.board.version,
4396
4745
  operations
4397
- }, { compact: true });
4746
+ });
4398
4747
  } catch (cause) {
4399
4748
  if (!(cause instanceof BoardTransactionError) || !cause.isVersionConflict || attempt >= retries) throw cause;
4400
4749
  }
@@ -4406,11 +4755,11 @@ var BoardClient = class {
4406
4755
  boardId: this.id
4407
4756
  });
4408
4757
  }
4409
- apply(transaction, options) {
4758
+ apply(transaction) {
4410
4759
  return this.boards.apply({
4411
4760
  ...transaction,
4412
4761
  boardId: this.id
4413
- }, options);
4762
+ });
4414
4763
  }
4415
4764
  updateAwareness(seq, update) {
4416
4765
  if (!this.websocketClient) return Promise.resolve();
@@ -4540,7 +4889,7 @@ var SpaceBoardsApi = class {
4540
4889
  return new BoardClient(this.spaceId, boardId, this.transport, this.websocketClient);
4541
4890
  }
4542
4891
  create(input) {
4543
- assertBoardNodes(input.nodes ?? []);
4892
+ for (const item of input.items ?? []) BoardAuthoringItemSchema.parse(item);
4544
4893
  return this.transport.request(`/api/spaces/${this.spaceId}/boards`, {
4545
4894
  method: "POST",
4546
4895
  headers: { "Content-Type": "application/json" },
@@ -4554,6 +4903,16 @@ var SpaceBoardsApi = class {
4554
4903
  const query = params.toString();
4555
4904
  return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}${query ? `?${query}` : ""}`, { fetch: customFetch });
4556
4905
  }
4906
+ authoring(boardId, customFetch) {
4907
+ return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/authoring`, { fetch: customFetch });
4908
+ }
4909
+ mutateSemantic(boardId, mutation) {
4910
+ return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/mutations`, {
4911
+ method: "POST",
4912
+ headers: { "Content-Type": "application/json" },
4913
+ body: JSON.stringify(mutation)
4914
+ });
4915
+ }
4557
4916
  summary(boardId, customFetch) {
4558
4917
  return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/summary`, { fetch: customFetch });
4559
4918
  }
@@ -4567,10 +4926,9 @@ var SpaceBoardsApi = class {
4567
4926
  body: JSON.stringify(transaction)
4568
4927
  });
4569
4928
  }
4570
- async apply(transaction, options) {
4571
- assertBoardTransactionNodeCreates(transaction.operations);
4929
+ async apply(transaction) {
4572
4930
  try {
4573
- return await this.transport.request(`/api/spaces/${this.spaceId}/boards/${transaction.boardId}/transactions${options?.compact ? "?compact=1" : ""}`, {
4931
+ return await this.transport.request(`/api/spaces/${this.spaceId}/boards/${transaction.boardId}/transactions`, {
4574
4932
  method: "POST",
4575
4933
  headers: { "Content-Type": "application/json" },
4576
4934
  body: JSON.stringify(transaction)
@@ -5359,4 +5717,4 @@ var CohubHttpClient = class {
5359
5717
  };
5360
5718
  const createHttpClient = (options) => new CohubHttpClient(options);
5361
5719
  //#endregion
5362
- export { parseUsername as $, WORK_SURFACE_READY_TIMEOUT_MS as A, parseWorkSurfaceRequest as B, BoardInputError as C, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES as D, validateBoardNodes as E, buildWorkSurfaceRequest as F, UI_COMMAND_PENDING_TTL_SECONDS as G, UI_COMMAND_DEFAULT_TIMEOUT_MS as H, buildWorkSurfaceResponse as I, UI_COMMAND_VERSION as J, UI_COMMAND_SETTLEMENT_GRACE_SECONDS as K, parseWorkComposerChipClear as L, buildWorkComposerChipClear as M, buildWorkComposerChipSet as N, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH as O, buildWorkSurfaceReady as P, parseSpaceSlug as Q, parseWorkComposerChipSet as R, createSessionPatchReducer as S, createBoardNode as T, UI_COMMAND_MAX_TIMEOUT_MS as U, parseWorkSurfaceResponse as V, UI_COMMAND_PAYLOAD_MAX_BYTES as W, isUiSurfaceMethod as X, isTerminalUiCommandStatus as Y, parseUiCommand as Z, buildSpacePath as _, PromptsApi as _t, ReferralsApi as a, validateBoardNodeInput as at, parseAssistantMessageCommit as b, CronJobsApi as bt, UiCommandsApi as c, parseBoardPlaybackPolicy as ct, BoardTransactionError as d, DEFAULT_BOARD_RENDER_LIMITS as dt, BoardAwarenessClientPayloadSchema as et, SpaceClient as f, SessionAccessApi as ft, buildSpaceInvitePath as g, SkillsApi as gt, PublicInviteApi as h, PublicAssetsApi as ht, WorksApi as i, BOARD_NODE_CONTRACT as it, WORK_SURFACE_REQUEST_TIMEOUT_MS as j, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH as k, TasksApi as l, ensureRealtimeConnected as lt, SpacesApi as m, SearchApi as mt, createHttpClient as n, BOARD_GEO_KINDS as nt, UsersApi as o, BoardCameraFocusParamsSchema as ot, SpacePublicFilesApi as p, ReferencesApi as pt, UI_COMMAND_TERMINAL_TTL_SECONDS as q, WorkCommerceApi as r, BOARD_NATIVE_NODE_TYPES as rt, UserApi as s, BoardPlaybackPolicySchema as st, CohubHttpClient as t, BOARD_COLOR_IDS as tt, BoardClient as u, BOARD_BUILTIN_CAPABILITIES as ut, SessionGenerationStreamClient as v, ModelsApi as vt, assertBoardNodes as w, SessionPatchReducer as x, ChannelsApi as xt, createSessionGenerationStreamClient as y, GenerationsApi as yt, parseWorkSurfaceReady as z };
5720
+ export { BOARD_GEO_KINDS as $, buildWorkComposerChipSet as A, UI_COMMAND_MAX_TIMEOUT_MS as B, ensureRealtimeConnected as C, SkillsApi as Ct, WORK_SURFACE_READY_TIMEOUT_MS as D, CronJobsApi as Dt, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH as E, GenerationsApi as Et, parseWorkComposerChipSet as F, UI_COMMAND_VERSION as G, UI_COMMAND_PENDING_TTL_SECONDS as H, parseWorkSurfaceReady as I, parseUiCommand as J, isTerminalUiCommandStatus as K, parseWorkSurfaceRequest as L, buildWorkSurfaceRequest as M, buildWorkSurfaceResponse as N, WORK_SURFACE_REQUEST_TIMEOUT_MS as O, ChannelsApi as Ot, parseWorkComposerChipClear as P, BOARD_COLOR_IDS as Q, parseWorkSurfaceResponse as R, createSessionPatchReducer as S, PublicAssetsApi as St, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH as T, ModelsApi as Tt, UI_COMMAND_SETTLEMENT_GRACE_SECONDS as U, UI_COMMAND_PAYLOAD_MAX_BYTES as V, UI_COMMAND_TERMINAL_TTL_SECONDS as W, parseUsername as X, parseSpaceSlug as Y, BoardAwarenessClientPayloadSchema as Z, buildSpacePath as _, BOARD_BUILTIN_CAPABILITIES as _t, ReferralsApi as a, BoardEffectSchema as at, parseAssistantMessageCommit as b, ReferencesApi as bt, UiCommandsApi as c, BoardAuthoringItemSchema as ct, BoardTransactionError as d, BOARD_ANIMATION_CHANNEL_CAPABILITIES as dt, BOARD_NATIVE_NODE_TYPES as et, SpaceClient as f, BoardCompositionInputSchema as ft, buildSpaceInvitePath as g, parseBoardCompositionInput as gt, PublicInviteApi as h, BoardTrackSchema as ht, WorksApi as i, validateBuiltinBoardClip as it, buildWorkSurfaceReady as j, buildWorkComposerChipClear as k, TasksApi as l, BoardItemPatchSchema as lt, SpacesApi as m, BoardProceduralClipSchema as mt, createHttpClient as n, validateBoardNodeInput as nt, UsersApi as o, BoardPlaybackPolicySchema as ot, SpacePublicFilesApi as p, BoardCompositionSchema as pt, isUiSurfaceMethod as q, WorkCommerceApi as r, estimateBuiltinBoardClipCost as rt, UserApi as s, parseBoardPlaybackPolicy as st, CohubHttpClient as t, BOARD_NODE_CONTRACT as tt, BoardClient as u, BOARD_ANIMATION_CHANNELS as ut, SessionGenerationStreamClient as v, DEFAULT_BOARD_RENDER_LIMITS as vt, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES as w, PromptsApi as wt, SessionPatchReducer as x, SearchApi as xt, createSessionGenerationStreamClient as y, SessionAccessApi as yt, UI_COMMAND_DEFAULT_TIMEOUT_MS as z };