@nodaro/shared 2.24.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,536 @@ 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
+ }
15189
+
15190
+ // src/studio-transient.ts
15191
+ var STUDIO_TRANSIENT_KEYS = [
15192
+ "trash",
15193
+ "pendingStills",
15194
+ "pendingClips",
15195
+ // The two markers that genuinely DO live at this level: a soundtrack render
15196
+ // and a story-planning run in flight. Same rule as the per-shot pair — they
15197
+ // name jobs on the owner's account and nobody else can land them.
15198
+ "pendingMusic",
15199
+ "pendingDraft",
15200
+ "freecutDraftUrl"
15201
+ ];
15202
+ var STUDIO_SHOT_TRANSIENT_KEYS = ["pendingClips", "pendingClip", "pendingStills"];
15203
+ function withoutKeys(source, drop) {
15204
+ const kept = {};
15205
+ for (const [key, value] of Object.entries(source)) {
15206
+ if (drop.includes(key)) continue;
15207
+ kept[key] = value;
15208
+ }
15209
+ return kept;
15210
+ }
15211
+ function stripShots(value) {
15212
+ if (!Array.isArray(value)) return value;
15213
+ let changed = false;
15214
+ const out = value.map((entry) => {
15215
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return entry;
15216
+ const shot = entry;
15217
+ if (!STUDIO_SHOT_TRANSIENT_KEYS.some((key) => key in shot)) return entry;
15218
+ changed = true;
15219
+ return withoutKeys(shot, STUDIO_SHOT_TRANSIENT_KEYS);
15220
+ });
15221
+ return changed ? out : value;
15222
+ }
15223
+ function stripStudioTransientSettings(settings) {
15224
+ if (!settings || typeof settings !== "object") return settings;
15225
+ const studio = settings.studio;
15226
+ if (!studio || typeof studio !== "object" || Array.isArray(studio)) return settings;
15227
+ const source = studio;
15228
+ const kept = withoutKeys(source, STUDIO_TRANSIENT_KEYS);
15229
+ const shots = stripShots(source.shots);
15230
+ if (source.shots !== void 0) kept.shots = shots;
15231
+ if (Object.keys(kept).length === Object.keys(source).length && shots === source.shots) {
15232
+ return settings;
15233
+ }
15234
+ return { ...settings, studio: kept };
15235
+ }
14681
15236
 
14682
15237
  exports.ACCESS_LEVELS = ACCESS_LEVELS;
14683
15238
  exports.ACTIVE_SCENE_HELPERS = ACTIVE_SCENE_HELPERS;
@@ -15006,6 +15561,15 @@ exports.REPEATABLE_NODE_TYPES = REPEATABLE_NODE_TYPES;
15006
15561
  exports.REPEAT_PLACEHOLDER = REPEAT_PLACEHOLDER;
15007
15562
  exports.REPLICATE_LIP_SYNC_PROVIDERS = REPLICATE_LIP_SYNC_PROVIDERS;
15008
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;
15009
15573
  exports.SCENE_HELPER_NAMES = SCENE_HELPER_NAMES;
15010
15574
  exports.SCRAPER_ACTOR_LABELS = SCRAPER_ACTOR_LABELS;
15011
15575
  exports.SCRAPER_CREDIT_COSTS = SCRAPER_CREDIT_COSTS;
@@ -15042,6 +15606,8 @@ exports.STAGE_PATCH_SCHEMA = STAGE_PATCH_SCHEMA;
15042
15606
  exports.STATIC_CAPTION_STYLES = STATIC_CAPTION_STYLES;
15043
15607
  exports.STRUCTURAL_SECTIONS = STRUCTURAL_SECTIONS;
15044
15608
  exports.STRUCTURED_VISION_MODELS = STRUCTURED_VISION_MODELS;
15609
+ exports.STUDIO_SHOT_TRANSIENT_KEYS = STUDIO_SHOT_TRANSIENT_KEYS;
15610
+ exports.STUDIO_TRANSIENT_KEYS = STUDIO_TRANSIENT_KEYS;
15045
15611
  exports.SUBMISSION_STATUSES = SUBMISSION_STATUSES;
15046
15612
  exports.SUNO_ADD_TRACK_MODELS = SUNO_ADD_TRACK_MODELS;
15047
15613
  exports.SUNO_FIELD_HANDLE_FIELDS = SUNO_FIELD_HANDLE_FIELDS;
@@ -15167,6 +15733,7 @@ exports.applyDefaultVideoSelection = applyDefaultVideoSelection;
15167
15733
  exports.applyHandleInputOverride = applyHandleInputOverride;
15168
15734
  exports.applyRange = applyRange;
15169
15735
  exports.applyRangeIndices = applyRangeIndices;
15736
+ exports.applyScene3DEditOperations = applyScene3DEditOperations;
15170
15737
  exports.applySlots = applySlots;
15171
15738
  exports.applyVideoAudioToggle = applyVideoAudioToggle;
15172
15739
  exports.applyVideoNegativePrompt = applyVideoNegativePrompt;
@@ -15345,6 +15912,8 @@ exports.isObjectAspectRatio = isObjectAspectRatio;
15345
15912
  exports.isOversizedScene = isOversizedScene;
15346
15913
  exports.isPaygRetentionActive = isPaygRetentionActive;
15347
15914
  exports.isPerSecondLipSyncProvider = isPerSecondLipSyncProvider;
15915
+ exports.isScene3DHttpUrl = isScene3DHttpUrl;
15916
+ exports.isScene3DPlan = isScene3DPlan;
15348
15917
  exports.isScraperActor = isScraperActor;
15349
15918
  exports.isSeedance2Provider = isSeedance2Provider;
15350
15919
  exports.isTiltDirection = isTiltDirection;
@@ -15379,6 +15948,7 @@ exports.modelToNodeTarget = modelToNodeTarget;
15379
15948
  exports.modelsForInputMode = modelsForInputMode;
15380
15949
  exports.modelsWithFeature = modelsWithFeature;
15381
15950
  exports.motionGraphicsFeature = motionGraphicsFeature;
15951
+ exports.newScene3DRevisionId = newScene3DRevisionId;
15382
15952
  exports.normalizeLottieLayers = normalizeLottieLayers;
15383
15953
  exports.normalizeMinimaxH3Resolution = normalizeMinimaxH3Resolution;
15384
15954
  exports.normalizeModelInput = normalizeModelInput;
@@ -15465,9 +16035,30 @@ exports.rewriteSlotTokens = rewriteSlotTokens;
15465
16035
  exports.rewriteSpeakerSlots = rewriteSpeakerSlots;
15466
16036
  exports.rgbaArrayToHex = rgbaArrayToHex;
15467
16037
  exports.roleToPhrase = roleToPhrase;
16038
+ exports.rotationVec3Schema = rotationVec3Schema;
15468
16039
  exports.runSelector = runSelector;
15469
16040
  exports.safetyRetryPolicy = safetyRetryPolicy;
15470
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;
15471
16062
  exports.searchModelVariants = searchModelVariants;
15472
16063
  exports.seedance2AudioLimitSec = seedance2AudioLimitSec;
15473
16064
  exports.segmentDurationsFor = segmentDurationsFor;
@@ -15479,6 +16070,7 @@ exports.selectLoraRoutingForMentions = selectLoraRoutingForMentions;
15479
16070
  exports.selectRandom = selectRandom;
15480
16071
  exports.setRegisteredPersonPackFields = setRegisteredPersonPackFields;
15481
16072
  exports.settledWithLimit = settledWithLimit;
16073
+ exports.sizeVec3Schema = sizeVec3Schema;
15482
16074
  exports.slotVariationSchema = slotVariationSchema;
15483
16075
  exports.sortCharacterEntriesForDisplay = sortCharacterEntriesForDisplay;
15484
16076
  exports.sortListItems = sortListItems;
@@ -15490,7 +16082,9 @@ exports.spreadJsonArrayIfSingleton = spreadJsonArrayIfSingleton;
15490
16082
  exports.stringifyPathResults = stringifyPathResults;
15491
16083
  exports.stripDerivedAnalysisFields = stripDerivedAnalysisFields;
15492
16084
  exports.stripExportContent = stripExportContent;
16085
+ exports.stripStudioTransientSettings = stripStudioTransientSettings;
15493
16086
  exports.stripTransientRuntimeData = stripTransientRuntimeData;
16087
+ exports.summarizeScene3DOperations = summarizeScene3DOperations;
15494
16088
  exports.sunoCreditType = sunoCreditType;
15495
16089
  exports.supportedDefaultDimensions = supportedDefaultDimensions;
15496
16090
  exports.supportsAdvancedMode = supportsAdvancedMode;
@@ -15519,6 +16113,7 @@ exports.validateObjects = validateObjects;
15519
16113
  exports.validateProviderForNodeType = validateProviderForNodeType;
15520
16114
  exports.validateSubWorkflowRoutes = validateSubWorkflowRoutes;
15521
16115
  exports.variantJobId = variantJobId;
16116
+ exports.vec3Schema = vec3Schema;
15522
16117
  exports.videoAnalysisCreditSegment = videoAnalysisCreditSegment;
15523
16118
  exports.videoAnalysisNumWindows = videoAnalysisNumWindows;
15524
16119
  exports.videoAnalysisResultSchema = videoAnalysisResultSchema;