@nodaro/shared 1.4.1

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 (665) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +32 -0
  3. package/dist/index.cjs +12506 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +12269 -0
  6. package/dist/index.d.ts +12269 -0
  7. package/dist/index.js +11831 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +54 -0
  10. package/src/__tests__/__snapshots__/prompt-builder-segments.test.ts.snap +65 -0
  11. package/src/__tests__/ai-avatar-pricing.test.ts +321 -0
  12. package/src/__tests__/apply-video-negative-prompt.test.ts +76 -0
  13. package/src/__tests__/attach-columns.test.ts +28 -0
  14. package/src/__tests__/audio-crossfade-curves.test.ts +33 -0
  15. package/src/__tests__/build-model-menu.test.ts +58 -0
  16. package/src/__tests__/character-aspect-defaults.test.ts +142 -0
  17. package/src/__tests__/character-mention-slug.test.ts +199 -0
  18. package/src/__tests__/character-reference-photo-kinds.test.ts +32 -0
  19. package/src/__tests__/character-usage-mode.test.ts +147 -0
  20. package/src/__tests__/character-variant-assets.test.ts +142 -0
  21. package/src/__tests__/character-voice.test.ts +130 -0
  22. package/src/__tests__/cinematic-avatar-pricing.test.ts +119 -0
  23. package/src/__tests__/combine-transitions.test.ts +137 -0
  24. package/src/__tests__/component-types.test.ts +87 -0
  25. package/src/__tests__/condition-variables.test.ts +98 -0
  26. package/src/__tests__/credit-identifiers.test.ts +811 -0
  27. package/src/__tests__/default-video-provider.test.ts +63 -0
  28. package/src/__tests__/entity-image-handle.test.ts +43 -0
  29. package/src/__tests__/entity-variants.test.ts +33 -0
  30. package/src/__tests__/fal-lip-sync-sets.test.ts +31 -0
  31. package/src/__tests__/featured-entities.test.ts +33 -0
  32. package/src/__tests__/film-pricing.test.ts +64 -0
  33. package/src/__tests__/filter-condition.test.ts +285 -0
  34. package/src/__tests__/freecut-protocol.test.ts +35 -0
  35. package/src/__tests__/generate-text-items.test.ts +19 -0
  36. package/src/__tests__/generated-results.test.ts +50 -0
  37. package/src/__tests__/graph-utils.test.ts +618 -0
  38. package/src/__tests__/group-aggregation.test.ts +83 -0
  39. package/src/__tests__/i18n-locale-completeness.test.ts +156 -0
  40. package/src/__tests__/image-critic-node.test.ts +86 -0
  41. package/src/__tests__/image-mask-mode.test.ts +16 -0
  42. package/src/__tests__/image-ref-limit.test.ts +78 -0
  43. package/src/__tests__/inpaint-region.test.ts +24 -0
  44. package/src/__tests__/input-field-map-parity.test.ts +30 -0
  45. package/src/__tests__/json-evaluator.test.ts +289 -0
  46. package/src/__tests__/json-path.test.ts +64 -0
  47. package/src/__tests__/lip-sync-pricing.test.ts +142 -0
  48. package/src/__tests__/list-merge.test.ts +96 -0
  49. package/src/__tests__/list-sort.test.ts +134 -0
  50. package/src/__tests__/llm-models.test.ts +445 -0
  51. package/src/__tests__/location-mention-slug.test.ts +246 -0
  52. package/src/__tests__/locations-coverage-critic-schema.test.ts +92 -0
  53. package/src/__tests__/loop-delimiter.test.ts +39 -0
  54. package/src/__tests__/lottie-slots.test.ts +245 -0
  55. package/src/__tests__/model-catalog-flux2-resolutions.test.ts +21 -0
  56. package/src/__tests__/model-constants-location.test.ts +20 -0
  57. package/src/__tests__/model-constants.test.ts +122 -0
  58. package/src/__tests__/model-tree.test.ts +86 -0
  59. package/src/__tests__/no-internal-imports.test.ts +25 -0
  60. package/src/__tests__/node-default-mappings.test.ts +175 -0
  61. package/src/__tests__/node-mappable-fields.test.ts +7 -0
  62. package/src/__tests__/node-preset-export.test.ts +46 -0
  63. package/src/__tests__/node-preset-extract.test.ts +166 -0
  64. package/src/__tests__/node-refs-canonical.test.ts +54 -0
  65. package/src/__tests__/node-refs-combine.test.ts +54 -0
  66. package/src/__tests__/node-refs.test.ts +187 -0
  67. package/src/__tests__/node-runtime-keys.test.ts +99 -0
  68. package/src/__tests__/object-aspect-defaults.test.ts +59 -0
  69. package/src/__tests__/parameter-node-value.test.ts +137 -0
  70. package/src/__tests__/pinterest-url.test.ts +35 -0
  71. package/src/__tests__/pipeline-chat.test.ts +110 -0
  72. package/src/__tests__/pipeline-validation.test.ts +98 -0
  73. package/src/__tests__/presentation-utils.test.ts +1137 -0
  74. package/src/__tests__/producer-types.test.ts +47 -0
  75. package/src/__tests__/prompt-length-limits.test.ts +156 -0
  76. package/src/__tests__/provider-directive-defaults.test.ts +46 -0
  77. package/src/__tests__/reduce-strategy-registry.test.ts +43 -0
  78. package/src/__tests__/reference-board-templates.test.ts +33 -0
  79. package/src/__tests__/reference-roles.test.ts +36 -0
  80. package/src/__tests__/resolve-field-mappings.test.ts +110 -0
  81. package/src/__tests__/resolve-pipeline-model.test.ts +76 -0
  82. package/src/__tests__/scene-node-types.test.ts +80 -0
  83. package/src/__tests__/scraper-actors.test.ts +67 -0
  84. package/src/__tests__/seedance-2-catalog.test.ts +18 -0
  85. package/src/__tests__/seedance-2-credit-id.test.ts +23 -0
  86. package/src/__tests__/seedance-aspects.test.ts +52 -0
  87. package/src/__tests__/selector.test.ts +1151 -0
  88. package/src/__tests__/settled-with-limit.test.ts +214 -0
  89. package/src/__tests__/social-media-specs.test.ts +129 -0
  90. package/src/__tests__/sub-workflow-validation.test.ts +88 -0
  91. package/src/__tests__/switchx-pricing.test.ts +28 -0
  92. package/src/__tests__/to-connected-references.test.ts +153 -0
  93. package/src/__tests__/utilities.test.ts +541 -0
  94. package/src/__tests__/video-analysis-pricing.test.ts +58 -0
  95. package/src/__tests__/video-analysis.test.ts +63 -0
  96. package/src/__tests__/video-audio-capability.test.ts +204 -0
  97. package/src/__tests__/video-gen-providers.test.ts +28 -0
  98. package/src/__tests__/video-mode-aliases.test.ts +150 -0
  99. package/src/__tests__/video-ref-limits.test.ts +72 -0
  100. package/src/__tests__/video-utils.test.ts +303 -0
  101. package/src/__tests__/voice-changer-models.test.ts +33 -0
  102. package/src/__tests__/workflow-export-strip.test.ts +46 -0
  103. package/src/ai-avatar-pricing.ts +213 -0
  104. package/src/ancestor-refs.ts +48 -0
  105. package/src/animals.ts +231 -0
  106. package/src/audio-crossfade-curves.ts +73 -0
  107. package/src/audio-fx-presets.ts +64 -0
  108. package/src/avatar-payload-validators.ts +214 -0
  109. package/src/caption-styles.ts +31 -0
  110. package/src/character-aspect-defaults.ts +102 -0
  111. package/src/character-facets.ts +37 -0
  112. package/src/character-mention-slug.ts +166 -0
  113. package/src/character-usage-mode.ts +109 -0
  114. package/src/character-variant-assets.ts +121 -0
  115. package/src/character-voice.ts +151 -0
  116. package/src/cinematic-avatar-pricing.ts +111 -0
  117. package/src/clone-utils.ts +46 -0
  118. package/src/combine-transitions.ts +482 -0
  119. package/src/community.ts +112 -0
  120. package/src/component-types.ts +127 -0
  121. package/src/condition-variables.ts +65 -0
  122. package/src/credit-estimators/index.ts +15 -0
  123. package/src/credit-estimators/video-utils.ts +165 -0
  124. package/src/credit-identifiers.ts +304 -0
  125. package/src/entity-approval-types.ts +31 -0
  126. package/src/entity-aspect-defaults.ts +82 -0
  127. package/src/entity-asset-types.ts +296 -0
  128. package/src/entity-image-handle.ts +36 -0
  129. package/src/extra-refs.ts +139 -0
  130. package/src/featured-entities.ts +136 -0
  131. package/src/film-pricing.ts +117 -0
  132. package/src/filter-condition.ts +250 -0
  133. package/src/flux2-pricing.ts +18 -0
  134. package/src/freecut-protocol.ts +52 -0
  135. package/src/furniture.ts +167 -0
  136. package/src/generate-text-items.ts +15 -0
  137. package/src/generated-results.ts +63 -0
  138. package/src/group-aggregation.ts +75 -0
  139. package/src/hint-graph-types.ts +18 -0
  140. package/src/i18n/action-fx.ar.ts +89 -0
  141. package/src/i18n/action-fx.de.ts +89 -0
  142. package/src/i18n/action-fx.es.ts +89 -0
  143. package/src/i18n/action-fx.fr.ts +89 -0
  144. package/src/i18n/action-fx.he.ts +89 -0
  145. package/src/i18n/action-fx.hi.ts +89 -0
  146. package/src/i18n/action-fx.ja.ts +89 -0
  147. package/src/i18n/action-fx.ko.ts +89 -0
  148. package/src/i18n/action-fx.pt-BR.ts +89 -0
  149. package/src/i18n/action-fx.ru.ts +89 -0
  150. package/src/i18n/action-fx.zh-CN.ts +89 -0
  151. package/src/i18n/aesthetic.ar.ts +170 -0
  152. package/src/i18n/aesthetic.de.ts +48 -0
  153. package/src/i18n/aesthetic.es.ts +169 -0
  154. package/src/i18n/aesthetic.fr.ts +52 -0
  155. package/src/i18n/aesthetic.he.ts +169 -0
  156. package/src/i18n/aesthetic.hi.ts +55 -0
  157. package/src/i18n/aesthetic.ja.ts +165 -0
  158. package/src/i18n/aesthetic.ko.ts +169 -0
  159. package/src/i18n/aesthetic.pt-BR.ts +148 -0
  160. package/src/i18n/aesthetic.ru.ts +155 -0
  161. package/src/i18n/aesthetic.zh-CN.ts +172 -0
  162. package/src/i18n/animals.ar.ts +145 -0
  163. package/src/i18n/animals.de.ts +144 -0
  164. package/src/i18n/animals.es.ts +158 -0
  165. package/src/i18n/animals.fr.ts +144 -0
  166. package/src/i18n/animals.he.ts +155 -0
  167. package/src/i18n/animals.hi.ts +157 -0
  168. package/src/i18n/animals.ja.ts +159 -0
  169. package/src/i18n/animals.ko.ts +159 -0
  170. package/src/i18n/animals.pt-BR.ts +157 -0
  171. package/src/i18n/animals.ru.ts +155 -0
  172. package/src/i18n/animals.zh-CN.ts +155 -0
  173. package/src/i18n/atmosphere.ar.ts +49 -0
  174. package/src/i18n/atmosphere.de.ts +47 -0
  175. package/src/i18n/atmosphere.es.ts +50 -0
  176. package/src/i18n/atmosphere.fr.ts +47 -0
  177. package/src/i18n/atmosphere.he.ts +47 -0
  178. package/src/i18n/atmosphere.hi.ts +49 -0
  179. package/src/i18n/atmosphere.ja.ts +49 -0
  180. package/src/i18n/atmosphere.ko.ts +49 -0
  181. package/src/i18n/atmosphere.pt-BR.ts +49 -0
  182. package/src/i18n/atmosphere.ru.ts +47 -0
  183. package/src/i18n/atmosphere.zh-CN.ts +152 -0
  184. package/src/i18n/backdrop.ar.ts +53 -0
  185. package/src/i18n/backdrop.de.ts +46 -0
  186. package/src/i18n/backdrop.es.ts +54 -0
  187. package/src/i18n/backdrop.fr.ts +52 -0
  188. package/src/i18n/backdrop.he.ts +51 -0
  189. package/src/i18n/backdrop.hi.ts +59 -0
  190. package/src/i18n/backdrop.ja.ts +61 -0
  191. package/src/i18n/backdrop.ko.ts +59 -0
  192. package/src/i18n/backdrop.pt-BR.ts +59 -0
  193. package/src/i18n/backdrop.ru.ts +57 -0
  194. package/src/i18n/backdrop.zh-CN.ts +57 -0
  195. package/src/i18n/camera-format.ar.ts +40 -0
  196. package/src/i18n/camera-format.de.ts +37 -0
  197. package/src/i18n/camera-format.es.ts +41 -0
  198. package/src/i18n/camera-format.fr.ts +37 -0
  199. package/src/i18n/camera-format.he.ts +37 -0
  200. package/src/i18n/camera-format.hi.ts +42 -0
  201. package/src/i18n/camera-format.ja.ts +37 -0
  202. package/src/i18n/camera-format.ko.ts +37 -0
  203. package/src/i18n/camera-format.pt-BR.ts +46 -0
  204. package/src/i18n/camera-format.ru.ts +42 -0
  205. package/src/i18n/camera-format.zh-CN.ts +42 -0
  206. package/src/i18n/camera-motions.ar.ts +86 -0
  207. package/src/i18n/camera-motions.de.ts +74 -0
  208. package/src/i18n/camera-motions.es.ts +86 -0
  209. package/src/i18n/camera-motions.fr.ts +86 -0
  210. package/src/i18n/camera-motions.he.ts +86 -0
  211. package/src/i18n/camera-motions.hi.ts +99 -0
  212. package/src/i18n/camera-motions.ja.ts +99 -0
  213. package/src/i18n/camera-motions.ko.ts +99 -0
  214. package/src/i18n/camera-motions.pt-BR.ts +99 -0
  215. package/src/i18n/camera-motions.ru.ts +99 -0
  216. package/src/i18n/camera-motions.zh-CN.ts +99 -0
  217. package/src/i18n/character-fx.ar.ts +72 -0
  218. package/src/i18n/character-fx.de.ts +72 -0
  219. package/src/i18n/character-fx.es.ts +72 -0
  220. package/src/i18n/character-fx.fr.ts +72 -0
  221. package/src/i18n/character-fx.he.ts +72 -0
  222. package/src/i18n/character-fx.hi.ts +72 -0
  223. package/src/i18n/character-fx.ja.ts +72 -0
  224. package/src/i18n/character-fx.ko.ts +72 -0
  225. package/src/i18n/character-fx.pt-BR.ts +72 -0
  226. package/src/i18n/character-fx.ru.ts +72 -0
  227. package/src/i18n/character-fx.zh-CN.ts +72 -0
  228. package/src/i18n/color-look.ar.ts +50 -0
  229. package/src/i18n/color-look.de.ts +47 -0
  230. package/src/i18n/color-look.es.ts +50 -0
  231. package/src/i18n/color-look.fr.ts +50 -0
  232. package/src/i18n/color-look.he.ts +49 -0
  233. package/src/i18n/color-look.hi.ts +52 -0
  234. package/src/i18n/color-look.ja.ts +54 -0
  235. package/src/i18n/color-look.ko.ts +54 -0
  236. package/src/i18n/color-look.pt-BR.ts +54 -0
  237. package/src/i18n/color-look.ru.ts +52 -0
  238. package/src/i18n/color-look.zh-CN.ts +53 -0
  239. package/src/i18n/composition-effects.ar.ts +26 -0
  240. package/src/i18n/composition-effects.de.ts +25 -0
  241. package/src/i18n/composition-effects.es.ts +27 -0
  242. package/src/i18n/composition-effects.fr.ts +25 -0
  243. package/src/i18n/composition-effects.he.ts +25 -0
  244. package/src/i18n/composition-effects.hi.ts +27 -0
  245. package/src/i18n/composition-effects.ja.ts +27 -0
  246. package/src/i18n/composition-effects.ko.ts +27 -0
  247. package/src/i18n/composition-effects.pt-BR.ts +25 -0
  248. package/src/i18n/composition-effects.ru.ts +82 -0
  249. package/src/i18n/composition-effects.zh-CN.ts +25 -0
  250. package/src/i18n/era.ar.ts +37 -0
  251. package/src/i18n/era.de.ts +34 -0
  252. package/src/i18n/era.es.ts +37 -0
  253. package/src/i18n/era.fr.ts +37 -0
  254. package/src/i18n/era.he.ts +36 -0
  255. package/src/i18n/era.hi.ts +39 -0
  256. package/src/i18n/era.ja.ts +41 -0
  257. package/src/i18n/era.ko.ts +39 -0
  258. package/src/i18n/era.pt-BR.ts +41 -0
  259. package/src/i18n/era.ru.ts +121 -0
  260. package/src/i18n/era.zh-CN.ts +39 -0
  261. package/src/i18n/exposure-settings.ar.ts +28 -0
  262. package/src/i18n/exposure-settings.de.ts +26 -0
  263. package/src/i18n/exposure-settings.es.ts +28 -0
  264. package/src/i18n/exposure-settings.fr.ts +29 -0
  265. package/src/i18n/exposure-settings.he.ts +28 -0
  266. package/src/i18n/exposure-settings.hi.ts +31 -0
  267. package/src/i18n/exposure-settings.ja.ts +31 -0
  268. package/src/i18n/exposure-settings.ko.ts +31 -0
  269. package/src/i18n/exposure-settings.pt-BR.ts +31 -0
  270. package/src/i18n/exposure-settings.ru.ts +31 -0
  271. package/src/i18n/exposure-settings.zh-CN.ts +31 -0
  272. package/src/i18n/framing.ar.ts +78 -0
  273. package/src/i18n/framing.de.ts +73 -0
  274. package/src/i18n/framing.es.ts +78 -0
  275. package/src/i18n/framing.fr.ts +78 -0
  276. package/src/i18n/framing.he.ts +77 -0
  277. package/src/i18n/framing.hi.ts +82 -0
  278. package/src/i18n/framing.ja.ts +84 -0
  279. package/src/i18n/framing.ko.ts +82 -0
  280. package/src/i18n/framing.pt-BR.ts +84 -0
  281. package/src/i18n/framing.ru.ts +82 -0
  282. package/src/i18n/framing.zh-CN.ts +82 -0
  283. package/src/i18n/furniture.ar.ts +92 -0
  284. package/src/i18n/furniture.de.ts +93 -0
  285. package/src/i18n/furniture.es.ts +101 -0
  286. package/src/i18n/furniture.fr.ts +93 -0
  287. package/src/i18n/furniture.he.ts +101 -0
  288. package/src/i18n/furniture.hi.ts +101 -0
  289. package/src/i18n/furniture.ja.ts +101 -0
  290. package/src/i18n/furniture.ko.ts +101 -0
  291. package/src/i18n/furniture.pt-BR.ts +101 -0
  292. package/src/i18n/furniture.ru.ts +101 -0
  293. package/src/i18n/furniture.zh-CN.ts +101 -0
  294. package/src/i18n/held-prop.ar.ts +74 -0
  295. package/src/i18n/held-prop.de.ts +65 -0
  296. package/src/i18n/held-prop.es.ts +83 -0
  297. package/src/i18n/held-prop.fr.ts +74 -0
  298. package/src/i18n/held-prop.he.ts +73 -0
  299. package/src/i18n/held-prop.hi.ts +82 -0
  300. package/src/i18n/held-prop.ja.ts +84 -0
  301. package/src/i18n/held-prop.ko.ts +84 -0
  302. package/src/i18n/held-prop.pt-BR.ts +84 -0
  303. package/src/i18n/held-prop.ru.ts +82 -0
  304. package/src/i18n/held-prop.zh-CN.ts +82 -0
  305. package/src/i18n/index.ts +208 -0
  306. package/src/i18n/instrumentation.ar.ts +195 -0
  307. package/src/i18n/instrumentation.de.ts +195 -0
  308. package/src/i18n/instrumentation.es.ts +195 -0
  309. package/src/i18n/instrumentation.fr.ts +195 -0
  310. package/src/i18n/instrumentation.he.ts +195 -0
  311. package/src/i18n/instrumentation.hi.ts +195 -0
  312. package/src/i18n/instrumentation.ja.ts +195 -0
  313. package/src/i18n/instrumentation.ko.ts +195 -0
  314. package/src/i18n/instrumentation.pt-BR.ts +195 -0
  315. package/src/i18n/instrumentation.ru.ts +195 -0
  316. package/src/i18n/instrumentation.zh-CN.ts +195 -0
  317. package/src/i18n/lens.ar.ts +24 -0
  318. package/src/i18n/lens.de.ts +24 -0
  319. package/src/i18n/lens.es.ts +24 -0
  320. package/src/i18n/lens.fr.ts +24 -0
  321. package/src/i18n/lens.he.ts +24 -0
  322. package/src/i18n/lens.hi.ts +24 -0
  323. package/src/i18n/lens.ja.ts +24 -0
  324. package/src/i18n/lens.ko.ts +24 -0
  325. package/src/i18n/lens.pt-BR.ts +24 -0
  326. package/src/i18n/lens.ru.ts +25 -0
  327. package/src/i18n/lens.zh-CN.ts +24 -0
  328. package/src/i18n/lighting.ar.ts +86 -0
  329. package/src/i18n/lighting.de.ts +84 -0
  330. package/src/i18n/lighting.es.ts +89 -0
  331. package/src/i18n/lighting.fr.ts +84 -0
  332. package/src/i18n/lighting.he.ts +88 -0
  333. package/src/i18n/lighting.hi.ts +90 -0
  334. package/src/i18n/lighting.ja.ts +90 -0
  335. package/src/i18n/lighting.ko.ts +90 -0
  336. package/src/i18n/lighting.pt-BR.ts +90 -0
  337. package/src/i18n/lighting.ru.ts +88 -0
  338. package/src/i18n/lighting.zh-CN.ts +92 -0
  339. package/src/i18n/loop-subject.ar.ts +39 -0
  340. package/src/i18n/loop-subject.de.ts +39 -0
  341. package/src/i18n/loop-subject.es.ts +39 -0
  342. package/src/i18n/loop-subject.fr.ts +39 -0
  343. package/src/i18n/loop-subject.he.ts +39 -0
  344. package/src/i18n/loop-subject.hi.ts +39 -0
  345. package/src/i18n/loop-subject.ja.ts +39 -0
  346. package/src/i18n/loop-subject.ko.ts +39 -0
  347. package/src/i18n/loop-subject.pt-BR.ts +39 -0
  348. package/src/i18n/loop-subject.ru.ts +39 -0
  349. package/src/i18n/loop-subject.zh-CN.ts +39 -0
  350. package/src/i18n/materials.ar.ts +80 -0
  351. package/src/i18n/materials.de.ts +80 -0
  352. package/src/i18n/materials.es.ts +87 -0
  353. package/src/i18n/materials.fr.ts +80 -0
  354. package/src/i18n/materials.he.ts +86 -0
  355. package/src/i18n/materials.hi.ts +86 -0
  356. package/src/i18n/materials.ja.ts +88 -0
  357. package/src/i18n/materials.ko.ts +88 -0
  358. package/src/i18n/materials.pt-BR.ts +88 -0
  359. package/src/i18n/materials.ru.ts +86 -0
  360. package/src/i18n/materials.zh-CN.ts +86 -0
  361. package/src/i18n/mood.ar.ts +61 -0
  362. package/src/i18n/mood.de.ts +61 -0
  363. package/src/i18n/mood.es.ts +66 -0
  364. package/src/i18n/mood.fr.ts +61 -0
  365. package/src/i18n/mood.he.ts +64 -0
  366. package/src/i18n/mood.hi.ts +66 -0
  367. package/src/i18n/mood.ja.ts +66 -0
  368. package/src/i18n/mood.ko.ts +66 -0
  369. package/src/i18n/mood.pt-BR.ts +64 -0
  370. package/src/i18n/mood.ru.ts +64 -0
  371. package/src/i18n/mood.zh-CN.ts +64 -0
  372. package/src/i18n/music-genre.ar.ts +302 -0
  373. package/src/i18n/music-genre.de.ts +302 -0
  374. package/src/i18n/music-genre.es.ts +302 -0
  375. package/src/i18n/music-genre.fr.ts +302 -0
  376. package/src/i18n/music-genre.he.ts +302 -0
  377. package/src/i18n/music-genre.hi.ts +302 -0
  378. package/src/i18n/music-genre.ja.ts +302 -0
  379. package/src/i18n/music-genre.ko.ts +302 -0
  380. package/src/i18n/music-genre.pt-BR.ts +302 -0
  381. package/src/i18n/music-genre.ru.ts +302 -0
  382. package/src/i18n/music-genre.zh-CN.ts +302 -0
  383. package/src/i18n/music-mood.ar.ts +75 -0
  384. package/src/i18n/music-mood.de.ts +75 -0
  385. package/src/i18n/music-mood.es.ts +75 -0
  386. package/src/i18n/music-mood.fr.ts +75 -0
  387. package/src/i18n/music-mood.he.ts +75 -0
  388. package/src/i18n/music-mood.hi.ts +75 -0
  389. package/src/i18n/music-mood.ja.ts +75 -0
  390. package/src/i18n/music-mood.ko.ts +75 -0
  391. package/src/i18n/music-mood.pt-BR.ts +75 -0
  392. package/src/i18n/music-mood.ru.ts +75 -0
  393. package/src/i18n/music-mood.zh-CN.ts +75 -0
  394. package/src/i18n/person.ar.ts +638 -0
  395. package/src/i18n/person.de.ts +629 -0
  396. package/src/i18n/person.es.ts +645 -0
  397. package/src/i18n/person.fr.ts +621 -0
  398. package/src/i18n/person.he.ts +656 -0
  399. package/src/i18n/person.hi.ts +654 -0
  400. package/src/i18n/person.ja.ts +662 -0
  401. package/src/i18n/person.ko.ts +662 -0
  402. package/src/i18n/person.pt-BR.ts +656 -0
  403. package/src/i18n/person.ru.ts +656 -0
  404. package/src/i18n/person.zh-CN.ts +656 -0
  405. package/src/i18n/photo-genre.ar.ts +58 -0
  406. package/src/i18n/photo-genre.de.ts +52 -0
  407. package/src/i18n/photo-genre.es.ts +64 -0
  408. package/src/i18n/photo-genre.fr.ts +58 -0
  409. package/src/i18n/photo-genre.he.ts +62 -0
  410. package/src/i18n/photo-genre.hi.ts +63 -0
  411. package/src/i18n/photo-genre.ja.ts +65 -0
  412. package/src/i18n/photo-genre.ko.ts +63 -0
  413. package/src/i18n/photo-genre.pt-BR.ts +65 -0
  414. package/src/i18n/photo-genre.ru.ts +63 -0
  415. package/src/i18n/photo-genre.zh-CN.ts +63 -0
  416. package/src/i18n/photographer.ar.ts +75 -0
  417. package/src/i18n/photographer.de.ts +74 -0
  418. package/src/i18n/photographer.es.ts +77 -0
  419. package/src/i18n/photographer.fr.ts +77 -0
  420. package/src/i18n/photographer.he.ts +78 -0
  421. package/src/i18n/photographer.hi.ts +80 -0
  422. package/src/i18n/photographer.ja.ts +84 -0
  423. package/src/i18n/photographer.ko.ts +84 -0
  424. package/src/i18n/photographer.pt-BR.ts +83 -0
  425. package/src/i18n/photographer.ru.ts +78 -0
  426. package/src/i18n/photographer.zh-CN.ts +78 -0
  427. package/src/i18n/pose.ar.ts +95 -0
  428. package/src/i18n/pose.de.ts +96 -0
  429. package/src/i18n/pose.es.ts +104 -0
  430. package/src/i18n/pose.fr.ts +96 -0
  431. package/src/i18n/pose.he.ts +104 -0
  432. package/src/i18n/pose.hi.ts +104 -0
  433. package/src/i18n/pose.ja.ts +104 -0
  434. package/src/i18n/pose.ko.ts +104 -0
  435. package/src/i18n/pose.pt-BR.ts +104 -0
  436. package/src/i18n/pose.ru.ts +104 -0
  437. package/src/i18n/pose.zh-CN.ts +104 -0
  438. package/src/i18n/post-process-effects.ar.ts +25 -0
  439. package/src/i18n/post-process-effects.de.ts +24 -0
  440. package/src/i18n/post-process-effects.es.ts +26 -0
  441. package/src/i18n/post-process-effects.fr.ts +24 -0
  442. package/src/i18n/post-process-effects.he.ts +24 -0
  443. package/src/i18n/post-process-effects.hi.ts +26 -0
  444. package/src/i18n/post-process-effects.ja.ts +26 -0
  445. package/src/i18n/post-process-effects.ko.ts +26 -0
  446. package/src/i18n/post-process-effects.pt-BR.ts +24 -0
  447. package/src/i18n/post-process-effects.ru.ts +24 -0
  448. package/src/i18n/post-process-effects.zh-CN.ts +24 -0
  449. package/src/i18n/render-quality.ar.ts +34 -0
  450. package/src/i18n/render-quality.de.ts +30 -0
  451. package/src/i18n/render-quality.es.ts +35 -0
  452. package/src/i18n/render-quality.fr.ts +34 -0
  453. package/src/i18n/render-quality.he.ts +33 -0
  454. package/src/i18n/render-quality.hi.ts +39 -0
  455. package/src/i18n/render-quality.ja.ts +39 -0
  456. package/src/i18n/render-quality.ko.ts +39 -0
  457. package/src/i18n/render-quality.pt-BR.ts +37 -0
  458. package/src/i18n/render-quality.ru.ts +37 -0
  459. package/src/i18n/render-quality.zh-CN.ts +37 -0
  460. package/src/i18n/seasons.ar.ts +10 -0
  461. package/src/i18n/seasons.de.ts +10 -0
  462. package/src/i18n/seasons.es.ts +10 -0
  463. package/src/i18n/seasons.fr.ts +10 -0
  464. package/src/i18n/seasons.he.ts +10 -0
  465. package/src/i18n/seasons.hi.ts +10 -0
  466. package/src/i18n/seasons.ja.ts +10 -0
  467. package/src/i18n/seasons.ko.ts +10 -0
  468. package/src/i18n/seasons.pt-BR.ts +10 -0
  469. package/src/i18n/seasons.ru.ts +10 -0
  470. package/src/i18n/seasons.zh-CN.ts +10 -0
  471. package/src/i18n/setting.ar.ts +73 -0
  472. package/src/i18n/setting.de.ts +73 -0
  473. package/src/i18n/setting.es.ts +77 -0
  474. package/src/i18n/setting.fr.ts +73 -0
  475. package/src/i18n/setting.he.ts +76 -0
  476. package/src/i18n/setting.hi.ts +76 -0
  477. package/src/i18n/setting.ja.ts +78 -0
  478. package/src/i18n/setting.ko.ts +76 -0
  479. package/src/i18n/setting.pt-BR.ts +78 -0
  480. package/src/i18n/setting.ru.ts +76 -0
  481. package/src/i18n/setting.zh-CN.ts +76 -0
  482. package/src/i18n/style.ar.ts +55 -0
  483. package/src/i18n/style.de.ts +54 -0
  484. package/src/i18n/style.es.ts +54 -0
  485. package/src/i18n/style.fr.ts +54 -0
  486. package/src/i18n/style.he.ts +54 -0
  487. package/src/i18n/style.hi.ts +54 -0
  488. package/src/i18n/style.ja.ts +54 -0
  489. package/src/i18n/style.ko.ts +54 -0
  490. package/src/i18n/style.pt-BR.ts +56 -0
  491. package/src/i18n/style.ru.ts +54 -0
  492. package/src/i18n/style.zh-CN.ts +54 -0
  493. package/src/i18n/styling.ar.ts +295 -0
  494. package/src/i18n/styling.de.ts +285 -0
  495. package/src/i18n/styling.es.ts +317 -0
  496. package/src/i18n/styling.fr.ts +285 -0
  497. package/src/i18n/styling.he.ts +307 -0
  498. package/src/i18n/styling.hi.ts +323 -0
  499. package/src/i18n/styling.ja.ts +317 -0
  500. package/src/i18n/styling.ko.ts +311 -0
  501. package/src/i18n/styling.pt-BR.ts +301 -0
  502. package/src/i18n/styling.ru.ts +301 -0
  503. package/src/i18n/styling.zh-CN.ts +307 -0
  504. package/src/i18n/temporal.ar.ts +31 -0
  505. package/src/i18n/temporal.de.ts +24 -0
  506. package/src/i18n/temporal.es.ts +27 -0
  507. package/src/i18n/temporal.fr.ts +28 -0
  508. package/src/i18n/temporal.he.ts +31 -0
  509. package/src/i18n/temporal.hi.ts +31 -0
  510. package/src/i18n/temporal.ja.ts +31 -0
  511. package/src/i18n/temporal.ko.ts +31 -0
  512. package/src/i18n/temporal.pt-BR.ts +31 -0
  513. package/src/i18n/temporal.ru.ts +31 -0
  514. package/src/i18n/temporal.zh-CN.ts +31 -0
  515. package/src/i18n/transitions.ar.ts +97 -0
  516. package/src/i18n/transitions.de.ts +97 -0
  517. package/src/i18n/transitions.es.ts +97 -0
  518. package/src/i18n/transitions.fr.ts +97 -0
  519. package/src/i18n/transitions.he.ts +97 -0
  520. package/src/i18n/transitions.hi.ts +97 -0
  521. package/src/i18n/transitions.ja.ts +97 -0
  522. package/src/i18n/transitions.ko.ts +97 -0
  523. package/src/i18n/transitions.pt-BR.ts +97 -0
  524. package/src/i18n/transitions.ru.ts +97 -0
  525. package/src/i18n/transitions.zh-CN.ts +97 -0
  526. package/src/i18n/types.ts +158 -0
  527. package/src/i18n/vehicles.ar.ts +137 -0
  528. package/src/i18n/vehicles.de.ts +125 -0
  529. package/src/i18n/vehicles.es.ts +138 -0
  530. package/src/i18n/vehicles.fr.ts +125 -0
  531. package/src/i18n/vehicles.he.ts +136 -0
  532. package/src/i18n/vehicles.hi.ts +138 -0
  533. package/src/i18n/vehicles.ja.ts +138 -0
  534. package/src/i18n/vehicles.ko.ts +138 -0
  535. package/src/i18n/vehicles.pt-BR.ts +136 -0
  536. package/src/i18n/vehicles.ru.ts +136 -0
  537. package/src/i18n/vehicles.zh-CN.ts +136 -0
  538. package/src/i18n/voice-character.ar.ts +126 -0
  539. package/src/i18n/voice-character.de.ts +126 -0
  540. package/src/i18n/voice-character.es.ts +126 -0
  541. package/src/i18n/voice-character.fr.ts +126 -0
  542. package/src/i18n/voice-character.he.ts +126 -0
  543. package/src/i18n/voice-character.hi.ts +126 -0
  544. package/src/i18n/voice-character.ja.ts +126 -0
  545. package/src/i18n/voice-character.ko.ts +126 -0
  546. package/src/i18n/voice-character.pt-BR.ts +126 -0
  547. package/src/i18n/voice-character.ru.ts +126 -0
  548. package/src/i18n/voice-character.zh-CN.ts +126 -0
  549. package/src/i18n/voice-delivery.ar.ts +77 -0
  550. package/src/i18n/voice-delivery.de.ts +77 -0
  551. package/src/i18n/voice-delivery.es.ts +77 -0
  552. package/src/i18n/voice-delivery.fr.ts +77 -0
  553. package/src/i18n/voice-delivery.he.ts +77 -0
  554. package/src/i18n/voice-delivery.hi.ts +77 -0
  555. package/src/i18n/voice-delivery.ja.ts +77 -0
  556. package/src/i18n/voice-delivery.ko.ts +77 -0
  557. package/src/i18n/voice-delivery.pt-BR.ts +77 -0
  558. package/src/i18n/voice-delivery.ru.ts +77 -0
  559. package/src/i18n/voice-delivery.zh-CN.ts +77 -0
  560. package/src/i18n/weapons.ar.ts +115 -0
  561. package/src/i18n/weapons.de.ts +114 -0
  562. package/src/i18n/weapons.es.ts +116 -0
  563. package/src/i18n/weapons.fr.ts +103 -0
  564. package/src/i18n/weapons.he.ts +114 -0
  565. package/src/i18n/weapons.hi.ts +116 -0
  566. package/src/i18n/weapons.ja.ts +116 -0
  567. package/src/i18n/weapons.ko.ts +116 -0
  568. package/src/i18n/weapons.pt-BR.ts +114 -0
  569. package/src/i18n/weapons.ru.ts +114 -0
  570. package/src/i18n/weapons.zh-CN.ts +114 -0
  571. package/src/image-critic-node.ts +61 -0
  572. package/src/image-critic-types.ts +40 -0
  573. package/src/index.ts +866 -0
  574. package/src/inject-upstream.ts +15 -0
  575. package/src/inpaint-region.ts +51 -0
  576. package/src/json-evaluator.ts +732 -0
  577. package/src/json-path.ts +52 -0
  578. package/src/lip-sync-pricing.ts +90 -0
  579. package/src/list-merge.ts +40 -0
  580. package/src/list-sort.ts +122 -0
  581. package/src/list-source-resolver.ts +50 -0
  582. package/src/llm-models.ts +245 -0
  583. package/src/location-mention-slug.ts +291 -0
  584. package/src/location-preset-catalog-map.ts +128 -0
  585. package/src/loop-delimiter.ts +73 -0
  586. package/src/lora-routing.ts +95 -0
  587. package/src/lottie-overlay-catalog.ts +175 -0
  588. package/src/lottie-slots.ts +309 -0
  589. package/src/model-catalog.ts +2386 -0
  590. package/src/model-constants.ts +1910 -0
  591. package/src/model-tree.ts +120 -0
  592. package/src/monetization.ts +15 -0
  593. package/src/multi-pick.ts +42 -0
  594. package/src/node-default-mappings.ts +262 -0
  595. package/src/node-mappable-fields.ts +70 -0
  596. package/src/node-preset-export.ts +56 -0
  597. package/src/node-preset-extract.ts +147 -0
  598. package/src/node-refs.ts +253 -0
  599. package/src/node-runtime-keys.ts +117 -0
  600. package/src/object-aspect-defaults.ts +72 -0
  601. package/src/object-picker-types.ts +22 -0
  602. package/src/parameter-node-value.ts +284 -0
  603. package/src/pinterest-url.ts +28 -0
  604. package/src/pipeline-chat.ts +72 -0
  605. package/src/pipeline-defaults.ts +318 -0
  606. package/src/pipeline-events.ts +162 -0
  607. package/src/pipeline-state-types.ts +196 -0
  608. package/src/pipeline-types.ts +647 -0
  609. package/src/pipeline-validation.ts +87 -0
  610. package/src/presentation-types.ts +23 -0
  611. package/src/presentation-utils.ts +397 -0
  612. package/src/producer-types.ts +188 -0
  613. package/src/progress-curve.ts +99 -0
  614. package/src/provider-directive-defaults.ts +69 -0
  615. package/src/reduce-strategy-registry.ts +121 -0
  616. package/src/reference-board-templates.ts +81 -0
  617. package/src/reference-roles.ts +153 -0
  618. package/src/reference-sheet/__tests__/board-to-column.test.ts +20 -0
  619. package/src/reference-sheet/__tests__/catalog.test.ts +50 -0
  620. package/src/reference-sheet/__tests__/cost.test.ts +38 -0
  621. package/src/reference-sheet/__tests__/no-drift.test.ts +28 -0
  622. package/src/reference-sheet/__tests__/panel-plan.test.ts +61 -0
  623. package/src/reference-sheet/__tests__/panel-prompts.test.ts +17 -0
  624. package/src/reference-sheet/__tests__/plan-generation.test.ts +72 -0
  625. package/src/reference-sheet/__tests__/presets.test.ts +54 -0
  626. package/src/reference-sheet/__tests__/types.test.ts +24 -0
  627. package/src/reference-sheet/catalog.ts +144 -0
  628. package/src/reference-sheet/cost.ts +37 -0
  629. package/src/reference-sheet/credit-id.ts +7 -0
  630. package/src/reference-sheet/index.ts +8 -0
  631. package/src/reference-sheet/panel-plan.ts +65 -0
  632. package/src/reference-sheet/panel-prompts.ts +14 -0
  633. package/src/reference-sheet/plan-generation.ts +79 -0
  634. package/src/reference-sheet/presets.ts +77 -0
  635. package/src/reference-sheet/types.ts +92 -0
  636. package/src/repeat-types.ts +97 -0
  637. package/src/resolve-field-mappings.ts +119 -0
  638. package/src/route-filter.ts +80 -0
  639. package/src/scene-helper-types.ts +249 -0
  640. package/src/scene-node-types.ts +281 -0
  641. package/src/scraper-actors.ts +57 -0
  642. package/src/scraper-output-schemas.ts +25 -0
  643. package/src/seasons.ts +45 -0
  644. package/src/selector.ts +864 -0
  645. package/src/settled-with-limit.ts +42 -0
  646. package/src/shot-element.ts +43 -0
  647. package/src/social-media-specs.ts +121 -0
  648. package/src/social-post.ts +24 -0
  649. package/src/sub-workflow-validation.ts +78 -0
  650. package/src/supported-fonts.ts +40 -0
  651. package/src/surround.ts +113 -0
  652. package/src/switchx-pricing.ts +33 -0
  653. package/src/text-separators.ts +59 -0
  654. package/src/to-connected-references.ts +77 -0
  655. package/src/types.ts +272 -0
  656. package/src/variant-job-id.ts +17 -0
  657. package/src/vehicles.ts +213 -0
  658. package/src/video-analysis-pricing.ts +74 -0
  659. package/src/video-analysis.ts +127 -0
  660. package/src/video-duration.ts +25 -0
  661. package/src/voice-changer-models.ts +36 -0
  662. package/src/voice-matcher-types.ts +37 -0
  663. package/src/voices.ts +92 -0
  664. package/src/weapons.ts +190 -0
  665. package/src/workflow-export.ts +153 -0
@@ -0,0 +1,87 @@
1
+ import type { ShowrunnerPlan } from "./pipeline-types"
2
+
3
+ export interface ObjectsValidationIssue {
4
+ severity: "blocking" | "warning"
5
+ type:
6
+ | "duplicate_key"
7
+ | "empty_significance"
8
+ | "orphan_object"
9
+ | "unresolved_scene_object_ref"
10
+ object_key?: string
11
+ scene_index?: number
12
+ message: string
13
+ }
14
+
15
+ export interface ObjectsValidationResult {
16
+ ok: boolean
17
+ verdict: "pass" | "fail"
18
+ issues: ObjectsValidationIssue[]
19
+ }
20
+
21
+ export function validateObjects(
22
+ objects: ShowrunnerPlan["objects"],
23
+ plan: ShowrunnerPlan,
24
+ ): ObjectsValidationResult {
25
+ const issues: ObjectsValidationIssue[] = []
26
+
27
+ // 1. Duplicate keys
28
+ const seenKeys = new Map<string, number>()
29
+ for (const obj of objects) {
30
+ seenKeys.set(obj.key, (seenKeys.get(obj.key) ?? 0) + 1)
31
+ }
32
+ for (const [key, count] of seenKeys) {
33
+ if (count > 1) {
34
+ issues.push({
35
+ severity: "blocking",
36
+ type: "duplicate_key",
37
+ object_key: key,
38
+ message: `Object key "${key}" appears ${count} times — keys must be unique`,
39
+ })
40
+ }
41
+ }
42
+
43
+ // 2. Empty narrative_significance
44
+ for (const obj of objects) {
45
+ if (!obj.narrative_significance || obj.narrative_significance.trim() === "") {
46
+ issues.push({
47
+ severity: "blocking",
48
+ type: "empty_significance",
49
+ object_key: obj.key,
50
+ message: `Object "${obj.key}" has empty narrative_significance`,
51
+ })
52
+ }
53
+ }
54
+
55
+ // 3. Unresolved scene → object refs
56
+ const objectKeySet = new Set(objects.map((o) => o.key))
57
+ const referencedKeys = new Set<string>()
58
+ for (const sc of plan.scenes) {
59
+ for (const k of sc.object_keys) {
60
+ referencedKeys.add(k)
61
+ if (!objectKeySet.has(k)) {
62
+ issues.push({
63
+ severity: "blocking",
64
+ type: "unresolved_scene_object_ref",
65
+ object_key: k,
66
+ scene_index: sc.scene_index,
67
+ message: `Scene ${sc.scene_index} references unknown object_key "${k}"`,
68
+ })
69
+ }
70
+ }
71
+ }
72
+
73
+ // 4. Orphan objects (listed but never referenced) — warning only
74
+ for (const obj of objects) {
75
+ if (!referencedKeys.has(obj.key)) {
76
+ issues.push({
77
+ severity: "warning",
78
+ type: "orphan_object",
79
+ object_key: obj.key,
80
+ message: `Object "${obj.key}" listed but no scene references it`,
81
+ })
82
+ }
83
+ }
84
+
85
+ const ok = !issues.some((i) => i.severity === "blocking")
86
+ return { ok, verdict: ok ? "pass" : "fail", issues }
87
+ }
@@ -0,0 +1,23 @@
1
+ export interface ExposableField {
2
+ readonly key: string
3
+ readonly label: string
4
+ readonly type: "select" | "slider" | "toggle" | "text" | "aspect-ratio" | "color"
5
+ readonly options?: ReadonlyArray<{ value: string; label: string }>
6
+ readonly min?: number
7
+ readonly max?: number
8
+ readonly step?: number
9
+ readonly defaultValue?: unknown
10
+ }
11
+
12
+ export interface ExposableOutput {
13
+ readonly key: string
14
+ readonly label: string
15
+ readonly outputType: "image" | "video" | "audio" | "text" | "data"
16
+ }
17
+
18
+ export type PresentationItem =
19
+ | { type: "node"; nodeId: string }
20
+ | { type: "field"; id: string; nodeId: string; field: string; allowedValues?: Array<string | number | boolean> }
21
+ | { type: "output"; id: string; nodeId: string; outputKey: string }
22
+ | { type: "richtext"; id: string; content: string }
23
+ | { type: "group"; id: string; title: string; items: PresentationItem[]; showTitle?: boolean; showBackground?: boolean }
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Presentation / API utilities — pure logic, no React deps.
3
+ * Shared between frontend (presentation mode) and backend (API schema generation).
4
+ */
5
+
6
+ import type { GenericNode, GenericEdge } from "./types.js"
7
+ import type { PresentationItem } from "./presentation-types.js"
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Node type sets
11
+ // ---------------------------------------------------------------------------
12
+
13
+ export const INPUT_NODE_TYPES = new Set([
14
+ "text-prompt",
15
+ "upload-image",
16
+ "upload-video",
17
+ "upload-audio",
18
+ "tone",
19
+ "style-guide",
20
+ "provider",
21
+ "scene-count",
22
+ "duration",
23
+ "aspect-ratio",
24
+ "motion",
25
+ "camera-motion",
26
+ "framing",
27
+ "lens",
28
+ "camera-format",
29
+ "lighting",
30
+ "color-look",
31
+ "atmosphere",
32
+ "action-fx",
33
+ "temporal",
34
+ "reference-audio",
35
+ ])
36
+
37
+ const TRIGGER_NODE_TYPES = new Set([
38
+ "webhook-trigger",
39
+ "schedule-trigger",
40
+ ])
41
+
42
+ const ALWAYS_EXCLUDED_TYPES = new Set([
43
+ "sticky-note",
44
+ "webhook-trigger",
45
+ "schedule-trigger",
46
+ "sub-workflow-input",
47
+ "sub-workflow-output",
48
+ ])
49
+
50
+ const NON_OUTPUT_TYPES = new Set([
51
+ "text-prompt",
52
+ "list",
53
+ "upload-image",
54
+ "upload-video",
55
+ "upload-audio",
56
+ "reference-audio",
57
+ "rss-feed",
58
+ "youtube-video",
59
+ "tone",
60
+ "style-guide",
61
+ "provider",
62
+ "scene-count",
63
+ "duration",
64
+ "aspect-ratio",
65
+ "motion",
66
+ "camera-motion",
67
+ "framing",
68
+ "lens",
69
+ "camera-format",
70
+ "lighting",
71
+ "color-look",
72
+ "atmosphere",
73
+ "action-fx",
74
+ "temporal",
75
+ "sticky-note",
76
+ "sub-workflow-input",
77
+ "sub-workflow-output",
78
+ "webhook-trigger",
79
+ "schedule-trigger",
80
+ "combine-text",
81
+ "split-text",
82
+ "save-to-storage",
83
+ "webhook-output",
84
+ "component",
85
+ ])
86
+
87
+ const MEDIA_PRODUCING_TYPES = new Set([
88
+ "generate-image",
89
+ "edit-image",
90
+ "image-to-image",
91
+ "image-to-video",
92
+ "text-to-video",
93
+ "video-to-video",
94
+ "text-to-speech",
95
+ "generate-music",
96
+ "text-to-audio",
97
+ "generate-script",
98
+ "ai-writer",
99
+ "llm-chat",
100
+ "render-video",
101
+ "video-composer",
102
+ "after-effects",
103
+ "lottie-overlay",
104
+ "3d-title",
105
+ "motion-graphics",
106
+ "composite",
107
+ "text-to-dialogue",
108
+ "voice-changer",
109
+ "dubbing",
110
+ "voice-remix",
111
+ "voice-design",
112
+ "extend-video",
113
+ "lip-sync",
114
+ "motion-transfer",
115
+ "video-upscale",
116
+ ])
117
+
118
+ // Output type categorizations
119
+ const IMAGE_OUTPUT_TYPES = new Set([
120
+ "generate-image", "edit-image", "image-to-image",
121
+ "upload-image",
122
+ ])
123
+
124
+ const VIDEO_OUTPUT_TYPES = new Set([
125
+ "image-to-video", "text-to-video", "video-to-video", "extend-video",
126
+ "render-video", "video-composer", "after-effects", "lottie-overlay",
127
+ "3d-title", "motion-graphics", "composite",
128
+ "combine-videos", "merge-video-audio", "resize-video", "trim-video",
129
+ "speed-ramp", "loop-video", "fade-video", "transcode-video",
130
+ "upload-video",
131
+ "lip-sync", "motion-transfer", "video-upscale", "add-captions",
132
+ "social-media-format",
133
+ ])
134
+
135
+ const AUDIO_OUTPUT_TYPES = new Set([
136
+ "text-to-speech", "generate-music", "text-to-audio",
137
+ "text-to-dialogue", "voice-changer", "dubbing", "voice-remix",
138
+ "voice-design", "mix-audio", "adjust-volume", "audio-fx", "trim-audio",
139
+ "audio-isolation",
140
+ "audio-separation",
141
+ "upload-audio",
142
+ ])
143
+
144
+ const TEXT_OUTPUT_TYPES = new Set([
145
+ "generate-script", "ai-writer", "llm-chat", "transcribe", "image-to-text", "qa-check",
146
+ "text-prompt",
147
+ ])
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Output type enum
151
+ // ---------------------------------------------------------------------------
152
+
153
+ export type OutputType = "image" | "video" | "audio" | "text" | "data"
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Core functions
157
+ // ---------------------------------------------------------------------------
158
+
159
+ /** Get nodes that act as user inputs (text prompts, uploads, parameters). */
160
+ export function getInputNodes<T extends GenericNode>(nodes: T[], curatedOnly = true): T[] {
161
+ return nodes.filter((n) => {
162
+ if (!n.type || ALWAYS_EXCLUDED_TYPES.has(n.type)) return false
163
+ if (n.hidden) return false
164
+ if (curatedOnly) {
165
+ // New explicit flag
166
+ if (n.data.presentationInput === true) return true
167
+ // Backwards compat: old flag on input-type nodes
168
+ if (n.data.presentationVisible === true && INPUT_NODE_TYPES.has(n.type)) return true
169
+ return false
170
+ }
171
+ return true
172
+ })
173
+ }
174
+
175
+ /** Get leaf/media-producing nodes that represent workflow outputs. */
176
+ export function getOutputNodes<T extends GenericNode>(
177
+ nodes: T[],
178
+ edges: GenericEdge[],
179
+ curatedOnly = true,
180
+ ): T[] {
181
+ const nodesWithOutgoing = new Set(edges.map((e) => e.source))
182
+
183
+ return nodes.filter((n) => {
184
+ if (!n.type || ALWAYS_EXCLUDED_TYPES.has(n.type)) return false
185
+ if (n.hidden) return false
186
+ if (curatedOnly) {
187
+ // New explicit flag
188
+ if (n.data.presentationOutput === true) return true
189
+ // Backwards compat: old flag on output-eligible nodes
190
+ if (n.data.presentationVisible === true) {
191
+ if (NON_OUTPUT_TYPES.has(n.type)) return false
192
+ return !nodesWithOutgoing.has(n.id) || MEDIA_PRODUCING_TYPES.has(n.type)
193
+ }
194
+ return false
195
+ }
196
+ return true
197
+ })
198
+ }
199
+
200
+ /** Map node type to its output media type. */
201
+ export function getOutputType(nodeType: string | undefined): OutputType {
202
+ if (!nodeType) return "data"
203
+ if (IMAGE_OUTPUT_TYPES.has(nodeType)) return "image"
204
+ if (VIDEO_OUTPUT_TYPES.has(nodeType)) return "video"
205
+ if (AUDIO_OUTPUT_TYPES.has(nodeType)) return "audio"
206
+ if (TEXT_OUTPUT_TYPES.has(nodeType)) return "text"
207
+ return "data"
208
+ }
209
+
210
+ /** Extract the result URL or text from a node's data. */
211
+ export function getNodeResult(
212
+ nodeData: Record<string, unknown>,
213
+ ): { url?: string; text?: string } {
214
+ // Preview node: extract first visible item from previewItems
215
+ const previewItems = nodeData.previewItems as
216
+ | Array<{ type: string; value: string; visible?: boolean }>
217
+ | undefined
218
+ if (previewItems && previewItems.length > 0) {
219
+ const first = previewItems.find((item) => item.visible !== false) ?? previewItems[0]
220
+ if (first) {
221
+ if (first.type === "image" || first.type === "video" || first.type === "audio") {
222
+ return { url: first.value }
223
+ }
224
+ return { text: first.value }
225
+ }
226
+ }
227
+
228
+ const results = nodeData.generatedResults as
229
+ | Array<Record<string, unknown>>
230
+ | undefined
231
+
232
+ if (results && results.length > 0) {
233
+ const idx = (nodeData.activeResultIndex as number) ?? 0
234
+ const active = results[idx] ?? results[0]
235
+ const url = (active?.url ?? active?.imageUrl ?? active?.videoUrl ?? active?.audioUrl) as
236
+ | string
237
+ | undefined
238
+ const text = (active?.text ?? active?.script) as string | undefined
239
+ return { url, text }
240
+ }
241
+
242
+ // Fallback to individual generated fields
243
+ const url = (nodeData.generatedImageUrl ??
244
+ nodeData.generatedVideoUrl ??
245
+ nodeData.generatedAudioUrl) as string | undefined
246
+ const text = (nodeData.generatedScript ?? nodeData.generatedText) as
247
+ | string
248
+ | undefined
249
+
250
+ return { url, text }
251
+ }
252
+
253
+ /** Human-readable label for a node. */
254
+ export function getNodeLabel(node: GenericNode): string {
255
+ return (
256
+ (node.data.label as string) ||
257
+ node.type?.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) ||
258
+ "Node"
259
+ )
260
+ }
261
+
262
+ // ---------------------------------------------------------------------------
263
+ // Input field schema (for API schema generation)
264
+ // ---------------------------------------------------------------------------
265
+
266
+ export interface InputFieldSchema {
267
+ key: string
268
+ type: "text" | "image-url" | "video-url" | "audio-url" | "select" | "number"
269
+ options?: string[]
270
+ }
271
+
272
+ // Override field per input-node type. The `key` for a parameter-picker MUST be
273
+ // the field that `getParameterValue()` (parameter-node-value.ts) actually reads
274
+ // — for multi-dimension pickers, its FIRST per-dimension field (the
275
+ // "representative key"). A mismatch silently drops the curated app/MCP/SDK input
276
+ // (the override lands on a field nothing reads). This map is the single source
277
+ // of truth for app-input override fields (consumed by extract-app-inputs.ts,
278
+ // api-tokens.ts, and the app-runner). Its parity with getParameterValue is
279
+ // guarded by input-field-map-parity.test.ts so the two can't drift.
280
+ export const INPUT_FIELD_MAP: Record<string, InputFieldSchema> = {
281
+ // --- Source / upload nodes (not parameter pickers) ---
282
+ "text-prompt": { key: "text", type: "text" },
283
+ "upload-image": { key: "url", type: "image-url" },
284
+ "upload-video": { key: "url", type: "video-url" },
285
+ "upload-audio": { key: "url", type: "audio-url" },
286
+ "reference-audio": { key: "extractedAudioUrl", type: "audio-url" },
287
+ // --- Classic parameters ---
288
+ "provider": { key: "provider", type: "select" },
289
+ "style-guide": { key: "text", type: "text" }, // reads data.text (was wrongly "styleGuide")
290
+ "scene-count": { key: "count", type: "number" },
291
+ "duration": { key: "seconds", type: "number" }, // reads data.seconds (was wrongly "duration")
292
+ "aspect-ratio": { key: "ratio", type: "select" }, // reads data.ratio (was wrongly "aspectRatio")
293
+ // --- Single-field parameter pickers ---
294
+ "tone": { key: "tone", type: "select" },
295
+ "motion": { key: "motion", type: "select" },
296
+ "camera-motion": { key: "cameraMotion", type: "select" },
297
+ "lens": { key: "lens", type: "select" },
298
+ "camera-format": { key: "cameraFormat", type: "select" },
299
+ "color-look": { key: "colorLook", type: "select" },
300
+ "atmosphere": { key: "atmosphere", type: "select" },
301
+ "style": { key: "style", type: "select" },
302
+ "setting": { key: "setting", type: "select" },
303
+ "mood": { key: "mood", type: "select" },
304
+ "photographer": { key: "photographer", type: "select" },
305
+ "aesthetic": { key: "aesthetic", type: "select" },
306
+ "era": { key: "era", type: "select" },
307
+ "material": { key: "material", type: "select" },
308
+ "animal": { key: "animal", type: "select" },
309
+ "vehicle": { key: "vehicle", type: "select" },
310
+ "weapon": { key: "weapon", type: "select" },
311
+ "furniture": { key: "furniture", type: "select" },
312
+ "photo-genre": { key: "photoGenre", type: "select" },
313
+ "backdrop": { key: "backdrop", type: "select" },
314
+ "held-prop": { key: "heldProp", type: "select" },
315
+ "render-quality": { key: "renderQuality", type: "select" },
316
+ "composition-effects": { key: "compositionEffect", type: "select" },
317
+ "post-process-effects": { key: "postProcess", type: "select" },
318
+ "action-fx": { key: "actionFx", type: "select" },
319
+ "character-fx": { key: "characterFx", type: "select" },
320
+ "transition": { key: "transition", type: "select" },
321
+ "loop-subject": { key: "loopSubject", type: "select" },
322
+ // --- Multi-dimension pickers: representative (first) field. Overriding only
323
+ // sets that dimension; full hint composition runs in the executors. ---
324
+ "framing": { key: "shotSize", type: "select" },
325
+ "lighting": { key: "timeOfDay", type: "select" },
326
+ "temporal": { key: "temporalSpeed", type: "select" },
327
+ "person": { key: "type", type: "select" },
328
+ "pose": { key: "pose", type: "select" },
329
+ "styling": { key: "makeup", type: "select" },
330
+ "exposure-settings": { key: "aperture", type: "select" },
331
+ "music-genre": { key: "subgenre", type: "select" },
332
+ "music-mood": { key: "emotion", type: "select" },
333
+ "instrumentation": { key: "production", type: "select" },
334
+ "voice-character": { key: "timbre", type: "select" },
335
+ "voice-delivery": { key: "archetype", type: "select" },
336
+ }
337
+
338
+ /** Get the overridable field schema for an input node type. */
339
+ export function getInputFieldSchema(nodeType: string): InputFieldSchema | undefined {
340
+ return INPUT_FIELD_MAP[nodeType]
341
+ }
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // Migration & validation helpers (PresentationItem)
345
+ // ---------------------------------------------------------------------------
346
+
347
+ /** Migrate a legacy string[] order to PresentationItem[]. */
348
+ export function migrateToItems(order: string[] | undefined): PresentationItem[] | undefined {
349
+ if (!order) return undefined
350
+ return order.map((nodeId) => ({ type: "node" as const, nodeId }))
351
+ }
352
+
353
+ /** Strip nested groups — groups may only contain non-group items. */
354
+ export function validateNoNestedGroups(items: PresentationItem[]): PresentationItem[] {
355
+ return items.map((item) => {
356
+ if (item.type === "group") {
357
+ return { ...item, items: item.items.filter((child) => child.type !== "group") }
358
+ }
359
+ return item
360
+ })
361
+ }
362
+
363
+ /** Recursively flatten all items, unwrapping groups. */
364
+ export function flattenItems(items: PresentationItem[]): PresentationItem[] {
365
+ const result: PresentationItem[] = []
366
+ for (const item of items) {
367
+ if (item.type === "group") {
368
+ result.push(...flattenItems(item.items))
369
+ } else {
370
+ result.push(item)
371
+ }
372
+ }
373
+ return result
374
+ }
375
+
376
+ /** Get the sortable/unique ID for a PresentationItem. */
377
+ export function getItemSortId(item: PresentationItem): string {
378
+ return item.type === "node" ? item.nodeId : item.id
379
+ }
380
+
381
+ /** Remove items whose nodeId no longer exists in the workflow. */
382
+ export function cleanOrphanedItems(
383
+ items: PresentationItem[],
384
+ nodeIds: Set<string>
385
+ ): PresentationItem[] {
386
+ return items
387
+ .filter((item) => {
388
+ if (item.type === "node" || item.type === "field" || item.type === "output") return nodeIds.has(item.nodeId)
389
+ return true
390
+ })
391
+ .map((item) => {
392
+ if (item.type === "group") {
393
+ return { ...item, items: cleanOrphanedItems(item.items, nodeIds) }
394
+ }
395
+ return item
396
+ })
397
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Producer-type sets — single source of truth for the node types whose
3
+ * primary output is a video URL, audio URL, etc.
4
+ *
5
+ * Consumed by:
6
+ * - backend/src/services/workflow-engine/execution-graph.ts (orchestrator
7
+ * dispatch + asset-type tagging in payload-builder, output-extractor,
8
+ * inline-executor — re-exported there as VIDEO_SOURCE_TYPES /
9
+ * AUDIO_SOURCE_TYPES for backwards compatibility).
10
+ * - frontend/src/lib/generate-video-handles.ts (handle connection
11
+ * validation on the Generate Video node).
12
+ *
13
+ * Lifting these here ensures the frontend handle validator can never reject
14
+ * a connection from a node type that the backend would happily route at
15
+ * execution time (the prior drift bug, see issue 1 of the Task 4.1 review).
16
+ */
17
+
18
+ /**
19
+ * Source node types whose primary output is a video URL.
20
+ * Mirrors the backend execution-graph VIDEO_SOURCE_TYPES verbatim.
21
+ */
22
+ export const VIDEO_PRODUCER_TYPES: ReadonlySet<string> = new Set([
23
+ "image-to-video",
24
+ "video-to-video",
25
+ "switchx", // Beeble SwitchX relight/composite
26
+ "text-to-video",
27
+ // Unified video node — emits videoUrl identically to i2v/t2v (its payload-builder
28
+ // case dispatches dynamically to "image-to-video" or "text-to-video" jobName based
29
+ // on whether a start frame is wired). Without this, getPrimaryOutput would fall
30
+ // through to the imageUrl/videoUrl/audioUrl/text default and downstream consumers
31
+ // could silently misroute the output.
32
+ "generate-video",
33
+ "upload-video",
34
+ "youtube-video",
35
+ "combine-videos",
36
+ "lip-sync",
37
+ "speech-to-video",
38
+ "motion-transfer",
39
+ "video-upscale",
40
+ "extend-video",
41
+ // face-swap output is video (writes generatedVideoUrl, per-result `url`).
42
+ // Frontend execution-graph already includes it in VIDEO_SOURCE_TYPES; this
43
+ // entry brings the shared set in line so canvas typed-handle validation
44
+ // doesn't reject face-swap → video-consumer edges that the orchestrator
45
+ // would happily route at runtime.
46
+ "face-swap",
47
+ "video-retake",
48
+ // video-sfx: adds an SFX track to a video → emits a video URL. Belongs here so
49
+ // canvas handle validation accepts video-sfx → video-consumer edges and the
50
+ // backend routes its output as video (it was previously relying on a fallback).
51
+ "video-sfx",
52
+ "suno-music-video",
53
+ "merge-video-audio",
54
+ "add-captions",
55
+ "resize-video",
56
+ "social-media-format",
57
+ "trim-video",
58
+ "render-video",
59
+ "speed-ramp",
60
+ "loop-video",
61
+ "fade-video",
62
+ "transcode-video",
63
+ "manual-edit",
64
+ // Remove Audio: strips the audio track, emits a silent video.
65
+ "remove-audio",
66
+ // AI Avatar (HeyGen): avatar + voice/audio → video.
67
+ "ai-avatar",
68
+ // Cinematic Avatar (HeyGen cinematic_avatar): prompt + 1–3 avatar looks → video.
69
+ "cinematic-avatar",
70
+ // Assemble Narrated Video: fits N (clip, voice) blocks into one MP4 → video.
71
+ "assemble-narrated-video",
72
+ ])
73
+
74
+ /**
75
+ * Source node types whose output media-type cannot be classified
76
+ * statically — iterators, dynamic dispatchers, sub-workflow wrappers.
77
+ * The orchestrator decides their actual output type at execution time
78
+ * based on what's wired upstream (loop iterates upstream column type;
79
+ * sub-workflow emits its leaf node's type; adjust-volume passes through
80
+ * video or audio based on its `lastInputType` runtime field;
81
+ * voice-changer emits audio in audio mode, video+audio in video mode).
82
+ *
83
+ * Typed-handle validators (frontend/src/lib/{generate-image,generate-
84
+ * video,ffmpeg}-handles.ts) must include these as acceptors on EVERY
85
+ * media-typed input handle — otherwise the canvas validator hard-
86
+ * rejects edges that the orchestrator would happily route at runtime.
87
+ *
88
+ * Lifted here from `frontend/src/lib/ffmpeg-handles.ts` (where the
89
+ * escape hatch was first added) so the same set drives ALL handle
90
+ * validators uniformly. Adding a new dynamic-output node type means
91
+ * one edit here instead of N across sibling validator files.
92
+ *
93
+ * Intentionally OMITTED:
94
+ * - `webhook-trigger` / `schedule-trigger` — their outputs are
95
+ * user-defined JSON shapes, not media URLs. Accepting them as
96
+ * media producers creates false-positive drops at the canvas
97
+ * that fail at execution. Users wanting trigger → media flows
98
+ * should wire through an upload node.
99
+ */
100
+ export const DYNAMIC_PRODUCER_TYPES: ReadonlySet<string> = new Set([
101
+ "list",
102
+ "sub-workflow",
103
+ "adjust-volume",
104
+ // Dual-mode: audio in → audio out; video in → video out (+ revoiced audio).
105
+ // Listed here so canvas validators accept its output on BOTH audio and video
106
+ // input handles (it also stays in AUDIO_PRODUCER_TYPES as its default).
107
+ "voice-changer",
108
+ // voice-changer-pro (renamed from voice-recast in #3581) is a behavioral
109
+ // twin of voice-changer — identical dual-mode output. Must mirror it in
110
+ // EVERY producer set or its outputs can't connect (was the "cannot connect
111
+ // the outputs of voice-changer-pro" bug). Guarded by producer-types.test.ts.
112
+ "voice-changer-pro",
113
+ "reduce",
114
+ // Dual-output time chunker (UI label "Split into Chunks"; type id stays
115
+ // "split-media"): video in → video chunks, audio in → audio chunks — two
116
+ // independent lanes on two output handles. Like voice-changer, the canvas
117
+ // validator only sees the source NODE type, not which handle a wire leaves,
118
+ // so it lives here to be accepted on BOTH audio and video input handles. The
119
+ // backend routes the correct lane by sourceHandle in getPrimaryOutput
120
+ // (output-extractor.ts); the frontend does so in extractNodeOutput.
121
+ "split-media",
122
+ ])
123
+
124
+ /**
125
+ * Source node types whose primary output is an audio URL.
126
+ * Mirrors the backend execution-graph AUDIO_SOURCE_TYPES verbatim.
127
+ */
128
+ export const AUDIO_PRODUCER_TYPES: ReadonlySet<string> = new Set([
129
+ "text-to-speech",
130
+ "text-to-audio",
131
+ "generate-music",
132
+ "upload-audio",
133
+ "suno-generate",
134
+ "suno-cover",
135
+ "suno-extend",
136
+ "suno-separate",
137
+ "audio-separation",
138
+ "suno-mashup",
139
+ "suno-replace-section",
140
+ "suno-add-instrumental",
141
+ "suno-add-vocals",
142
+ "suno-convert-wav",
143
+ "suno-upload-extend",
144
+ "trim-audio",
145
+ "mix-audio",
146
+ "combine-audio",
147
+ "adjust-volume",
148
+ "audio-fx",
149
+ "reference-audio",
150
+ "audio-isolation",
151
+ "text-to-dialogue",
152
+ "voice-changer",
153
+ // Twin of voice-changer (see DYNAMIC_PRODUCER_TYPES note). Audio is its
154
+ // default output mode; video mode is handled via DYNAMIC membership.
155
+ "voice-changer-pro",
156
+ "dubbing",
157
+ "voice-remix",
158
+ "voice-design",
159
+ // Extract Audio: demuxes a video's audio track to a standalone MP3.
160
+ "extract-audio",
161
+ ])
162
+
163
+ /**
164
+ * Source node types whose primary output is a LIST that, by default, fans out
165
+ * one downstream execution per element when an edge leaves them WITHOUT an
166
+ * explicit `outputMode` (all other edges default to "last"). `selector` is
167
+ * included because its picked/rest channels are lists too.
168
+ *
169
+ * Single source of truth for the fan-out "each" default, consumed by:
170
+ * - backend/src/services/workflow-engine/input-resolver.ts (DEFAULT_EACH_TYPES)
171
+ * - frontend/src/components/editor/workflow-editor/types.ts (FAN_OUT_EACH_TYPES)
172
+ * - frontend/src/components/editor/workflow-editor/node-input-resolver.ts
173
+ *
174
+ * These three previously kept independent copies; the backend copy drifted
175
+ * (missing the four list-transform types), so server-side workflow runs
176
+ * silently dropped all-but-the-first item for filter-list / deduplicate /
177
+ * merge-lists / sort-list sources while the canvas fanned out N ways. Lifting
178
+ * the set here makes that drift impossible.
179
+ */
180
+ export const FAN_OUT_EACH_TYPES: ReadonlySet<string> = new Set([
181
+ "list",
182
+ "split-text",
183
+ "filter-list",
184
+ "deduplicate",
185
+ "merge-lists",
186
+ "sort-list",
187
+ "selector",
188
+ ])