@nodaro/shared 2.26.0 → 2.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4133,7 +4133,12 @@ var COMPOSER_PLAN_MAP = {
4133
4133
  "lottie-overlay": { planType: "lottie-overlay", planField: "overlayPlan" },
4134
4134
  "3d-title": { planType: "3d-title", planField: "titlePlan" },
4135
4135
  "motion-graphics": { planType: "motion-graphics", planField: "motionPlan" },
4136
- "composite": { planType: "composite", planField: "compositePlan" }
4136
+ "composite": { planType: "composite", planField: "compositePlan" },
4137
+ // Scene3D previz (v1) — both the generator and the editor emit the SAME
4138
+ // validated `Scene3DPlan` revision on their `composition` handle, so
4139
+ // render-video routes either one to the `3d-scene` renderer unchanged.
4140
+ "generate-3d-scene": { planType: "3d-scene", planField: "scenePlan" },
4141
+ "edit-3d-scene": { planType: "3d-scene", planField: "scenePlan" }
4137
4142
  };
4138
4143
  var COMPOSER_PLAN_FIELDS = [
4139
4144
  ...new Set(Object.values(COMPOSER_PLAN_MAP).map((m) => m.planField))
@@ -5882,6 +5887,7 @@ var LLM_FEATURE_DEFAULTS = {
5882
5887
  "motion-graphics-lottie": "claude-sonnet-4.6",
5883
5888
  "lottie-overlay": "claude-sonnet-4.6",
5884
5889
  "3d-title": "claude-sonnet-4.6",
5890
+ "3d-scene": "claude-sonnet-4.6",
5885
5891
  "image-to-text": "claude-sonnet-4.6",
5886
5892
  "describe-to-picker": "claude-opus-5",
5887
5893
  "qa-check": "gemini-3.6-flash",
@@ -5982,7 +5988,10 @@ var LLM_ROUTE_DEFAULTS = {
5982
5988
  "lottie-overlay": { temperature: 0.3, maxTokens: 2048, structuredOutput: true },
5983
5989
  "motion-graphics": { temperature: 0.3, maxTokens: 2048, structuredOutput: true },
5984
5990
  "motion-graphics-lottie": { temperature: 0.3, maxTokens: 8192, structuredOutput: true },
5985
- "3d-title": { temperature: 0.4, maxTokens: 3072, structuredOutput: true }
5991
+ "3d-title": { temperature: 0.4, maxTokens: 3072, structuredOutput: true },
5992
+ // A 100-object plan with keyframe tracks is the biggest structured payload
5993
+ // any composer feature emits — 3072 (3d-title's cap) truncates it mid-array.
5994
+ "3d-scene": { temperature: 0.3, maxTokens: 8192, structuredOutput: true }
5986
5995
  };
5987
5996
  function llmRouteDefaults(feature) {
5988
5997
  return feature && LLM_ROUTE_DEFAULTS[feature] || {};
@@ -8765,6 +8774,8 @@ var NODE_MAPPABLE_FIELDS = {
8765
8774
  "after-effects": ["effectPrompt"],
8766
8775
  "lottie-overlay": ["overlayPrompt"],
8767
8776
  "3d-title": ["titlePrompt"],
8777
+ "generate-3d-scene": ["scenePrompt"],
8778
+ "edit-3d-scene": ["editPrompt"],
8768
8779
  "motion-graphics": ["motionPrompt"],
8769
8780
  "generate-script": ["styleGuide"],
8770
8781
  "speech-to-video": ["prompt", "negativePrompt"],
@@ -13142,8 +13153,22 @@ function readPromptAffixes(data) {
13142
13153
  }
13143
13154
 
13144
13155
  // src/node-preset-extract.ts
13145
- var PRESET_APPLY_CLEAR_KEYS = [...COMPOSER_PLAN_FIELDS, "lottieUrl"];
13146
- var PROMPT_CONTENT_KEYS = ["prompt", PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY];
13156
+ var PRESET_APPLY_CLEAR_KEYS = [
13157
+ ...COMPOSER_PLAN_FIELDS,
13158
+ "lottieUrl",
13159
+ // Scene revision history contains old plans and their reference/prompt context.
13160
+ // Preserve it in workflows, never capture or resurrect it through a preset.
13161
+ "sceneHistory",
13162
+ "scenePendingPlan",
13163
+ "sceneJobBaseRevisionId",
13164
+ "expectedRevisionId",
13165
+ "changeSummary",
13166
+ "selectedObjectIds",
13167
+ "lockedObjectIds",
13168
+ "referenceObjectIds",
13169
+ "referenceRoles"
13170
+ ];
13171
+ var PROMPT_CONTENT_KEYS = ["prompt", "scenePrompt", "editPrompt", PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY];
13147
13172
  function presetApplyClearKeys(presetData) {
13148
13173
  const ownsPromptContent = PROMPT_CONTENT_KEYS.some((k) => presetData[k] !== void 0);
13149
13174
  return ownsPromptContent ? [...PRESET_APPLY_CLEAR_KEYS, PROMPT_PREFIX_KEY, PROMPT_SUFFIX_KEY] : PRESET_APPLY_CLEAR_KEYS;
@@ -14678,6 +14703,489 @@ function entityHydrationColumns(kind) {
14678
14703
  function entityScalarFields(kind) {
14679
14704
  return [...ENTITY_SCALAR_FIELDS, ...ENTITY_KIND_SCALAR_FIELDS[kind]];
14680
14705
  }
14706
+ var SCENE3D_PLAN_TYPE = "3d-scene";
14707
+ var SCENE3D_SCHEMA_VERSION = 1;
14708
+ var SCENE3D_DEFAULT_FPS = 24;
14709
+ var SCENE3D_DEFAULT_DURATION_SECONDS = 4;
14710
+ var SCENE3D_LIMITS = {
14711
+ minDimensionPx: 100,
14712
+ maxDimensionPx: 1920,
14713
+ minFps: 15,
14714
+ maxFps: 60,
14715
+ minDurationInFrames: 1,
14716
+ maxDurationInFrames: 3600,
14717
+ /** Shortest scene an authoring request may ask for, in seconds. One second
14718
+ * is the frozen v1 floor the SDK/MCP surface and the public docs state; the
14719
+ * route Zod and the canvas path both quote it from here so they cannot
14720
+ * drift below it. */
14721
+ minDurationSeconds: 1,
14722
+ /** Hard ceiling on wall-clock length, checked against fps × frames. */
14723
+ maxDurationSeconds: 60,
14724
+ minObjects: 1,
14725
+ maxObjects: 100,
14726
+ /** Per-object and per-camera track length. */
14727
+ maxKeyframes: 240,
14728
+ maxReferences: 8,
14729
+ maxOperations: 100,
14730
+ /** |x|, |y|, |z| ceiling for positions and camera/target coordinates. */
14731
+ maxCoordinate: 1e3,
14732
+ minSize: 1e-3,
14733
+ maxSize: 1e3,
14734
+ minScale: 1e-3,
14735
+ maxScale: 1e3,
14736
+ minFocalLengthMm: 10,
14737
+ maxFocalLengthMm: 200,
14738
+ defaultSensorWidthMm: 36,
14739
+ minSensorWidthMm: 1,
14740
+ maxSensorWidthMm: 200,
14741
+ maxIntensity: 100,
14742
+ /** How deep a parent chain may nest. Bounds the renderer's transform walk. */
14743
+ maxHierarchyDepth: 8,
14744
+ maxIdLength: 64,
14745
+ maxNameLength: 120,
14746
+ maxUrlLength: 2048,
14747
+ maxChangeSummaryLength: 2e3
14748
+ };
14749
+ var SCENE3D_PRIMITIVES = [
14750
+ "box",
14751
+ "sphere",
14752
+ "cylinder",
14753
+ "cone",
14754
+ "plane",
14755
+ "capsule",
14756
+ "group"
14757
+ ];
14758
+ function boundedVec3(max, label) {
14759
+ return zod.z.tuple([
14760
+ zod.z.number().min(-max).max(max),
14761
+ zod.z.number().min(-max).max(max),
14762
+ zod.z.number().min(-max).max(max)
14763
+ ]).describe(label);
14764
+ }
14765
+ var vec3Schema = boundedVec3(SCENE3D_LIMITS.maxCoordinate, "position triple (meters)");
14766
+ var sizeVec3Schema = zod.z.tuple([
14767
+ zod.z.number().min(SCENE3D_LIMITS.minSize).max(SCENE3D_LIMITS.maxSize),
14768
+ zod.z.number().min(SCENE3D_LIMITS.minSize).max(SCENE3D_LIMITS.maxSize),
14769
+ zod.z.number().min(SCENE3D_LIMITS.minSize).max(SCENE3D_LIMITS.maxSize)
14770
+ ]);
14771
+ var scaleVec3Schema = zod.z.tuple([
14772
+ zod.z.number().min(SCENE3D_LIMITS.minScale).max(SCENE3D_LIMITS.maxScale),
14773
+ zod.z.number().min(SCENE3D_LIMITS.minScale).max(SCENE3D_LIMITS.maxScale),
14774
+ zod.z.number().min(SCENE3D_LIMITS.minScale).max(SCENE3D_LIMITS.maxScale)
14775
+ ]);
14776
+ var rotationVec3Schema = boundedVec3(1e3, "euler XYZ (radians)");
14777
+ var HEX_COLOR = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
14778
+ var scene3DColorSchema = zod.z.string().regex(HEX_COLOR, "color must be an opaque hex string such as #4f8ef7 (3 or 6 digits; alpha is not supported)");
14779
+ var SCENE3D_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
14780
+ var scene3DIdSchema = zod.z.string().min(1).max(SCENE3D_LIMITS.maxIdLength).regex(SCENE3D_ID, "id must start alphanumeric and contain only letters, digits, '_', '-' or '.'");
14781
+ function isScene3DHttpUrl(value) {
14782
+ try {
14783
+ const parsed = new URL(value);
14784
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
14785
+ } catch {
14786
+ return false;
14787
+ }
14788
+ }
14789
+ var scene3DUrlSchema = zod.z.string().min(1).max(SCENE3D_LIMITS.maxUrlLength).refine(isScene3DHttpUrl, "url must be an http(s) URL");
14790
+ var scene3DEasingSchema = zod.z.enum(["linear", "easeInOut"]);
14791
+ var scene3DPrimitiveSchema = zod.z.enum([
14792
+ "box",
14793
+ "sphere",
14794
+ "cylinder",
14795
+ "cone",
14796
+ "plane",
14797
+ "capsule",
14798
+ "group"
14799
+ ]);
14800
+ var frameSchema = zod.z.number().int().min(0).max(SCENE3D_LIMITS.maxDurationInFrames);
14801
+ var scene3DObjectKeyframeSchema = zod.z.object({
14802
+ frame: frameSchema,
14803
+ position: vec3Schema.optional(),
14804
+ rotation: rotationVec3Schema.optional(),
14805
+ scale: scaleVec3Schema.optional(),
14806
+ easing: scene3DEasingSchema.optional()
14807
+ }).strict();
14808
+ var scene3DCameraKeyframeSchema = zod.z.object({
14809
+ frame: frameSchema,
14810
+ position: vec3Schema.optional(),
14811
+ target: vec3Schema.optional(),
14812
+ focalLengthMm: zod.z.number().min(SCENE3D_LIMITS.minFocalLengthMm).max(SCENE3D_LIMITS.maxFocalLengthMm).optional(),
14813
+ easing: scene3DEasingSchema.optional()
14814
+ }).strict();
14815
+ var scene3DObjectSchema = zod.z.object({
14816
+ id: scene3DIdSchema,
14817
+ name: zod.z.string().min(1).max(SCENE3D_LIMITS.maxNameLength),
14818
+ primitive: scene3DPrimitiveSchema,
14819
+ parentId: scene3DIdSchema.optional(),
14820
+ dimensions: sizeVec3Schema,
14821
+ position: vec3Schema,
14822
+ rotation: rotationVec3Schema,
14823
+ scale: scaleVec3Schema,
14824
+ color: scene3DColorSchema,
14825
+ keyframes: zod.z.array(scene3DObjectKeyframeSchema).max(SCENE3D_LIMITS.maxKeyframes).optional()
14826
+ }).strict();
14827
+ var scene3DCameraSchema = zod.z.object({
14828
+ position: vec3Schema,
14829
+ target: vec3Schema,
14830
+ focalLengthMm: zod.z.number().min(SCENE3D_LIMITS.minFocalLengthMm).max(SCENE3D_LIMITS.maxFocalLengthMm),
14831
+ sensorWidthMm: zod.z.number().min(SCENE3D_LIMITS.minSensorWidthMm).max(SCENE3D_LIMITS.maxSensorWidthMm).default(SCENE3D_LIMITS.defaultSensorWidthMm),
14832
+ keyframes: zod.z.array(scene3DCameraKeyframeSchema).max(SCENE3D_LIMITS.maxKeyframes).optional()
14833
+ }).strict();
14834
+ var scene3DLightingSchema = zod.z.object({
14835
+ ambientIntensity: zod.z.number().min(0).max(SCENE3D_LIMITS.maxIntensity),
14836
+ keyIntensity: zod.z.number().min(0).max(SCENE3D_LIMITS.maxIntensity),
14837
+ keyPosition: vec3Schema
14838
+ }).strict();
14839
+ var scene3DReferenceSchema = zod.z.object({
14840
+ id: scene3DIdSchema,
14841
+ url: scene3DUrlSchema,
14842
+ kind: zod.z.enum(["image", "video"]),
14843
+ role: zod.z.enum(["appearance", "layout", "motion"]),
14844
+ objectId: scene3DIdSchema.optional(),
14845
+ startSeconds: zod.z.number().min(0).max(86400).optional(),
14846
+ endSeconds: zod.z.number().min(0).max(86400).optional()
14847
+ }).strict();
14848
+ function checkKeyframeTrack(frames, durationInFrames, path, issues) {
14849
+ let previous = -1;
14850
+ frames.forEach((kf, index) => {
14851
+ if (kf.frame > durationInFrames - 1) {
14852
+ issues.push({
14853
+ path: [...path, index, "frame"],
14854
+ message: `frame ${kf.frame} is past the scene's last frame (${durationInFrames - 1})`
14855
+ });
14856
+ }
14857
+ if (kf.frame === previous) {
14858
+ issues.push({ path: [...path, index, "frame"], message: `duplicate keyframe at frame ${kf.frame}` });
14859
+ } else if (kf.frame < previous) {
14860
+ issues.push({
14861
+ path: [...path, index, "frame"],
14862
+ message: `keyframes must be sorted by frame (${kf.frame} follows ${previous})`
14863
+ });
14864
+ }
14865
+ previous = kf.frame;
14866
+ });
14867
+ }
14868
+ function scene3DPlanIssues(plan) {
14869
+ const issues = [];
14870
+ const seconds = plan.durationInFrames / plan.fps;
14871
+ if (seconds > SCENE3D_LIMITS.maxDurationSeconds) {
14872
+ issues.push({
14873
+ path: ["durationInFrames"],
14874
+ message: `scene is ${seconds.toFixed(2)}s; the limit is ${SCENE3D_LIMITS.maxDurationSeconds}s`
14875
+ });
14876
+ }
14877
+ const byId = /* @__PURE__ */ new Map();
14878
+ plan.objects.forEach((object, index) => {
14879
+ if (byId.has(object.id)) {
14880
+ issues.push({ path: ["objects", index, "id"], message: `duplicate object id "${object.id}"` });
14881
+ return;
14882
+ }
14883
+ byId.set(object.id, object);
14884
+ });
14885
+ plan.objects.forEach((object, index) => {
14886
+ if (object.parentId === void 0) return;
14887
+ if (object.parentId === object.id) {
14888
+ issues.push({ path: ["objects", index, "parentId"], message: `object "${object.id}" cannot parent itself` });
14889
+ return;
14890
+ }
14891
+ if (!byId.has(object.parentId)) {
14892
+ issues.push({
14893
+ path: ["objects", index, "parentId"],
14894
+ message: `object "${object.id}" references unknown parent "${object.parentId}"`
14895
+ });
14896
+ return;
14897
+ }
14898
+ const seen = /* @__PURE__ */ new Set([object.id]);
14899
+ let cursor = byId.get(object.parentId);
14900
+ let depth = 1;
14901
+ while (cursor) {
14902
+ if (seen.has(cursor.id)) {
14903
+ issues.push({
14904
+ path: ["objects", index, "parentId"],
14905
+ message: `parent cycle through object "${cursor.id}"`
14906
+ });
14907
+ break;
14908
+ }
14909
+ seen.add(cursor.id);
14910
+ depth += 1;
14911
+ if (depth > SCENE3D_LIMITS.maxHierarchyDepth) {
14912
+ issues.push({
14913
+ path: ["objects", index, "parentId"],
14914
+ message: `hierarchy deeper than ${SCENE3D_LIMITS.maxHierarchyDepth} levels`
14915
+ });
14916
+ break;
14917
+ }
14918
+ cursor = cursor.parentId === void 0 ? void 0 : byId.get(cursor.parentId);
14919
+ }
14920
+ });
14921
+ plan.objects.forEach((object, index) => {
14922
+ if (object.keyframes && object.keyframes.length > 0) {
14923
+ checkKeyframeTrack(object.keyframes, plan.durationInFrames, ["objects", index, "keyframes"], issues);
14924
+ }
14925
+ });
14926
+ if (plan.camera.keyframes && plan.camera.keyframes.length > 0) {
14927
+ checkKeyframeTrack(plan.camera.keyframes, plan.durationInFrames, ["camera", "keyframes"], issues);
14928
+ }
14929
+ const referenceIds = /* @__PURE__ */ new Set();
14930
+ (plan.references ?? []).forEach((reference, index) => {
14931
+ if (referenceIds.has(reference.id)) {
14932
+ issues.push({ path: ["references", index, "id"], message: `duplicate reference id "${reference.id}"` });
14933
+ }
14934
+ referenceIds.add(reference.id);
14935
+ if (reference.objectId !== void 0 && !byId.has(reference.objectId)) {
14936
+ issues.push({
14937
+ path: ["references", index, "objectId"],
14938
+ message: `reference "${reference.id}" points at unknown object "${reference.objectId}"`
14939
+ });
14940
+ }
14941
+ if (reference.startSeconds !== void 0 && reference.endSeconds !== void 0 && reference.endSeconds <= reference.startSeconds) {
14942
+ issues.push({
14943
+ path: ["references", index, "endSeconds"],
14944
+ message: `reference "${reference.id}" ends at or before it starts`
14945
+ });
14946
+ }
14947
+ if (reference.kind === "image" && (reference.startSeconds !== void 0 || reference.endSeconds !== void 0)) {
14948
+ issues.push({
14949
+ path: ["references", index, "startSeconds"],
14950
+ message: `reference "${reference.id}" is an image; a time window applies to video only`
14951
+ });
14952
+ }
14953
+ });
14954
+ return issues;
14955
+ }
14956
+ var scene3DPlanSchema = zod.z.object({
14957
+ planType: zod.z.literal(SCENE3D_PLAN_TYPE),
14958
+ schemaVersion: zod.z.literal(SCENE3D_SCHEMA_VERSION),
14959
+ revisionId: zod.z.uuid(),
14960
+ parentRevisionId: zod.z.uuid().optional(),
14961
+ width: zod.z.number().int().min(SCENE3D_LIMITS.minDimensionPx).max(SCENE3D_LIMITS.maxDimensionPx),
14962
+ height: zod.z.number().int().min(SCENE3D_LIMITS.minDimensionPx).max(SCENE3D_LIMITS.maxDimensionPx),
14963
+ fps: zod.z.number().int().min(SCENE3D_LIMITS.minFps).max(SCENE3D_LIMITS.maxFps),
14964
+ durationInFrames: zod.z.number().int().min(SCENE3D_LIMITS.minDurationInFrames).max(SCENE3D_LIMITS.maxDurationInFrames),
14965
+ backgroundColor: scene3DColorSchema,
14966
+ camera: scene3DCameraSchema,
14967
+ objects: zod.z.array(scene3DObjectSchema).min(SCENE3D_LIMITS.minObjects).max(SCENE3D_LIMITS.maxObjects),
14968
+ lighting: scene3DLightingSchema,
14969
+ references: zod.z.array(scene3DReferenceSchema).max(SCENE3D_LIMITS.maxReferences).optional()
14970
+ }).strict().superRefine((plan, ctx) => {
14971
+ for (const issue2 of scene3DPlanIssues(plan)) {
14972
+ ctx.addIssue({ code: "custom", path: issue2.path, message: issue2.message });
14973
+ }
14974
+ });
14975
+ function scene3DDeepEqual(a, b) {
14976
+ if (a === b) return true;
14977
+ if (typeof a !== typeof b) return false;
14978
+ if (a === null || b === null || typeof a !== "object") return false;
14979
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
14980
+ if (Array.isArray(a) && Array.isArray(b)) {
14981
+ if (a.length !== b.length) return false;
14982
+ return a.every((item, i) => scene3DDeepEqual(item, b[i]));
14983
+ }
14984
+ const aObj = a;
14985
+ const bObj = b;
14986
+ const aKeys = Object.keys(aObj);
14987
+ const bKeys = Object.keys(bObj);
14988
+ if (aKeys.length !== bKeys.length) return false;
14989
+ return aKeys.every((key) => key in bObj && scene3DDeepEqual(aObj[key], bObj[key]));
14990
+ }
14991
+ function newScene3DRevisionId() {
14992
+ const webCrypto = globalThis.crypto;
14993
+ if (webCrypto && typeof webCrypto.randomUUID === "function") return webCrypto.randomUUID();
14994
+ const bytes = new Uint8Array(16);
14995
+ if (webCrypto && typeof webCrypto.getRandomValues === "function") {
14996
+ webCrypto.getRandomValues(bytes);
14997
+ } else {
14998
+ for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
14999
+ }
15000
+ bytes[6] = bytes[6] & 15 | 64;
15001
+ bytes[8] = bytes[8] & 63 | 128;
15002
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
15003
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
15004
+ }
15005
+ function isScene3DPlan(value) {
15006
+ return scene3DPlanSchema.safeParse(value).success;
15007
+ }
15008
+ var SCENE3D_PLAN_FIELD = "scenePlan";
15009
+ var SCENE3D_GENERATE_NODE_TYPE = "generate-3d-scene";
15010
+ var SCENE3D_EDIT_NODE_TYPE = "edit-3d-scene";
15011
+ var scene3DObjectChangesSchema = zod.z.object({
15012
+ name: zod.z.string().min(1).max(SCENE3D_LIMITS.maxNameLength).optional(),
15013
+ primitive: scene3DPrimitiveSchema.optional(),
15014
+ /** `null` detaches from the parent; omitted leaves it as-is. */
15015
+ parentId: scene3DIdSchema.nullable().optional(),
15016
+ dimensions: sizeVec3Schema.optional(),
15017
+ position: vec3Schema.optional(),
15018
+ rotation: rotationVec3Schema.optional(),
15019
+ scale: scaleVec3Schema.optional(),
15020
+ color: scene3DColorSchema.optional(),
15021
+ keyframes: zod.z.array(scene3DObjectKeyframeSchema).max(SCENE3D_LIMITS.maxKeyframes).optional()
15022
+ }).strict();
15023
+ var scene3DCameraChangesSchema = zod.z.object({
15024
+ position: vec3Schema.optional(),
15025
+ target: vec3Schema.optional(),
15026
+ focalLengthMm: zod.z.number().min(SCENE3D_LIMITS.minFocalLengthMm).max(SCENE3D_LIMITS.maxFocalLengthMm).optional(),
15027
+ sensorWidthMm: zod.z.number().min(SCENE3D_LIMITS.minSensorWidthMm).max(SCENE3D_LIMITS.maxSensorWidthMm).optional(),
15028
+ keyframes: zod.z.array(scene3DCameraKeyframeSchema).max(SCENE3D_LIMITS.maxKeyframes).optional()
15029
+ }).strict();
15030
+ var scene3DLightingChangesSchema = zod.z.object({
15031
+ ambientIntensity: zod.z.number().min(0).max(SCENE3D_LIMITS.maxIntensity).optional(),
15032
+ keyIntensity: zod.z.number().min(0).max(SCENE3D_LIMITS.maxIntensity).optional(),
15033
+ keyPosition: vec3Schema.optional()
15034
+ }).strict();
15035
+ var scene3DEditOperationSchema = zod.z.discriminatedUnion("op", [
15036
+ zod.z.object({ op: zod.z.literal("set-object"), objectId: scene3DIdSchema, changes: scene3DObjectChangesSchema }).strict(),
15037
+ zod.z.object({ op: zod.z.literal("add-object"), object: scene3DObjectSchema }).strict(),
15038
+ zod.z.object({ op: zod.z.literal("remove-object"), objectId: scene3DIdSchema }).strict(),
15039
+ zod.z.object({ op: zod.z.literal("set-camera"), changes: scene3DCameraChangesSchema }).strict(),
15040
+ zod.z.object({ op: zod.z.literal("set-lighting"), changes: scene3DLightingChangesSchema }).strict(),
15041
+ zod.z.object({ op: zod.z.literal("set-background"), color: scene3DColorSchema }).strict()
15042
+ ]);
15043
+ var scene3DEditOperationsSchema = zod.z.array(scene3DEditOperationSchema).min(1).max(SCENE3D_LIMITS.maxOperations);
15044
+ function clonePlan(plan) {
15045
+ return JSON.parse(JSON.stringify(plan));
15046
+ }
15047
+ function summarizeScene3DOperations(operations) {
15048
+ const lines = operations.map((operation) => {
15049
+ switch (operation.op) {
15050
+ case "set-object": {
15051
+ const fields = Object.keys(operation.changes);
15052
+ return `Updated ${fields.length > 0 ? fields.join(", ") : "nothing"} on "${operation.objectId}"`;
15053
+ }
15054
+ case "add-object":
15055
+ return `Added ${operation.object.primitive} "${operation.object.name}" (${operation.object.id})`;
15056
+ case "remove-object":
15057
+ return `Removed "${operation.objectId}"`;
15058
+ case "set-camera":
15059
+ return `Updated camera ${Object.keys(operation.changes).join(", ") || "nothing"}`;
15060
+ case "set-lighting":
15061
+ return `Updated lighting ${Object.keys(operation.changes).join(", ") || "nothing"}`;
15062
+ case "set-background":
15063
+ return `Set background to ${operation.color}`;
15064
+ }
15065
+ });
15066
+ return lines.join("; ").slice(0, SCENE3D_LIMITS.maxChangeSummaryLength);
15067
+ }
15068
+ function firstIssueMessage(error) {
15069
+ const issue2 = error.issues[0];
15070
+ if (!issue2) return "invalid";
15071
+ const path = issue2.path.join(".");
15072
+ return path ? `${path}: ${issue2.message}` : issue2.message;
15073
+ }
15074
+ function applyScene3DEditOperations(plan, operations, options = {}) {
15075
+ const parsedPlan = scene3DPlanSchema.safeParse(plan);
15076
+ if (!parsedPlan.success) {
15077
+ return { ok: false, code: "invalid_plan", message: `scenePlan is invalid \u2014 ${firstIssueMessage(parsedPlan.error)}` };
15078
+ }
15079
+ const source = parsedPlan.data;
15080
+ if (options.expectedRevisionId !== void 0 && options.expectedRevisionId !== source.revisionId) {
15081
+ return {
15082
+ ok: false,
15083
+ code: "stale_revision",
15084
+ message: `This scene has moved on \u2014 expected revision ${options.expectedRevisionId}, the plan is at ${source.revisionId}.`
15085
+ };
15086
+ }
15087
+ const parsedOps = scene3DEditOperationsSchema.safeParse(operations);
15088
+ if (!parsedOps.success) {
15089
+ const issue2 = parsedOps.error.issues[0];
15090
+ const index = typeof issue2?.path[0] === "number" ? issue2.path[0] : void 0;
15091
+ return {
15092
+ ok: false,
15093
+ code: "invalid_operations",
15094
+ message: `operations are invalid \u2014 ${firstIssueMessage(parsedOps.error)}`,
15095
+ ...index === void 0 ? {} : { operationIndex: index }
15096
+ };
15097
+ }
15098
+ const ops = parsedOps.data;
15099
+ const next = clonePlan(source);
15100
+ const changed = /* @__PURE__ */ new Set();
15101
+ for (let index = 0; index < ops.length; index++) {
15102
+ const operation = ops[index];
15103
+ switch (operation.op) {
15104
+ case "set-object": {
15105
+ const target = next.objects.findIndex((o) => o.id === operation.objectId);
15106
+ if (target === -1) {
15107
+ return {
15108
+ ok: false,
15109
+ code: "unknown_object",
15110
+ message: `no object "${operation.objectId}" in this scene`,
15111
+ operationIndex: index
15112
+ };
15113
+ }
15114
+ const { parentId, ...rest } = operation.changes;
15115
+ const updated = { ...next.objects[target], ...rest };
15116
+ if (parentId !== void 0) {
15117
+ if (parentId === null) delete updated.parentId;
15118
+ else updated.parentId = parentId;
15119
+ }
15120
+ next.objects = next.objects.map((o, i) => i === target ? updated : o);
15121
+ changed.add(operation.objectId);
15122
+ break;
15123
+ }
15124
+ case "add-object": {
15125
+ if (next.objects.some((o) => o.id === operation.object.id)) {
15126
+ return {
15127
+ ok: false,
15128
+ code: "duplicate_object",
15129
+ message: `an object with id "${operation.object.id}" already exists`,
15130
+ operationIndex: index
15131
+ };
15132
+ }
15133
+ next.objects = [...next.objects, operation.object];
15134
+ changed.add(operation.object.id);
15135
+ break;
15136
+ }
15137
+ case "remove-object": {
15138
+ if (!next.objects.some((o) => o.id === operation.objectId)) {
15139
+ return {
15140
+ ok: false,
15141
+ code: "unknown_object",
15142
+ message: `no object "${operation.objectId}" in this scene`,
15143
+ operationIndex: index
15144
+ };
15145
+ }
15146
+ next.objects = next.objects.filter((o) => o.id !== operation.objectId);
15147
+ changed.add(operation.objectId);
15148
+ break;
15149
+ }
15150
+ case "set-camera":
15151
+ next.camera = { ...next.camera, ...operation.changes };
15152
+ break;
15153
+ case "set-lighting":
15154
+ next.lighting = { ...next.lighting, ...operation.changes };
15155
+ break;
15156
+ case "set-background":
15157
+ next.backgroundColor = operation.color;
15158
+ break;
15159
+ }
15160
+ }
15161
+ for (const lockedId of options.lockedObjectIds ?? []) {
15162
+ const before = source.objects.find((o) => o.id === lockedId);
15163
+ const after = next.objects.find((o) => o.id === lockedId);
15164
+ if (before === void 0) continue;
15165
+ if (after === void 0) {
15166
+ return { ok: false, code: "locked_object", message: `object "${lockedId}" is locked and cannot be removed` };
15167
+ }
15168
+ if (!scene3DDeepEqual(before, after)) {
15169
+ return { ok: false, code: "locked_object", message: `object "${lockedId}" is locked and cannot be modified` };
15170
+ }
15171
+ }
15172
+ next.parentRevisionId = source.revisionId;
15173
+ next.revisionId = options.revisionId ?? newScene3DRevisionId();
15174
+ const validated = scene3DPlanSchema.safeParse(next);
15175
+ if (!validated.success) {
15176
+ return {
15177
+ ok: false,
15178
+ code: "invalid_plan",
15179
+ message: `the edit would leave the scene invalid \u2014 ${firstIssueMessage(validated.error)}`
15180
+ };
15181
+ }
15182
+ return {
15183
+ ok: true,
15184
+ plan: validated.data,
15185
+ changedObjectIds: [...changed],
15186
+ changeSummary: summarizeScene3DOperations(ops)
15187
+ };
15188
+ }
14681
15189
 
14682
15190
  // src/studio-transient.ts
14683
15191
  var STUDIO_TRANSIENT_KEYS = [
@@ -15053,6 +15561,15 @@ exports.REPEATABLE_NODE_TYPES = REPEATABLE_NODE_TYPES;
15053
15561
  exports.REPEAT_PLACEHOLDER = REPEAT_PLACEHOLDER;
15054
15562
  exports.REPLICATE_LIP_SYNC_PROVIDERS = REPLICATE_LIP_SYNC_PROVIDERS;
15055
15563
  exports.RESERVED_TEMPLATE_VARS = RESERVED_TEMPLATE_VARS;
15564
+ exports.SCENE3D_DEFAULT_DURATION_SECONDS = SCENE3D_DEFAULT_DURATION_SECONDS;
15565
+ exports.SCENE3D_DEFAULT_FPS = SCENE3D_DEFAULT_FPS;
15566
+ exports.SCENE3D_EDIT_NODE_TYPE = SCENE3D_EDIT_NODE_TYPE;
15567
+ exports.SCENE3D_GENERATE_NODE_TYPE = SCENE3D_GENERATE_NODE_TYPE;
15568
+ exports.SCENE3D_LIMITS = SCENE3D_LIMITS;
15569
+ exports.SCENE3D_PLAN_FIELD = SCENE3D_PLAN_FIELD;
15570
+ exports.SCENE3D_PLAN_TYPE = SCENE3D_PLAN_TYPE;
15571
+ exports.SCENE3D_PRIMITIVES = SCENE3D_PRIMITIVES;
15572
+ exports.SCENE3D_SCHEMA_VERSION = SCENE3D_SCHEMA_VERSION;
15056
15573
  exports.SCENE_HELPER_NAMES = SCENE_HELPER_NAMES;
15057
15574
  exports.SCRAPER_ACTOR_LABELS = SCRAPER_ACTOR_LABELS;
15058
15575
  exports.SCRAPER_CREDIT_COSTS = SCRAPER_CREDIT_COSTS;
@@ -15216,6 +15733,7 @@ exports.applyDefaultVideoSelection = applyDefaultVideoSelection;
15216
15733
  exports.applyHandleInputOverride = applyHandleInputOverride;
15217
15734
  exports.applyRange = applyRange;
15218
15735
  exports.applyRangeIndices = applyRangeIndices;
15736
+ exports.applyScene3DEditOperations = applyScene3DEditOperations;
15219
15737
  exports.applySlots = applySlots;
15220
15738
  exports.applyVideoAudioToggle = applyVideoAudioToggle;
15221
15739
  exports.applyVideoNegativePrompt = applyVideoNegativePrompt;
@@ -15394,6 +15912,8 @@ exports.isObjectAspectRatio = isObjectAspectRatio;
15394
15912
  exports.isOversizedScene = isOversizedScene;
15395
15913
  exports.isPaygRetentionActive = isPaygRetentionActive;
15396
15914
  exports.isPerSecondLipSyncProvider = isPerSecondLipSyncProvider;
15915
+ exports.isScene3DHttpUrl = isScene3DHttpUrl;
15916
+ exports.isScene3DPlan = isScene3DPlan;
15397
15917
  exports.isScraperActor = isScraperActor;
15398
15918
  exports.isSeedance2Provider = isSeedance2Provider;
15399
15919
  exports.isTiltDirection = isTiltDirection;
@@ -15428,6 +15948,7 @@ exports.modelToNodeTarget = modelToNodeTarget;
15428
15948
  exports.modelsForInputMode = modelsForInputMode;
15429
15949
  exports.modelsWithFeature = modelsWithFeature;
15430
15950
  exports.motionGraphicsFeature = motionGraphicsFeature;
15951
+ exports.newScene3DRevisionId = newScene3DRevisionId;
15431
15952
  exports.normalizeLottieLayers = normalizeLottieLayers;
15432
15953
  exports.normalizeMinimaxH3Resolution = normalizeMinimaxH3Resolution;
15433
15954
  exports.normalizeModelInput = normalizeModelInput;
@@ -15514,9 +16035,30 @@ exports.rewriteSlotTokens = rewriteSlotTokens;
15514
16035
  exports.rewriteSpeakerSlots = rewriteSpeakerSlots;
15515
16036
  exports.rgbaArrayToHex = rgbaArrayToHex;
15516
16037
  exports.roleToPhrase = roleToPhrase;
16038
+ exports.rotationVec3Schema = rotationVec3Schema;
15517
16039
  exports.runSelector = runSelector;
15518
16040
  exports.safetyRetryPolicy = safetyRetryPolicy;
15519
16041
  exports.sanitizeRole = sanitizeRole;
16042
+ exports.scaleVec3Schema = scaleVec3Schema;
16043
+ exports.scene3DCameraChangesSchema = scene3DCameraChangesSchema;
16044
+ exports.scene3DCameraKeyframeSchema = scene3DCameraKeyframeSchema;
16045
+ exports.scene3DCameraSchema = scene3DCameraSchema;
16046
+ exports.scene3DColorSchema = scene3DColorSchema;
16047
+ exports.scene3DDeepEqual = scene3DDeepEqual;
16048
+ exports.scene3DEasingSchema = scene3DEasingSchema;
16049
+ exports.scene3DEditOperationSchema = scene3DEditOperationSchema;
16050
+ exports.scene3DEditOperationsSchema = scene3DEditOperationsSchema;
16051
+ exports.scene3DIdSchema = scene3DIdSchema;
16052
+ exports.scene3DLightingChangesSchema = scene3DLightingChangesSchema;
16053
+ exports.scene3DLightingSchema = scene3DLightingSchema;
16054
+ exports.scene3DObjectChangesSchema = scene3DObjectChangesSchema;
16055
+ exports.scene3DObjectKeyframeSchema = scene3DObjectKeyframeSchema;
16056
+ exports.scene3DObjectSchema = scene3DObjectSchema;
16057
+ exports.scene3DPlanIssues = scene3DPlanIssues;
16058
+ exports.scene3DPlanSchema = scene3DPlanSchema;
16059
+ exports.scene3DPrimitiveSchema = scene3DPrimitiveSchema;
16060
+ exports.scene3DReferenceSchema = scene3DReferenceSchema;
16061
+ exports.scene3DUrlSchema = scene3DUrlSchema;
15520
16062
  exports.searchModelVariants = searchModelVariants;
15521
16063
  exports.seedance2AudioLimitSec = seedance2AudioLimitSec;
15522
16064
  exports.segmentDurationsFor = segmentDurationsFor;
@@ -15528,6 +16070,7 @@ exports.selectLoraRoutingForMentions = selectLoraRoutingForMentions;
15528
16070
  exports.selectRandom = selectRandom;
15529
16071
  exports.setRegisteredPersonPackFields = setRegisteredPersonPackFields;
15530
16072
  exports.settledWithLimit = settledWithLimit;
16073
+ exports.sizeVec3Schema = sizeVec3Schema;
15531
16074
  exports.slotVariationSchema = slotVariationSchema;
15532
16075
  exports.sortCharacterEntriesForDisplay = sortCharacterEntriesForDisplay;
15533
16076
  exports.sortListItems = sortListItems;
@@ -15541,6 +16084,7 @@ exports.stripDerivedAnalysisFields = stripDerivedAnalysisFields;
15541
16084
  exports.stripExportContent = stripExportContent;
15542
16085
  exports.stripStudioTransientSettings = stripStudioTransientSettings;
15543
16086
  exports.stripTransientRuntimeData = stripTransientRuntimeData;
16087
+ exports.summarizeScene3DOperations = summarizeScene3DOperations;
15544
16088
  exports.sunoCreditType = sunoCreditType;
15545
16089
  exports.supportedDefaultDimensions = supportedDefaultDimensions;
15546
16090
  exports.supportsAdvancedMode = supportsAdvancedMode;
@@ -15569,6 +16113,7 @@ exports.validateObjects = validateObjects;
15569
16113
  exports.validateProviderForNodeType = validateProviderForNodeType;
15570
16114
  exports.validateSubWorkflowRoutes = validateSubWorkflowRoutes;
15571
16115
  exports.variantJobId = variantJobId;
16116
+ exports.vec3Schema = vec3Schema;
15572
16117
  exports.videoAnalysisCreditSegment = videoAnalysisCreditSegment;
15573
16118
  exports.videoAnalysisNumWindows = videoAnalysisNumWindows;
15574
16119
  exports.videoAnalysisResultSchema = videoAnalysisResultSchema;