@nodaro/shared 1.4.2

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,147 @@
1
+ import { EXECUTION_DATA_KEYS } from "./node-runtime-keys.js"
2
+ import { COMPOSER_PLAN_FIELDS } from "./model-constants.js"
3
+
4
+ /**
5
+ * Generated-plan state on composer nodes that a preset must NEITHER capture NOR
6
+ * carry, and that the dropdown must CLEAR when applying a preset:
7
+ *
8
+ * - every composer node's plan field (`COMPOSER_PLAN_MAP[*].planField` —
9
+ * sceneGraph / effectPlan / overlayPlan / titlePlan / motionPlan /
10
+ * compositePlan): a large LLM-authored plan blob, regenerated per run.
11
+ * - `lottieUrl`: the R2 url of the authored Lottie JSON (motion-graphics'
12
+ * `lottie` handle output) — a reapable generated artifact, not config.
13
+ *
14
+ * Derived from the single source of truth (COMPOSER_PLAN_MAP) so a new composer
15
+ * node type is covered by construction — no per-type list to keep in sync.
16
+ *
17
+ * Two distinct hazards this closes (see motion-graphics-presets design §2a/§2b):
18
+ * 1. Apply-time staleness: applying a preset over a node that already generated
19
+ * a plan would leave the OLD animation showing under the NEW prompt (the
20
+ * plan field is named by no patch, so it persists in the preview and on the
21
+ * output handle). The dropdown spreads `{ ...clearPatch, ...preset.data }`
22
+ * where clearPatch maps each key here to `undefined`.
23
+ * 2. Capture pollution: a user-saved preset would otherwise capture the plan
24
+ * blob (~tens of KB) + a stale url, re-injecting them on every apply and
25
+ * bloating the preset row. These keys are in the capture-exclusion set below.
26
+ */
27
+ export const PRESET_APPLY_CLEAR_KEYS: readonly string[] = [...COMPOSER_PLAN_FIELDS, "lottieUrl"]
28
+
29
+ /**
30
+ * Config fields that are intentionally NOT part of a preset, because they are not portable
31
+ * configuration — they identify the node on the canvas, wire it into one specific graph, or point
32
+ * at specific DB entities. Capturing them would make a preset silently wrong when applied to a
33
+ * different node or workflow. This is the single place that defines that contract, so the system
34
+ * stays correct-by-construction as new node types are added.
35
+ *
36
+ * Identity:
37
+ * - `label`: the node's canvas identity. Presets carry their own name and must not rename the node.
38
+ *
39
+ * Graph wiring (reference specific upstream node / handle / tile ids — meaningless elsewhere):
40
+ * - `fieldMappings`: field -> upstream node id.
41
+ * - `referenceImageOrder` / `referenceOrder` / `connectedMediaOrder` / `connectedRefImageOrder`:
42
+ * orderings keyed by the ids of wired upstream references.
43
+ *
44
+ * Structural identifiers (handle / port / route / channel ids that must stay unique per node and
45
+ * that downstream edges target — copying them across nodes orphans edges or duplicates a channel):
46
+ * - `routes` / `routeId` / `routeIds`: router routes + sub-workflow/condition route pairing ids.
47
+ * - `ports` / `inputPorts` / `outputPorts`: sub-workflow port arrays (each port has a handle id).
48
+ * - `channel` / `channelColor`: teleport send/receive pairing channel (auto-assigned per node).
49
+ *
50
+ * DB entity references / per-wiring identity state (tied to specific characters/locations):
51
+ * - `characterDefinitionIds`, `suppressedCanonicalCharacterIds`, `suppressedCanonicalLocationIds`.
52
+ * - `identityMeta`: per-identity overrides keyed by the wired identity's index/label.
53
+ * - `extraRefs`: extra references carrying `characterSlug` + workflow-specific asset urls.
54
+ *
55
+ * Preset meta:
56
+ * - `__activePresetId`: which preset is currently loaded onto the node (for the dropdown's
57
+ * active-name + dirty `*`). Node-local UI state, never part of a preset's own config.
58
+ * - `__promptFinalView`: field keys currently showing the final-prompt view (Edit ⇄ Final
59
+ * per-field toggle). Node-local UI state, never part of a preset's own config.
60
+ *
61
+ * NOTE: manual reference image urls (`referenceImageUrl` / `referenceImageUrls`) are deliberately
62
+ * KEPT — they are self-contained R2 urls a user set as input, and portable across nodes.
63
+ *
64
+ * Generated composer-plan state (`PRESET_APPLY_CLEAR_KEYS` — every COMPOSER_PLAN_MAP plan field +
65
+ * `lottieUrl`): NOT portable config — a per-run LLM-authored plan blob / reapable artifact url.
66
+ * Excluded here (preset-scoped, zero blast radius) rather than via EXECUTION_DATA_KEYS, whose
67
+ * non-preset consumers (undo-skip in use-workflow-store, auto-execute IGNORE_KEYS) would then
68
+ * treat a legitimately-persisted plan as transient runtime state.
69
+ */
70
+ export const PRESET_EXCLUDED_KEYS: ReadonlySet<string> = new Set([
71
+ "label",
72
+ "fieldMappings",
73
+ "__activePresetId",
74
+ "__promptFinalView",
75
+ "referenceImageOrder",
76
+ "referenceOrder",
77
+ "connectedMediaOrder",
78
+ "connectedRefImageOrder",
79
+ "characterDefinitionIds",
80
+ "suppressedCanonicalCharacterIds",
81
+ "suppressedCanonicalLocationIds",
82
+ "identityMeta",
83
+ "extraRefs",
84
+ // structural identifiers
85
+ "routes",
86
+ "routeId",
87
+ "routeIds",
88
+ "ports",
89
+ "inputPorts",
90
+ "outputPorts",
91
+ "channel",
92
+ "channelColor",
93
+ // generated composer-plan state (derived from COMPOSER_PLAN_MAP + lottieUrl)
94
+ ...PRESET_APPLY_CLEAR_KEYS,
95
+ ])
96
+
97
+ /**
98
+ * Capture a node's reusable configuration from its `data`: a shallow copy minus runtime/result
99
+ * state (EXECUTION_DATA_KEYS) and graph/identity fields (PRESET_EXCLUDED_KEYS). Pure; never mutates
100
+ * the input. Node-type-agnostic — the node type is stored alongside the result by the caller.
101
+ */
102
+ export function extractPresetData(
103
+ data: Readonly<Record<string, unknown>>,
104
+ ): Record<string, unknown> {
105
+ const out: Record<string, unknown> = {}
106
+ for (const key of Object.keys(data)) {
107
+ if (EXECUTION_DATA_KEYS.has(key)) continue
108
+ if (PRESET_EXCLUDED_KEYS.has(key)) continue
109
+ out[key] = data[key]
110
+ }
111
+ return out
112
+ }
113
+
114
+ function deepEqual(a: unknown, b: unknown): boolean {
115
+ if (a === b) return true
116
+ if (typeof a !== typeof b) return false
117
+ if (a === null || b === null) return a === b
118
+ if (Array.isArray(a) || Array.isArray(b)) {
119
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false
120
+ return a.every((v, i) => deepEqual(v, b[i]))
121
+ }
122
+ if (typeof a === "object" && typeof b === "object") {
123
+ const ka = Object.keys(a as object)
124
+ const kb = Object.keys(b as object)
125
+ if (ka.length !== kb.length) return false
126
+ return ka.every((k) =>
127
+ deepEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),
128
+ )
129
+ }
130
+ return false
131
+ }
132
+
133
+ /**
134
+ * True iff the node's `data` still matches the preset — i.e. every key the preset defines
135
+ * deep-equals the node's current value. Compares only preset-defined keys (the node may carry
136
+ * extra keys the preset doesn't set, e.g. seed, which must NOT count as a mismatch). Used to drive
137
+ * the dropdown's dirty `*` indicator.
138
+ */
139
+ export function presetDataMatches(
140
+ nodeData: Readonly<Record<string, unknown>>,
141
+ presetData: Readonly<Record<string, unknown>>,
142
+ ): boolean {
143
+ for (const key of Object.keys(presetData)) {
144
+ if (!deepEqual(nodeData[key], presetData[key])) return false
145
+ }
146
+ return true
147
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Node reference pattern and resolution for {Node Label} template syntax.
3
+ * Shared between frontend and backend.
4
+ */
5
+
6
+ /** Matches {Node Label} references in text. */
7
+ export const NODE_REF_PATTERN = /\{([^}]+)\}/g
8
+
9
+ /** Reserved template variable names that should NOT be resolved as node refs */
10
+ export const RESERVED_TEMPLATE_VARS = new Set([
11
+ "name",
12
+ "description",
13
+ "userPrompt",
14
+ "assetDescriptions",
15
+ "outputCount",
16
+ ])
17
+
18
+ /**
19
+ * Split a `{...}` token body into its node name and optional fallback. Splits on the FIRST `||`
20
+ * (a fallback may itself contain `||`); both sides are trimmed. `fallback` is null when there is
21
+ * no `||`, preserving the legacy "leave the literal token" behavior.
22
+ * "person || man" -> { name: "person", fallback: "man" }
23
+ * "person || " -> { name: "person", fallback: "" } (resolves to empty when absent)
24
+ * "person" -> { name: "person", fallback: null } (literal {person} when absent)
25
+ */
26
+ export function parseNodeRef(raw: string): { name: string; fallback: string | null } {
27
+ const i = raw.indexOf("||")
28
+ if (i === -1) return { name: raw.trim(), fallback: null }
29
+ return { name: raw.slice(0, i).trim(), fallback: raw.slice(i + 2).trim() }
30
+ }
31
+
32
+ /**
33
+ * Canonical form of a node-name variable: trimmed + lowercased. Node-name
34
+ * variables are CASE-INSENSITIVE with lowercase as canonical, so nodes labeled
35
+ * `TEXt`, `TEXT`, `text` all feed the one `{text}` variable and `{TEXT}` /
36
+ * `{Text}` / `{text}` in a prompt all resolve to it.
37
+ *
38
+ * Reserved template vars (RESERVED_TEMPLATE_VARS, e.g. `userPrompt`) are matched
39
+ * case-sensitively on the ORIGINAL token BEFORE canonicalization, so `{userPrompt}`
40
+ * stays a reserved token rather than becoming a `userprompt` node ref.
41
+ */
42
+ export function canonicalVarName(label: string): string {
43
+ return label.trim().toLowerCase()
44
+ }
45
+
46
+ /**
47
+ * The distinct `{label}` names referenced across the given texts. Mirrors the
48
+ * canvas-side `referencedRefs`: strips the `|| fallback`, and ignores empty
49
+ * tokens, image-ref tokens (`{image:N}`), and RESERVED_TEMPLATE_VARS. Used to
50
+ * SUPPRESS auto-injection of a connected node the author already placed
51
+ * explicitly via `{label}` (so it isn't injected twice). `matchAll` over the
52
+ * global pattern does not mutate its lastIndex, so sharing the constant is safe.
53
+ */
54
+ export function extractReferencedLabels(
55
+ ...texts: ReadonlyArray<string | undefined | null>
56
+ ): Set<string> {
57
+ const refs = new Set<string>()
58
+ for (const text of texts) {
59
+ if (typeof text !== "string" || text.length === 0) continue
60
+ for (const m of text.matchAll(NODE_REF_PATTERN)) {
61
+ const { name } = parseNodeRef(m[1] ?? "")
62
+ if (name === "" || name.startsWith("image:") || RESERVED_TEMPLATE_VARS.has(name)) continue
63
+ refs.add(canonicalVarName(name)) // case-insensitive: {Foo} suppresses a `foo` node
64
+ }
65
+ }
66
+ return refs
67
+ }
68
+
69
+ /** Handle → combine-order category for same-label `{label}` merging. Lower wins
70
+ * first: prompt (0) → elements (1) → look family (2). Other handles are -1
71
+ * (not combinable — they keep the per-engine last-wins/suffix behavior). */
72
+ export const REF_HANDLE_CATEGORY: Readonly<Record<string, number>> = {
73
+ prompt: 0,
74
+ elements: 1,
75
+ look: 2,
76
+ cinematography: 2,
77
+ style: 2,
78
+ }
79
+
80
+ /** Combine-order category for a direct-parent edge's `targetHandle`; -1 when the
81
+ * handle isn't one of the combinable prompt/elements/look handles. */
82
+ export function refHandleCategory(targetHandle: string | null | undefined): number {
83
+ if (targetHandle != null && Object.prototype.hasOwnProperty.call(REF_HANDLE_CATEGORY, targetHandle)) {
84
+ return REF_HANDLE_CATEGORY[targetHandle]
85
+ }
86
+ return -1
87
+ }
88
+
89
+ /**
90
+ * Reference-handle id → resolved-input key. The SINGLE SOURCE OF TRUTH for which
91
+ * `targetHandle` ids carry image / video / audio reference inputs. Shared by the
92
+ * backend input-resolver (routes the wired URL into the matching
93
+ * `referenceImageUrls` / `referenceVideoUrls` / `referenceAudioUrls` slot) AND
94
+ * the positional `{image:N}` / `{video:N}` / `{audio:N}` body-token COUNTS in the
95
+ * FE preview (`video-prompt-assembly.ts`), FE run (`execute-node.ts`), and BE
96
+ * payload-builder.
97
+ *
98
+ * Two handle families map to the same keys:
99
+ * - Legacy / i2v single-name ids (`references` / `reference-videos` /
100
+ * `reference-audio`) — kept for un-migrated workflows.
101
+ * - Canonical typed-handle ids the modern Generate Video node exposes
102
+ * (`imageReferences` / `videoReferences` / `audioReferences`) — share the
103
+ * resolved-input keys with the legacy ids so consumer code never forks.
104
+ */
105
+ export const REFERENCE_HANDLE_MAP: Record<string, "referenceImageUrls" | "referenceVideoUrls" | "referenceAudioUrls"> = {
106
+ // Legacy / i2v single-name handle ids (kept for un-migrated workflows)
107
+ "references": "referenceImageUrls",
108
+ "reference-videos": "referenceVideoUrls",
109
+ "reference-audio": "referenceAudioUrls",
110
+ // New canonical typed-handle ids (Generate Video) — share the resolved-input
111
+ // keys with the legacy ids so payload-builder code doesn't fork.
112
+ "imageReferences": "referenceImageUrls",
113
+ "videoReferences": "referenceVideoUrls",
114
+ "audioReferences": "referenceAudioUrls",
115
+ }
116
+
117
+ /** The three reference modalities a reference handle can carry. */
118
+ export type ReferenceModality = "image" | "video" | "audio"
119
+
120
+ /**
121
+ * The reference MODALITY a `targetHandle` carries, or null when the handle is
122
+ * not a reference handle. Resolves BOTH the legacy single-name ids and the
123
+ * canonical Generate Video ids (via REFERENCE_HANDLE_MAP), so positional
124
+ * `{image:N}` / `{video:N}` / `{audio:N}` token counts cover every handle alias
125
+ * — without this, the generate-video `imageReferences` handle was silently
126
+ * uncounted and `{image:N}` tokens dropped to bare labels on the only creatable
127
+ * video node.
128
+ */
129
+ export function referenceModalityForHandle(targetHandle: string | null | undefined): ReferenceModality | null {
130
+ if (targetHandle == null) return null
131
+ const key = REFERENCE_HANDLE_MAP[targetHandle]
132
+ return key === "referenceImageUrls" ? "image" : key === "referenceVideoUrls" ? "video" : key === "referenceAudioUrls" ? "audio" : null
133
+ }
134
+
135
+ /**
136
+ * Frame target handles (start / end keyframes). An image wired into one of these
137
+ * is a KEYFRAME, not an `{image:N}` reference: the backend appends frames at the
138
+ * TAIL of `reference_image_urls` (`resolveSeedance2Inputs`), never numbered. The
139
+ * editor MUST therefore exclude them from `{image:N}` numbering so editor token N
140
+ * equals the worker reference slot N.
141
+ *
142
+ * The SINGLE source of truth for "is this a frame handle," shared by the
143
+ * config-panel autocomplete (`video-configs.tsx`) AND the inline/modal builder
144
+ * (`connected-references.ts`) — without ONE definition those two surfaces drifted
145
+ * and the inline editor numbered the frame as `{image:1}` (the C1 bug). Frames
146
+ * are deliberately NOT in `REFERENCE_HANDLE_MAP`: they carry no reference
147
+ * modality, so `referenceModalityForHandle` already returns `null` for them on
148
+ * the count side — this set is the exclusion lever for the EDITOR-numbering side.
149
+ */
150
+ export const FRAME_TARGET_HANDLES: ReadonlySet<string> = new Set(["startFrame", "endFrame"])
151
+
152
+ /**
153
+ * Minimal structural edge shape {@link countRefModalityEdges} reads — only the
154
+ * two fields the count needs. Both the FE `WorkflowEdge` (xyflow `Edge`, whose
155
+ * `target` is a non-null string) and the BE `SimpleEdge` satisfy it.
156
+ */
157
+ export interface RefModalityEdge {
158
+ target?: string | null
159
+ targetHandle?: string | null
160
+ }
161
+
162
+ /**
163
+ * Count the reference-handle edges of a given reference MODALITY wired into a
164
+ * node — the ONE positional `{image:N}` / `{video:N}` / `{audio:N}` counter
165
+ * shared by the FE preview (`video-prompt-assembly.ts`), the FE run
166
+ * (`execute-node.ts`), and the BE orchestrator (`payload-builder.ts`). Counting
167
+ * via `referenceModalityForHandle` (not a single handle string) covers BOTH the
168
+ * legacy single-name ids (`references` / `reference-videos` / `reference-audio`)
169
+ * AND the canonical Generate Video ids (`imageReferences` / `videoReferences` /
170
+ * `audioReferences`), so editor token N maps 1:1 to the worker
171
+ * `referenceImageUrls` / `referenceVideoUrls` / `referenceAudioUrls` slot N on
172
+ * every handle alias. This is the EDGE count (not the resolved/list-expanded URL
173
+ * count) so all three paths stay byte-identical; the edge-vs-resolved divergence
174
+ * for a reference edge that fans out to >1 URL (a List producer) is a shared,
175
+ * documented v1 limitation.
176
+ */
177
+ export function countRefModalityEdges(
178
+ edges: ReadonlyArray<RefModalityEdge>,
179
+ nodeId: string,
180
+ modality: ReferenceModality,
181
+ ): number {
182
+ let n = 0
183
+ for (const e of edges) {
184
+ if (e.target === nodeId && referenceModalityForHandle(e.targetHandle) === modality) n += 1
185
+ }
186
+ return n
187
+ }
188
+
189
+ /** A candidate contribution to the `{label}` map: the node's label, its resolved
190
+ * output, and its direct-parent handle category (-1 = not combinable / deeper). */
191
+ export interface RefCandidate {
192
+ label: string
193
+ output: string
194
+ category: number
195
+ }
196
+
197
+ /**
198
+ * Same-label COMBINE: when ≥2 direct-parent candidates (category ≥ 0) share a
199
+ * label, `{label}` resolves to their values joined by ", " ordered prompt →
200
+ * elements → look, and within a category by candidate (edge) order. Returns a
201
+ * Map of ONLY the combined labels; the caller fills the rest with its own
202
+ * (FE suffix / BE last-wins) logic, skipping any label present here. Shared so
203
+ * the frontend executor and backend orchestrator never drift.
204
+ */
205
+ export function combineSameLabelRefs(candidates: ReadonlyArray<RefCandidate>): Map<string, string> {
206
+ const byLabel = new Map<string, RefCandidate[]>()
207
+ for (const c of candidates) {
208
+ if (c.category < 0) continue
209
+ const key = canonicalVarName(c.label) // TEXt + TEXT + text collapse into one {text}
210
+ const arr = byLabel.get(key)
211
+ if (arr) arr.push(c)
212
+ else byLabel.set(key, [c])
213
+ }
214
+ const combined = new Map<string, string>()
215
+ for (const [label, group] of byLabel) {
216
+ if (group.length < 2) continue
217
+ const ordered = group
218
+ .map((c, i) => ({ c, i }))
219
+ .sort((a, b) => a.c.category - b.c.category || a.i - b.i)
220
+ .map((x) => x.c.output)
221
+ combined.set(label, ordered.join(", "))
222
+ }
223
+ return combined
224
+ }
225
+
226
+ /**
227
+ * Resolve {Node Label} references in text by replacing them with actual node outputs.
228
+ * Skips reserved template variables used by applyTemplate().
229
+ * Iterates until stable to handle nested refs (e.g. {List} → {Animal1} → "dog").
230
+ */
231
+ export function resolveNodeRefs(
232
+ text: string,
233
+ labelToOutput: ReadonlyMap<string, string>,
234
+ ): string {
235
+ const MAX_PASSES = 10
236
+ let result = text
237
+ for (let i = 0; i < MAX_PASSES; i++) {
238
+ const next = result.replace(NODE_REF_PATTERN, (match, raw: string) => {
239
+ const { name, fallback } = parseNodeRef(raw)
240
+ if (RESERVED_TEMPLATE_VARS.has(name)) return match
241
+ // Node-name vars are case-insensitive: try the literal key first (keeps
242
+ // original-cased maps like condition-variables working), then the
243
+ // lowercase-canonical key (node-ref maps are canonical-keyed).
244
+ const output = labelToOutput.get(name) ?? labelToOutput.get(canonicalVarName(name))
245
+ if (output !== undefined) return output // connected, non-empty output → its value
246
+ if (fallback !== null) return fallback // absent/empty + fallback → default ("" for {name || })
247
+ return match // absent + no || → literal {name}
248
+ })
249
+ if (next === result) break
250
+ result = next
251
+ }
252
+ return result
253
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Field names on a node's `data` that hold runtime / result / transient execution
3
+ * state (job ids, progress, generated outputs, fan-out bookkeeping). These are NEVER
4
+ * part of a node's reusable configuration.
5
+ *
6
+ * Single source of truth, consumed by:
7
+ * - the workflow store's undo logic (execution-only updates skip undo capture, so job
8
+ * polling does not pollute undo history or flip `isDirty`)
9
+ * - the node-preset extractor (presets must never capture runtime state)
10
+ * - the backend defensive strip on preset writes
11
+ *
12
+ * When adding a new runtime/result field to a node's data, add its key here.
13
+ */
14
+ export const EXECUTION_DATA_KEYS: ReadonlySet<string> = new Set([
15
+ "executionStatus",
16
+ "currentJobId",
17
+ "currentJobProgress",
18
+ "errorMessage",
19
+ "isStreaming",
20
+ "generatedImageUrl",
21
+ "generatedVideoUrl",
22
+ "generatedAudioUrl",
23
+ "generatedText",
24
+ "generatedScript",
25
+ "generatedItems",
26
+ "generatedResults",
27
+ "activeResultIndex",
28
+ "sourceImageUrl",
29
+ "__listTotal",
30
+ "__listCompleted",
31
+ "__listResults",
32
+ // List fan-out window flag (abandon-guard exemption). Set/cleared by
33
+ // executeNodeForList — purely execution-related, never user-edited.
34
+ "__listRunning",
35
+ // Selector node dual-channel outputs (picked + rest). Server-side execution
36
+ // output, not user-edited config.
37
+ "pickedResults",
38
+ "restResults",
39
+ "__pickedResults",
40
+ "__restResults",
41
+ "__pickedTotal",
42
+ "__restTotal",
43
+ "generatedJson",
44
+ "subWorkflowProgress",
45
+ "outputResults",
46
+ "shots",
47
+ "result",
48
+ "processedResult",
49
+ "activeRoutes",
50
+ "routeOutputs",
51
+ "_upstreamRefresh",
52
+ "zoom",
53
+ // Character LoRA training status fields — written every 8s while training.
54
+ "loraReplicateVersion",
55
+ "loraTriggerWord",
56
+ "loraTrainingStatus",
57
+ // Collect (fan-in) execution snapshot.
58
+ "lastInputs",
59
+ "lastMeta",
60
+ "__upstreamCount",
61
+ ])
62
+
63
+ /**
64
+ * The PURE RUN-STATE subset of EXECUTION_DATA_KEYS: per-tick values (status
65
+ * flips, job ids, progress counters, fan-out bookkeeping) that must NEITHER
66
+ * mark the workflow dirty NOR be persisted in the save payload. Everything
67
+ * else in EXECUTION_DATA_KEYS is a RESULT the user expects to survive reload
68
+ * (generated URLs/results, errorMessage, LoRA outputs, collect snapshots).
69
+ *
70
+ * Why this split exists: writing these keys used to set `isDirty`, so job
71
+ * polling phantom-dirtied passive tabs → spurious autosaves → false
72
+ * "changed in another tab" banners → the remote-ahead latch froze autosave
73
+ * (the "not saved for a long time" report). Consumed by the workflow
74
+ * store's dirty decision and the save-payload sanitizer below.
75
+ *
76
+ * Invariant (guarded by node-runtime-keys.test.ts): subset of
77
+ * EXECUTION_DATA_KEYS — anything transient is also undo-exempt.
78
+ */
79
+ export const TRANSIENT_RUNTIME_KEYS: ReadonlySet<string> = new Set([
80
+ "executionStatus",
81
+ "currentJobId",
82
+ "currentJobProgress",
83
+ "isStreaming",
84
+ "subWorkflowProgress",
85
+ "__listTotal",
86
+ "__listCompleted",
87
+ "__listRunning",
88
+ "_upstreamRefresh",
89
+ "__upstreamCount",
90
+ ])
91
+
92
+ /**
93
+ * Pure save-payload sanitizer: returns new node objects with the transient
94
+ * run-state keys removed from `data`. Nodes without transient keys (or
95
+ * without `data`) are returned by reference — cheap for the common case.
96
+ */
97
+ export function stripTransientRuntimeData<
98
+ T extends { data?: Record<string, unknown> | undefined },
99
+ >(nodes: readonly T[]): T[] {
100
+ return nodes.map((node) => {
101
+ const data = node.data
102
+ if (!data) return node
103
+ let hasTransient = false
104
+ for (const key of TRANSIENT_RUNTIME_KEYS) {
105
+ if (key in data) {
106
+ hasTransient = true
107
+ break
108
+ }
109
+ }
110
+ if (!hasTransient) return node
111
+ const cleaned: Record<string, unknown> = {}
112
+ for (const [key, value] of Object.entries(data)) {
113
+ if (!TRANSIENT_RUNTIME_KEYS.has(key)) cleaned[key] = value
114
+ }
115
+ return { ...node, data: cleaned }
116
+ })
117
+ }
@@ -0,0 +1,72 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // Object aspect-ratio defaults
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ //
5
+ // Objects are isolated subjects — product-showcase framing. Unlike characters
6
+ // (which have per-asset-type framing differences — head shots 3:4, full-body
7
+ // 9:16), objects use 1:1 square framing for every asset type by default. This
8
+ // matches the natural framing of the Object Studio's asset-card grid (square
9
+ // tiles) and avoids cropping the subject regardless of which variant axis
10
+ // (angles/materials/variations/motion) is being generated.
11
+ //
12
+ // Resolution precedence (low → high):
13
+ // 1. Per-asset-type default (this table — all 1:1 currently)
14
+ // 2. Object node's `defaultAssetAspectRatio` (user override, Phase E)
15
+ // 3. Explicit `aspectRatio` on the API request (caller wins)
16
+ // ─────────────────────────────────────────────────────────────────────────────
17
+
18
+ export const OBJECT_ASPECT_OPTIONS = ["1:1", "3:4", "16:9", "9:16", "4:3"] as const
19
+ export type ObjectAspectRatio = (typeof OBJECT_ASPECT_OPTIONS)[number]
20
+
21
+ /**
22
+ * The asset categories with distinct natural framing. For objects, all bucket
23
+ * defaults are currently `"1:1"`, but the per-asset-type table is kept (vs a
24
+ * single flat constant) so future tuning (e.g., wider framing for motion clips
25
+ * with camera-orbit moves) can be added without touching the resolver.
26
+ */
27
+ export type ObjectAssetTypeForAspect = "angles" | "materials" | "variations" | "motion" | "custom"
28
+
29
+ export const OBJECT_ASPECT_DEFAULTS: Record<ObjectAssetTypeForAspect, ObjectAspectRatio> = {
30
+ angles: "1:1",
31
+ materials: "1:1",
32
+ variations: "1:1",
33
+ // Motion clips are 1:1 because objects rotate in place. Switch to "16:9" if
34
+ // future motion variants pan through a scene (e.g., drone-orbit with horizon).
35
+ motion: "1:1",
36
+ custom: "1:1",
37
+ }
38
+
39
+ export function isObjectAspectRatio(s: string): s is ObjectAspectRatio {
40
+ return (OBJECT_ASPECT_OPTIONS as readonly string[]).includes(s)
41
+ }
42
+
43
+ export interface ResolveObjectAspectOptions {
44
+ /** Explicit `aspectRatio` on the request body — highest precedence. */
45
+ readonly explicit?: string | null
46
+ /**
47
+ * The object node's `defaultAssetAspectRatio` — set per-canvas-node via the
48
+ * 5-pill toggle on the object node (Phase E). Flows through the API as
49
+ * `objectNodeAspectRatio`.
50
+ */
51
+ readonly nodeOverride?: string | null
52
+ /** What's being generated. Drives the default when nothing else is set. */
53
+ readonly assetType: ObjectAssetTypeForAspect
54
+ }
55
+
56
+ /**
57
+ * Resolve the final aspect ratio for an object generation request.
58
+ *
59
+ * Precedence: explicit > node override > per-asset-type default. The two
60
+ * "soft" inputs are tolerated as raw strings and only honored when they parse
61
+ * to a value in OBJECT_ASPECT_OPTIONS — anything else falls through to the
62
+ * next layer (so a stale enum value doesn't poison the generation).
63
+ */
64
+ export function resolveObjectAspectRatio(opts: ResolveObjectAspectOptions): ObjectAspectRatio {
65
+ if (opts.explicit && isObjectAspectRatio(opts.explicit)) {
66
+ return opts.explicit
67
+ }
68
+ if (opts.nodeOverride && isObjectAspectRatio(opts.nodeOverride)) {
69
+ return opts.nodeOverride
70
+ }
71
+ return OBJECT_ASPECT_DEFAULTS[opts.assetType]
72
+ }
@@ -0,0 +1,22 @@
1
+ import { PARAMETER_NODE_TYPES } from "./parameter-node-value.js"
2
+
3
+ /**
4
+ * The 5 parameter-picker node types that can wire INTO the Object Studio's
5
+ * `type` input handle as upstream identity providers (Material, Animal,
6
+ * Vehicle, Weapon, Furniture). The resolver in
7
+ * `backend/src/services/workflow-engine/input-resolver.ts` (and its frontend
8
+ * mirror) reads this set to discriminate wired pickers from arbitrary source
9
+ * nodes when composing the seedPromptHint.
10
+ *
11
+ * Defensive filter: if `PARAMETER_NODE_TYPES` doesn't yet contain `"furniture"`
12
+ * (i.e., the Furniture prereq PR hasn't been deployed in this environment),
13
+ * furniture silently drops from the set. The wired-picker resolver then
14
+ * skips furniture pickers without error rather than firing a wrong prompt
15
+ * fragment. Production deploy order is enforced as: Prereq PR (#2601, merged)
16
+ * → Object Studio PRs; this defensive code is a safety net.
17
+ */
18
+ export const OBJECT_PICKER_NODE_TYPES: ReadonlySet<string> = new Set(
19
+ ["material", "animal", "vehicle", "weapon", "furniture"].filter((t) =>
20
+ PARAMETER_NODE_TYPES.has(t),
21
+ ),
22
+ )