@nodetool-ai/websocket 0.8.4-a24.3 → 0.8.4

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 (251) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/example-workflows.d.ts +2 -0
  3. package/dist/example-workflows.d.ts.map +1 -1
  4. package/dist/example-workflows.js +16 -4
  5. package/dist/example-workflows.js.map +1 -1
  6. package/dist/examples/nodetool-base/A Campaign Concept from a Brief.json +135 -0
  7. package/dist/examples/nodetool-base/A Gradient Card as PNG.json +128 -0
  8. package/dist/examples/nodetool-base/A Poster in Portrait.json +129 -0
  9. package/dist/examples/nodetool-base/A Repeating Texture Tile.json +129 -0
  10. package/dist/examples/nodetool-base/A Run Button for Any Job.json +152 -0
  11. package/dist/examples/nodetool-base/A Webhook That Answers.json +170 -0
  12. package/dist/examples/nodetool-base/AI Spokesperson.json +328 -0
  13. package/dist/examples/nodetool-base/Action Items from a Meeting Recording.json +198 -0
  14. package/dist/examples/nodetool-base/Ad Copy in Three Registers.json +135 -0
  15. package/dist/examples/nodetool-base/Ad Loop from a Product Photo.json +277 -0
  16. package/dist/examples/nodetool-base/Add Reverb to a Voice.json +125 -0
  17. package/dist/examples/nodetool-base/Album Art from a Mood.json +129 -0
  18. package/dist/examples/nodetool-base/Assemble a UGC Product Video.json +158 -0
  19. package/dist/examples/nodetool-base/Audio To Image.json +184 -0
  20. package/dist/examples/nodetool-base/B-Roll Reel from a Brief.json +339 -0
  21. package/dist/examples/nodetool-base/Batch a List.json +171 -0
  22. package/dist/examples/nodetool-base/Black and White, Hard.json +147 -0
  23. package/dist/examples/nodetool-base/Blog Post to Social Thread.json +135 -0
  24. package/dist/examples/nodetool-base/Blur a Channel.json +205 -0
  25. package/dist/examples/nodetool-base/Blur an Image.json +179 -0
  26. package/dist/examples/nodetool-base/Boost Saturation.json +178 -0
  27. package/dist/examples/nodetool-base/Brand Asset Generator.json +585 -0
  28. package/dist/examples/nodetool-base/Brand a UGC Product Video.json +402 -0
  29. package/dist/examples/nodetool-base/Bring a Still to Life.json +250 -0
  30. package/dist/examples/nodetool-base/Bullet Points to Press Release.json +135 -0
  31. package/dist/examples/nodetool-base/Chapter Markers for a Long Recording.json +198 -0
  32. package/dist/examples/nodetool-base/Chat With Your Documents.json +569 -0
  33. package/dist/examples/nodetool-base/Chunk a Transcript for Indexing.json +202 -0
  34. package/dist/examples/nodetool-base/Circles and Lines.json +126 -0
  35. package/dist/examples/nodetool-base/Clean Up a Rough Voice Recording.json +208 -0
  36. package/dist/examples/nodetool-base/Clip on Kie.json +173 -0
  37. package/dist/examples/nodetool-base/Cold Outreach Co-Pilot.json +423 -0
  38. package/dist/examples/nodetool-base/Color Boost Video.json +250 -0
  39. package/dist/examples/nodetool-base/Compose Directed Campaign Formats.json +764 -0
  40. package/dist/examples/nodetool-base/Concept Art Iteration Board.json +439 -0
  41. package/dist/examples/nodetool-base/Concept Art, Then a Moving Version.json +173 -0
  42. package/dist/examples/nodetool-base/Conditional Logic Engine.json +535 -0
  43. package/dist/examples/nodetool-base/Count a List.json +168 -0
  44. package/dist/examples/nodetool-base/Count to Three with a Loop.json +157 -0
  45. package/dist/examples/nodetool-base/Crop an Image.json +181 -0
  46. package/dist/examples/nodetool-base/Crop then Fit.json +208 -0
  47. package/dist/examples/nodetool-base/Crush It to 8 Bits.json +123 -0
  48. package/dist/examples/nodetool-base/Cut Out the Subject.json +184 -0
  49. package/dist/examples/nodetool-base/Cut a Landscape Clip for Vertical.json +123 -0
  50. package/dist/examples/nodetool-base/Cut a Product Out of Its Background.json +238 -0
  51. package/dist/examples/nodetool-base/Data Generator.json +216 -0
  52. package/dist/examples/nodetool-base/Denoise Footage.json +178 -0
  53. package/dist/examples/nodetool-base/Direct a Short Film.json +305 -0
  54. package/dist/examples/nodetool-base/Directed Film to Timeline.json +385 -0
  55. package/dist/examples/nodetool-base/Does This Text Mention the Deadline.json +130 -0
  56. package/dist/examples/nodetool-base/Draft Answers for an FAQ.json +135 -0
  57. package/dist/examples/nodetool-base/Draw a Badge in SVG.json +126 -0
  58. package/dist/examples/nodetool-base/Drop Duplicates.json +170 -0
  59. package/dist/examples/nodetool-base/Drop the First Few.json +148 -0
  60. package/dist/examples/nodetool-base/Dungeon and Ruins Starter Pack.json +786 -0
  61. package/dist/examples/nodetool-base/Edit a Still with Words.json +284 -0
  62. package/dist/examples/nodetool-base/Editorial Still from a Line.json +197 -0
  63. package/dist/examples/nodetool-base/Emboss a Still.json +120 -0
  64. package/dist/examples/nodetool-base/Every Combination.json +225 -0
  65. package/dist/examples/nodetool-base/Every Hour, On the Hour.json +162 -0
  66. package/dist/examples/nodetool-base/Executive Summary of a Long Document.json +135 -0
  67. package/dist/examples/nodetool-base/Explainer Clip from a Paragraph.json +244 -0
  68. package/dist/examples/nodetool-base/Extract the Claims Worth Checking.json +135 -0
  69. package/dist/examples/nodetool-base/Fade Audio In and Out.json +203 -0
  70. package/dist/examples/nodetool-base/Fade Audio Out.json +202 -0
  71. package/dist/examples/nodetool-base/Fill a Template.json +149 -0
  72. package/dist/examples/nodetool-base/Find Every Number in a Sentence.json +127 -0
  73. package/dist/examples/nodetool-base/Find and Replace.json +224 -0
  74. package/dist/examples/nodetool-base/Find the Edges.json +123 -0
  75. package/dist/examples/nodetool-base/Fit an Image.json +179 -0
  76. package/dist/examples/nodetool-base/Five Headlines for a Landing Page.json +135 -0
  77. package/dist/examples/nodetool-base/Flashcard Generator.json +297 -0
  78. package/dist/examples/nodetool-base/Gate Out the Room Noise.json +124 -0
  79. package/dist/examples/nodetool-base/Generate a Native-Audio UGC Testimonial.json +276 -0
  80. package/dist/examples/nodetool-base/Generate a Silent Bed.json +126 -0
  81. package/dist/examples/nodetool-base/Generate then Upscale a Poster.json +164 -0
  82. package/dist/examples/nodetool-base/Hacker News Agent.json +183 -0
  83. package/dist/examples/nodetool-base/Hook & Thumbnail Factory.json +498 -0
  84. package/dist/examples/nodetool-base/How Long Is This Transcript.json +185 -0
  85. package/dist/examples/nodetool-base/Image Enhance.json +272 -0
  86. package/dist/examples/nodetool-base/Image To Audio Story.json +180 -0
  87. package/dist/examples/nodetool-base/Image to Video Animation.json +346 -0
  88. package/dist/examples/nodetool-base/Inspect a Clip's Audio.json +187 -0
  89. package/dist/examples/nodetool-base/Inspect a Clip.json +163 -0
  90. package/dist/examples/nodetool-base/Invert a Still.json +120 -0
  91. package/dist/examples/nodetool-base/Keep Only the Long Lines.json +220 -0
  92. package/dist/examples/nodetool-base/Keep Only the Matches.json +148 -0
  93. package/dist/examples/nodetool-base/Key Out a Green Screen.json +124 -0
  94. package/dist/examples/nodetool-base/Last One Wins.json +144 -0
  95. package/dist/examples/nodetool-base/Lip-sync a Clip to a Voice Track.json +313 -0
  96. package/dist/examples/nodetool-base/Localise a Product Listing.json +135 -0
  97. package/dist/examples/nodetool-base/Localise a Script and Revoice It.json +242 -0
  98. package/dist/examples/nodetool-base/Localized Explainer.json +430 -0
  99. package/dist/examples/nodetool-base/Low-poly Adventure Starter Pack.json +786 -0
  100. package/dist/examples/nodetool-base/Low-poly Seaside Starter Pack.json +786 -0
  101. package/dist/examples/nodetool-base/Master a Voice Track.json +180 -0
  102. package/dist/examples/nodetool-base/Meeting Transcript Summarizer.json +524 -0
  103. package/dist/examples/nodetool-base/Model Arena.json +550 -0
  104. package/dist/examples/nodetool-base/Moodboard Frame from a Theme.json +129 -0
  105. package/dist/examples/nodetool-base/Movie Posters.json +660 -0
  106. package/dist/examples/nodetool-base/Movie Trailer Generator.json +418 -0
  107. package/dist/examples/nodetool-base/Music Video Visualizer.json +720 -0
  108. package/dist/examples/nodetool-base/Name a File from Its Narration.json +231 -0
  109. package/dist/examples/nodetool-base/Narrate a Script.json +169 -0
  110. package/dist/examples/nodetool-base/Narration with a Music Bed.json +360 -0
  111. package/dist/examples/nodetool-base/Normalize a Clip's Audio.json +201 -0
  112. package/dist/examples/nodetool-base/One Tagline, Six Markets.json +135 -0
  113. package/dist/examples/nodetool-base/Pad a Canvas.json +188 -0
  114. package/dist/examples/nodetool-base/Per-SKU Ad Factory.json +511 -0
  115. package/dist/examples/nodetool-base/Photo Enhancement Suite.json +417 -0
  116. package/dist/examples/nodetool-base/Picture in Picture.json +231 -0
  117. package/dist/examples/nodetool-base/Pitch a Voice Up.json +122 -0
  118. package/dist/examples/nodetool-base/Pixelate a Still.json +122 -0
  119. package/dist/examples/nodetool-base/Platformer Playground Starter Pack.json +786 -0
  120. package/dist/examples/nodetool-base/Podcast Episode to Show Notes.json +198 -0
  121. package/dist/examples/nodetool-base/Podcast Repurposing Studio.json +745 -0
  122. package/dist/examples/nodetool-base/Pokemon Maker.json +282 -0
  123. package/dist/examples/nodetool-base/Poster on Kie.json +172 -0
  124. package/dist/examples/nodetool-base/Posterize to Four Bits.json +122 -0
  125. package/dist/examples/nodetool-base/Private Assistant.json +236 -0
  126. package/dist/examples/nodetool-base/Product Mockup Generator.json +608 -0
  127. package/dist/examples/nodetool-base/Product Spot from Text.json +330 -0
  128. package/dist/examples/nodetool-base/Product Video Generator.json +344 -0
  129. package/dist/examples/nodetool-base/Prompt Template.json +221 -0
  130. package/dist/examples/nodetool-base/Propose Three Campaign Directions.json +614 -0
  131. package/dist/examples/nodetool-base/Pull a Field out of JSON Text.json +128 -0
  132. package/dist/examples/nodetool-base/Pull a Still from a Clip.json +179 -0
  133. package/dist/examples/nodetool-base/Pull the Quotable Lines Out of an Interview.json +198 -0
  134. package/dist/examples/nodetool-base/Put a Product on a Studio Backdrop.json +252 -0
  135. package/dist/examples/nodetool-base/Put a Voice Over a Music Bed.json +358 -0
  136. package/dist/examples/nodetool-base/README_EXAMPLES.md +59 -0
  137. package/dist/examples/nodetool-base/Re-delimit a List.json +199 -0
  138. package/dist/examples/nodetool-base/Read a Clip's Frame Rate.json +120 -0
  139. package/dist/examples/nodetool-base/Redact Email Addresses.json +129 -0
  140. package/dist/examples/nodetool-base/Redact and Tidy a Log Line.json +266 -0
  141. package/dist/examples/nodetool-base/Relight a Product for a Seasonal Campaign.json +131 -0
  142. package/dist/examples/nodetool-base/Render a Directed Campaign Hero.json +338 -0
  143. package/dist/examples/nodetool-base/Reopen a Directed Campaign.json +1748 -0
  144. package/dist/examples/nodetool-base/Research Agent.json +231 -0
  145. package/dist/examples/nodetool-base/Research a Brand from Its Website.json +445 -0
  146. package/dist/examples/nodetool-base/Restyle a Photo as an Illustration.json +136 -0
  147. package/dist/examples/nodetool-base/Reverse Audio.json +201 -0
  148. package/dist/examples/nodetool-base/Reverse a Clip.json +176 -0
  149. package/dist/examples/nodetool-base/Revise an Accepted Campaign Hero.json +486 -0
  150. package/dist/examples/nodetool-base/Rewrite This So a Customer Understands It.json +135 -0
  151. package/dist/examples/nodetool-base/Rotate Footage.json +178 -0
  152. package/dist/examples/nodetool-base/SEO Content Engine.json +708 -0
  153. package/dist/examples/nodetool-base/SEO Title and Meta Description.json +135 -0
  154. package/dist/examples/nodetool-base/Sci-fi Game Model Starter Pack.json +693 -0
  155. package/dist/examples/nodetool-base/Sci-fi Outpost Expansion.json +601 -0
  156. package/dist/examples/nodetool-base/Score a Clip from Its Own Mood.json +229 -0
  157. package/dist/examples/nodetool-base/Score a Silent Clip.json +382 -0
  158. package/dist/examples/nodetool-base/Script to Narrated Clip.json +224 -0
  159. package/dist/examples/nodetool-base/Script to Screen.json +940 -0
  160. package/dist/examples/nodetool-base/Sharpen Footage.json +179 -0
  161. package/dist/examples/nodetool-base/Shot List from a Synopsis.json +136 -0
  162. package/dist/examples/nodetool-base/Single Shot from a Line.json +199 -0
  163. package/dist/examples/nodetool-base/Slice Text.json +159 -0
  164. package/dist/examples/nodetool-base/Slow a Clip Without Dropping Pitch.json +122 -0
  165. package/dist/examples/nodetool-base/Soften a Plate.json +178 -0
  166. package/dist/examples/nodetool-base/Solarize a Still.json +122 -0
  167. package/dist/examples/nodetool-base/Speak a Line, Then Trim the Silence.json +157 -0
  168. package/dist/examples/nodetool-base/Spin a Packshot into a Turntable Clip.json +137 -0
  169. package/dist/examples/nodetool-base/Split a Channel.json +178 -0
  170. package/dist/examples/nodetool-base/Split a Log into Records.json +128 -0
  171. package/dist/examples/nodetool-base/Stabilize Handheld.json +179 -0
  172. package/dist/examples/nodetool-base/Still on Replicate.json +172 -0
  173. package/dist/examples/nodetool-base/Subject Lines Worth Testing.json +135 -0
  174. package/dist/examples/nodetool-base/Subtitle Lines from a Script.json +170 -0
  175. package/dist/examples/nodetool-base/Subtitle Text from a Recording.json +198 -0
  176. package/dist/examples/nodetool-base/Summarise a Recorded Call.json +198 -0
  177. package/dist/examples/nodetool-base/Swirl and Echo.json +154 -0
  178. package/dist/examples/nodetool-base/Take While the Numbers Are Small.json +148 -0
  179. package/dist/examples/nodetool-base/Take a Product Shot to Print Resolution.json +209 -0
  180. package/dist/examples/nodetool-base/Take the First Few Seconds of a Bed.json +127 -0
  181. package/dist/examples/nodetool-base/Take the First Few.json +170 -0
  182. package/dist/examples/nodetool-base/Telephone Voice.json +174 -0
  183. package/dist/examples/nodetool-base/Themes Across Customer Feedback.json +135 -0
  184. package/dist/examples/nodetool-base/Three Ratios.json +315 -0
  185. package/dist/examples/nodetool-base/Tidy Up Messy Text.json +199 -0
  186. package/dist/examples/nodetool-base/Top-down Native Asset Pack.json +955 -0
  187. package/dist/examples/nodetool-base/Trailer Beats from a Premise.json +136 -0
  188. package/dist/examples/nodetool-base/Transcribe Audio.json +138 -0
  189. package/dist/examples/nodetool-base/Transcribe a Clip.json +198 -0
  190. package/dist/examples/nodetool-base/Translate Marketing Copy to German.json +135 -0
  191. package/dist/examples/nodetool-base/Translate a Script for Dubbing.json +224 -0
  192. package/dist/examples/nodetool-base/Trim Audio to a Range.json +203 -0
  193. package/dist/examples/nodetool-base/Trim a Blurb to Length.json +128 -0
  194. package/dist/examples/nodetool-base/Trim a Clip.json +180 -0
  195. package/dist/examples/nodetool-base/Turn Research Notes into a Comparison Table.json +136 -0
  196. package/dist/examples/nodetool-base/Turn Specs into Product Copy.json +135 -0
  197. package/dist/examples/nodetool-base/Turn a Changelog into Release Notes.json +135 -0
  198. package/dist/examples/nodetool-base/Turn a Logo into Print-Ready Vector.json +126 -0
  199. package/dist/examples/nodetool-base/Turn a Support Thread into a Bug Report.json +135 -0
  200. package/dist/examples/nodetool-base/Turn a Talk into a Blog Post.json +198 -0
  201. package/dist/examples/nodetool-base/Upscale a Still.json +186 -0
  202. package/dist/examples/nodetool-base/Vectorize a Generated Logo.json +162 -0
  203. package/dist/examples/nodetool-base/Video Restyle Studio.json +281 -0
  204. package/dist/examples/nodetool-base/Vignette a Portrait.json +124 -0
  205. package/dist/examples/nodetool-base/Voice a Script in Two Voices.json +217 -0
  206. package/dist/examples/nodetool-base/Warm Up a Cold Clip.json +124 -0
  207. package/dist/examples/nodetool-base/Watch a Folder, Act on What Lands.json +177 -0
  208. package/dist/examples/nodetool-base/Widen a Mono Voice.json +151 -0
  209. package/dist/examples/nodetool-base/Workflow As A Tool.json +257 -0
  210. package/dist/examples/nodetool-base/Write a Listing from the Product Photo.json +139 -0
  211. package/dist/examples/nodetool-base/Write the Prompt, Then Make the Image.json +200 -0
  212. package/dist/examples/nodetool-base/gallery_thumbnail_generator.ts +384 -0
  213. package/dist/fake-runtime.d.ts.map +1 -1
  214. package/dist/fake-runtime.js +5 -1
  215. package/dist/fake-runtime.js.map +1 -1
  216. package/dist/http-api.d.ts.map +1 -1
  217. package/dist/http-api.js +20 -3
  218. package/dist/http-api.js.map +1 -1
  219. package/dist/lib/openverse.d.ts +47 -0
  220. package/dist/lib/openverse.d.ts.map +1 -0
  221. package/dist/lib/openverse.js +255 -0
  222. package/dist/lib/openverse.js.map +1 -0
  223. package/dist/model-execution-availability.d.ts.map +1 -1
  224. package/dist/model-execution-availability.js +7 -2
  225. package/dist/model-execution-availability.js.map +1 -1
  226. package/dist/screenshot-server.js +87 -49
  227. package/dist/screenshot-server.js.map +1 -1
  228. package/dist/trpc/router.d.ts +2 -0
  229. package/dist/trpc/router.d.ts.map +1 -1
  230. package/dist/trpc/router.js +2 -0
  231. package/dist/trpc/router.js.map +1 -1
  232. package/dist/trpc/routers/games.d.ts +179 -0
  233. package/dist/trpc/routers/games.d.ts.map +1 -1
  234. package/dist/trpc/routers/games.js +1 -1
  235. package/dist/trpc/routers/games.js.map +1 -1
  236. package/dist/trpc/routers/models.d.ts +270 -0
  237. package/dist/trpc/routers/models.d.ts.map +1 -1
  238. package/dist/trpc/routers/models.js +37 -19
  239. package/dist/trpc/routers/models.js.map +1 -1
  240. package/dist/trpc/routers/openverse.d.ts +91 -0
  241. package/dist/trpc/routers/openverse.d.ts.map +1 -0
  242. package/dist/trpc/routers/openverse.js +98 -0
  243. package/dist/trpc/routers/openverse.js.map +1 -0
  244. package/dist/trpc/sandbox-coverage.d.ts.map +1 -1
  245. package/dist/trpc/sandbox-coverage.js +11 -0
  246. package/dist/trpc/sandbox-coverage.js.map +1 -1
  247. package/dist/websocket-client-session.d.ts +8 -0
  248. package/dist/websocket-client-session.d.ts.map +1 -1
  249. package/dist/websocket-client-session.js +31 -1
  250. package/dist/websocket-client-session.js.map +1 -1
  251. package/package.json +27 -28
@@ -0,0 +1,1748 @@
1
+ {
2
+ "id": "directed-campaign-restore",
3
+ "access": "private",
4
+ "created_at": "2026-09-12T00:00:00.000Z",
5
+ "updated_at": "2026-09-12T00:00:00.000Z",
6
+ "name": "Reopen a Directed Campaign",
7
+ "tool_name": null,
8
+ "description": "Validate a portable schema-version-1 campaign record and restore its accepted original and optional revision without calling a model.",
9
+ "tags": [
10
+ "campaign",
11
+ "document",
12
+ "example"
13
+ ],
14
+ "thumbnail": null,
15
+ "thumbnail_url": null,
16
+ "graph": {
17
+ "nodes": [
18
+ {
19
+ "id": "intro",
20
+ "type": "nodetool.workflows.base_node.Comment",
21
+ "data": {
22
+ "comment": "# Reopen a Directed Campaign\n\nRestores a saved campaign from its portable directed-campaign.json record, with the accepted original and the optional revision. Use it to pick up a campaign later or on another machine. No model or provider runs.\n\n**How it works:** \"Campaign record\" feeds every Code node. \"Validate complete campaign record\" checks the size limit of 16 MiB, the JSON MIME type, schema version 1, the brief, copy, selected direction and contracts. Ten \"Validate ...\" nodes then check each embedded image or SVG payload in turn, passing a gate string along the chain. Only after the last check do ten \"Restore ...\" nodes turn the base64 payloads back into the hero, 4:5, 9:16 and SVG outputs for the original and the revision. \"Finish restored campaign\" writes the contracts, \"Original record\" and \"Restored phase\", and its ready flag lets \"Release validated record\" pass the file to the \"Campaign record\" output.\n\n**Try this:** Open a copy of the record, change \"schemaVersion\" to 2 and run it. \"Validate complete campaign record\" stops the run before any output is written."
23
+ },
24
+ "ui_properties": {
25
+ "position": {
26
+ "x": 0,
27
+ "y": 0
28
+ },
29
+ "width": 560,
30
+ "height": 423,
31
+ "selectable": true
32
+ },
33
+ "dynamic_properties": {}
34
+ },
35
+ {
36
+ "id": "in-record_file",
37
+ "type": "nodetool.input.DocumentInput",
38
+ "data": {
39
+ "name": "record_file",
40
+ "value": {
41
+ "type": "document",
42
+ "uri": ""
43
+ },
44
+ "description": "A directed-campaign.json record exported by Compose Directed Campaign Formats."
45
+ },
46
+ "ui_properties": {
47
+ "position": {
48
+ "x": 0,
49
+ "y": 990
50
+ },
51
+ "width": 300,
52
+ "title": "Campaign record"
53
+ },
54
+ "dynamic_properties": {},
55
+ "dynamic_outputs": {}
56
+ },
57
+ {
58
+ "id": "validate-record",
59
+ "type": "nodetool.code.Code",
60
+ "data": {
61
+ "code": "const MAX_RECORD_BYTES = 16 * 1024 * 1024;\nconst fileInfo = await media.info(inputs.record_file);\nif (fileInfo.size > MAX_RECORD_BYTES) throw new Error(\"The campaign record exceeds the 16 MiB portable record limit.\");\nif (fileInfo.mimeType !== \"application/json\") throw new Error(`Campaign record MIME type must be application/json, received ${fileInfo.mimeType || \"unknown\"}.`);\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record contains malformed JSON. The current campaign was not changed.\"); }\nif (!record || record.schemaVersion !== 1) throw new Error(\"Campaign record schemaVersion must be 1. The current campaign was not changed.\");\nif (![\"original\", \"revision\"].includes(record.acceptedVersion)) throw new Error(\"Campaign record acceptedVersion must be original or revision.\");\nconst text = (label, value, max) => {\n if (typeof value !== \"string\" || value.length === 0) throw new Error(`${label} is required.`);\n if (value.length > max) throw new Error(`${label} exceeds ${max} characters.`);\n return value;\n};\nif (!record.brief || typeof record.brief !== \"object\") throw new Error(\"Campaign record brief is missing.\");\ntext(\"Product name\", record.brief.productName, 120);\ntext(\"Campaign message\", record.brief.campaignMessage, 600);\ntext(\"Audience\", record.brief.audience, 300);\ntext(\"Headline\", record.brief.headline, 120);\ntext(\"CTA\", record.brief.cta, 80);\nconst roles = [\"none\", \"composition\", \"palette\", \"material\", \"lighting\"];\nif (!record.references || !roles.includes(record.references.role)) throw new Error(\"Campaign record reference role is invalid.\");\nif (String(record.references.use ?? \"\").length > 300 || String(record.references.ignore ?? \"\").length > 300) throw new Error(\"Campaign record reference notes exceed 300 characters.\");\nconst directionFields = [\"id\", \"title\", \"setting\", \"composition\", \"lighting\", \"palette\", \"preservationInstructions\"];\nif (!record.selectedDirection || record.selectedDirection.id === undefined) throw new Error(\"Campaign record selected direction is missing.\");\nif (![\"A\", \"B\", \"C\"].includes(record.selectedDirection.id)) throw new Error(\"Campaign record selected direction ID is invalid.\");\nfor (const field of directionFields) text(`Selected direction ${field}`, record.selectedDirection[field], field === \"id\" ? 1 : 600);\nif (!record.approvedCopy || record.approvedCopy.productName !== record.brief.productName || record.approvedCopy.headline !== record.brief.headline || record.approvedCopy.cta !== record.brief.cta) throw new Error(\"Campaign record approved copy does not match its brief.\");\nconst validateContract = (contract, kind, label) => {\n if (!contract || contract.schemaVersion !== 1 || contract.kind !== kind || !contract.brief || !contract.copy || !contract.selectedDirection) throw new Error(`${label} is incomplete.`);\n if (contract.copy.productName !== record.approvedCopy.productName || contract.copy.headline !== record.approvedCopy.headline || contract.copy.cta !== record.approvedCopy.cta) throw new Error(`${label} copy does not match the approved copy.`);\n};\nif (!record.original || typeof record.original !== \"object\") throw new Error(\"Campaign record original section is missing.\");\nvalidateContract(record.original.contract, \"hero\", \"Original contract\");\nif (record.acceptedVersion === \"revision\") {\n if (!record.revision || typeof record.revision !== \"object\") throw new Error(\"A revision record must include the revision section.\");\n validateContract(record.revision.contract, \"revision\", \"Revision contract\");\n if (JSON.stringify(record.revision.contract.parentContract) !== JSON.stringify(record.original.contract)) throw new Error(\"Revision ancestry does not match the accepted original contract.\");\n if (!record.revision.request || JSON.stringify(record.revision.request) !== JSON.stringify(record.revision.contract.revisionRequest)) throw new Error(\"Revision request does not match the revision contract.\");\n text(\"Revision change\", record.revision.request.change, 500);\n text(\"Revision preserve\", record.revision.request.preserve, 700);\n text(\"Revision allow\", record.revision.request.allow, 500);\n} else if (record.revision !== undefined && record.revision !== null) {\n throw new Error(\"An original-only record cannot contain an accepted revision.\");\n}\nawait output(\"gate\", \"validated\");"
62
+ },
63
+ "ui_properties": {
64
+ "position": {
65
+ "x": 400,
66
+ "y": 934
67
+ },
68
+ "width": 500,
69
+ "title": "Validate complete campaign record"
70
+ },
71
+ "dynamic_properties": {
72
+ "record_file": {
73
+ "type": "document",
74
+ "uri": ""
75
+ }
76
+ },
77
+ "dynamic_inputs": {
78
+ "record_file": {
79
+ "type": {
80
+ "type": "document"
81
+ }
82
+ }
83
+ },
84
+ "dynamic_outputs": {
85
+ "gate": {
86
+ "type": "str",
87
+ "type_args": [],
88
+ "optional": false
89
+ }
90
+ },
91
+ "sync_mode": "on_all"
92
+ },
93
+ {
94
+ "id": "validate-original-source",
95
+ "type": "nodetool.code.Code",
96
+ "data": {
97
+ "code": "if (String(inputs.gate ?? \"\") !== \"validated\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.original?.source;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-original-source\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"original source is missing.\");\n const allowed = \"source\" === \"svg\" ? [\"image/svg+xml\"] : \"source\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`original source has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = null;\n const height = null;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`original source must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"original source has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"original source has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"original source has invalid base64 data.\");\n await output(\"gate\", \"validate-original-source\");\n}"
98
+ },
99
+ "ui_properties": {
100
+ "position": {
101
+ "x": 1000,
102
+ "y": 938
103
+ },
104
+ "width": 420,
105
+ "title": "Validate original source"
106
+ },
107
+ "dynamic_properties": {
108
+ "record_file": {
109
+ "type": "document",
110
+ "uri": ""
111
+ },
112
+ "gate": ""
113
+ },
114
+ "dynamic_inputs": {
115
+ "record_file": {
116
+ "type": {
117
+ "type": "document"
118
+ }
119
+ },
120
+ "gate": {
121
+ "type": {
122
+ "type": "str"
123
+ }
124
+ }
125
+ },
126
+ "dynamic_outputs": {
127
+ "gate": {
128
+ "type": "str",
129
+ "type_args": [],
130
+ "optional": false
131
+ }
132
+ },
133
+ "sync_mode": "on_all"
134
+ },
135
+ {
136
+ "id": "validate-original-portrait",
137
+ "type": "nodetool.code.Code",
138
+ "data": {
139
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-original-source\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.original?.portrait;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-original-portrait\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"original portrait is missing.\");\n const allowed = \"png\" === \"svg\" ? [\"image/svg+xml\"] : \"png\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`original portrait has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1350;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`original portrait must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"original portrait has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"original portrait has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"original portrait has invalid base64 data.\");\n await output(\"gate\", \"validate-original-portrait\");\n}"
140
+ },
141
+ "ui_properties": {
142
+ "position": {
143
+ "x": 1520,
144
+ "y": 945
145
+ },
146
+ "width": 420,
147
+ "title": "Validate original portrait"
148
+ },
149
+ "dynamic_properties": {
150
+ "record_file": {
151
+ "type": "document",
152
+ "uri": ""
153
+ },
154
+ "gate": ""
155
+ },
156
+ "dynamic_inputs": {
157
+ "record_file": {
158
+ "type": {
159
+ "type": "document"
160
+ }
161
+ },
162
+ "gate": {
163
+ "type": {
164
+ "type": "str"
165
+ }
166
+ }
167
+ },
168
+ "dynamic_outputs": {
169
+ "gate": {
170
+ "type": "str",
171
+ "type_args": [],
172
+ "optional": false
173
+ }
174
+ },
175
+ "sync_mode": "on_all"
176
+ },
177
+ {
178
+ "id": "validate-original-story",
179
+ "type": "nodetool.code.Code",
180
+ "data": {
181
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-original-portrait\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.original?.story;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-original-story\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"original story is missing.\");\n const allowed = \"png\" === \"svg\" ? [\"image/svg+xml\"] : \"png\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`original story has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1920;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`original story must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"original story has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"original story has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"original story has invalid base64 data.\");\n await output(\"gate\", \"validate-original-story\");\n}"
182
+ },
183
+ "ui_properties": {
184
+ "position": {
185
+ "x": 2040,
186
+ "y": 945
187
+ },
188
+ "width": 420,
189
+ "title": "Validate original story"
190
+ },
191
+ "dynamic_properties": {
192
+ "record_file": {
193
+ "type": "document",
194
+ "uri": ""
195
+ },
196
+ "gate": ""
197
+ },
198
+ "dynamic_inputs": {
199
+ "record_file": {
200
+ "type": {
201
+ "type": "document"
202
+ }
203
+ },
204
+ "gate": {
205
+ "type": {
206
+ "type": "str"
207
+ }
208
+ }
209
+ },
210
+ "dynamic_outputs": {
211
+ "gate": {
212
+ "type": "str",
213
+ "type_args": [],
214
+ "optional": false
215
+ }
216
+ },
217
+ "sync_mode": "on_all"
218
+ },
219
+ {
220
+ "id": "validate-original-portrait-svg",
221
+ "type": "nodetool.code.Code",
222
+ "data": {
223
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-original-story\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.original?.portraitSvg;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-original-portrait-svg\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"original portraitSvg is missing.\");\n const allowed = \"svg\" === \"svg\" ? [\"image/svg+xml\"] : \"svg\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`original portraitSvg has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1350;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`original portraitSvg must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"original portraitSvg has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"original portraitSvg has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"original portraitSvg has invalid base64 data.\");\n await output(\"gate\", \"validate-original-portrait-svg\");\n}"
224
+ },
225
+ "ui_properties": {
226
+ "position": {
227
+ "x": 2560,
228
+ "y": 945
229
+ },
230
+ "width": 420,
231
+ "title": "Validate original portraitSvg"
232
+ },
233
+ "dynamic_properties": {
234
+ "record_file": {
235
+ "type": "document",
236
+ "uri": ""
237
+ },
238
+ "gate": ""
239
+ },
240
+ "dynamic_inputs": {
241
+ "record_file": {
242
+ "type": {
243
+ "type": "document"
244
+ }
245
+ },
246
+ "gate": {
247
+ "type": {
248
+ "type": "str"
249
+ }
250
+ }
251
+ },
252
+ "dynamic_outputs": {
253
+ "gate": {
254
+ "type": "str",
255
+ "type_args": [],
256
+ "optional": false
257
+ }
258
+ },
259
+ "sync_mode": "on_all"
260
+ },
261
+ {
262
+ "id": "validate-original-story-svg",
263
+ "type": "nodetool.code.Code",
264
+ "data": {
265
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-original-portrait-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.original?.storySvg;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-original-story-svg\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"original storySvg is missing.\");\n const allowed = \"svg\" === \"svg\" ? [\"image/svg+xml\"] : \"svg\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`original storySvg has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1920;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`original storySvg must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"original storySvg has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"original storySvg has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"original storySvg has invalid base64 data.\");\n await output(\"gate\", \"validate-original-story-svg\");\n}"
266
+ },
267
+ "ui_properties": {
268
+ "position": {
269
+ "x": 3080,
270
+ "y": 945
271
+ },
272
+ "width": 420,
273
+ "title": "Validate original storySvg"
274
+ },
275
+ "dynamic_properties": {
276
+ "record_file": {
277
+ "type": "document",
278
+ "uri": ""
279
+ },
280
+ "gate": ""
281
+ },
282
+ "dynamic_inputs": {
283
+ "record_file": {
284
+ "type": {
285
+ "type": "document"
286
+ }
287
+ },
288
+ "gate": {
289
+ "type": {
290
+ "type": "str"
291
+ }
292
+ }
293
+ },
294
+ "dynamic_outputs": {
295
+ "gate": {
296
+ "type": "str",
297
+ "type_args": [],
298
+ "optional": false
299
+ }
300
+ },
301
+ "sync_mode": "on_all"
302
+ },
303
+ {
304
+ "id": "validate-revision-source",
305
+ "type": "nodetool.code.Code",
306
+ "data": {
307
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-original-story-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.revision?.source;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-revision-source\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"revision source is missing.\");\n const allowed = \"source\" === \"svg\" ? [\"image/svg+xml\"] : \"source\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`revision source has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = null;\n const height = null;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`revision source must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"revision source has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"revision source has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"revision source has invalid base64 data.\");\n await output(\"gate\", \"validate-revision-source\");\n}"
308
+ },
309
+ "ui_properties": {
310
+ "position": {
311
+ "x": 3600,
312
+ "y": 945
313
+ },
314
+ "width": 420,
315
+ "title": "Validate revision source"
316
+ },
317
+ "dynamic_properties": {
318
+ "record_file": {
319
+ "type": "document",
320
+ "uri": ""
321
+ },
322
+ "gate": ""
323
+ },
324
+ "dynamic_inputs": {
325
+ "record_file": {
326
+ "type": {
327
+ "type": "document"
328
+ }
329
+ },
330
+ "gate": {
331
+ "type": {
332
+ "type": "str"
333
+ }
334
+ }
335
+ },
336
+ "dynamic_outputs": {
337
+ "gate": {
338
+ "type": "str",
339
+ "type_args": [],
340
+ "optional": false
341
+ }
342
+ },
343
+ "sync_mode": "on_all"
344
+ },
345
+ {
346
+ "id": "validate-revision-portrait",
347
+ "type": "nodetool.code.Code",
348
+ "data": {
349
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-revision-source\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.revision?.portrait;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-revision-portrait\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"revision portrait is missing.\");\n const allowed = \"png\" === \"svg\" ? [\"image/svg+xml\"] : \"png\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`revision portrait has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1350;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`revision portrait must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"revision portrait has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"revision portrait has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"revision portrait has invalid base64 data.\");\n await output(\"gate\", \"validate-revision-portrait\");\n}"
350
+ },
351
+ "ui_properties": {
352
+ "position": {
353
+ "x": 4120,
354
+ "y": 945
355
+ },
356
+ "width": 420,
357
+ "title": "Validate revision portrait"
358
+ },
359
+ "dynamic_properties": {
360
+ "record_file": {
361
+ "type": "document",
362
+ "uri": ""
363
+ },
364
+ "gate": ""
365
+ },
366
+ "dynamic_inputs": {
367
+ "record_file": {
368
+ "type": {
369
+ "type": "document"
370
+ }
371
+ },
372
+ "gate": {
373
+ "type": {
374
+ "type": "str"
375
+ }
376
+ }
377
+ },
378
+ "dynamic_outputs": {
379
+ "gate": {
380
+ "type": "str",
381
+ "type_args": [],
382
+ "optional": false
383
+ }
384
+ },
385
+ "sync_mode": "on_all"
386
+ },
387
+ {
388
+ "id": "validate-revision-story",
389
+ "type": "nodetool.code.Code",
390
+ "data": {
391
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-revision-portrait\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.revision?.story;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-revision-story\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"revision story is missing.\");\n const allowed = \"png\" === \"svg\" ? [\"image/svg+xml\"] : \"png\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`revision story has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1920;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`revision story must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"revision story has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"revision story has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"revision story has invalid base64 data.\");\n await output(\"gate\", \"validate-revision-story\");\n}"
392
+ },
393
+ "ui_properties": {
394
+ "position": {
395
+ "x": 4640,
396
+ "y": 944
397
+ },
398
+ "width": 420,
399
+ "title": "Validate revision story"
400
+ },
401
+ "dynamic_properties": {
402
+ "record_file": {
403
+ "type": "document",
404
+ "uri": ""
405
+ },
406
+ "gate": ""
407
+ },
408
+ "dynamic_inputs": {
409
+ "record_file": {
410
+ "type": {
411
+ "type": "document"
412
+ }
413
+ },
414
+ "gate": {
415
+ "type": {
416
+ "type": "str"
417
+ }
418
+ }
419
+ },
420
+ "dynamic_outputs": {
421
+ "gate": {
422
+ "type": "str",
423
+ "type_args": [],
424
+ "optional": false
425
+ }
426
+ },
427
+ "sync_mode": "on_all"
428
+ },
429
+ {
430
+ "id": "validate-revision-portrait-svg",
431
+ "type": "nodetool.code.Code",
432
+ "data": {
433
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-revision-story\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.revision?.portraitSvg;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-revision-portrait-svg\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"revision portraitSvg is missing.\");\n const allowed = \"svg\" === \"svg\" ? [\"image/svg+xml\"] : \"svg\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`revision portraitSvg has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1350;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`revision portraitSvg must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"revision portraitSvg has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"revision portraitSvg has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"revision portraitSvg has invalid base64 data.\");\n await output(\"gate\", \"validate-revision-portrait-svg\");\n}"
434
+ },
435
+ "ui_properties": {
436
+ "position": {
437
+ "x": 5160,
438
+ "y": 943
439
+ },
440
+ "width": 420,
441
+ "title": "Validate revision portraitSvg"
442
+ },
443
+ "dynamic_properties": {
444
+ "record_file": {
445
+ "type": "document",
446
+ "uri": ""
447
+ },
448
+ "gate": ""
449
+ },
450
+ "dynamic_inputs": {
451
+ "record_file": {
452
+ "type": {
453
+ "type": "document"
454
+ }
455
+ },
456
+ "gate": {
457
+ "type": {
458
+ "type": "str"
459
+ }
460
+ }
461
+ },
462
+ "dynamic_outputs": {
463
+ "gate": {
464
+ "type": "str",
465
+ "type_args": [],
466
+ "optional": false
467
+ }
468
+ },
469
+ "sync_mode": "on_all"
470
+ },
471
+ {
472
+ "id": "validate-revision-story-svg",
473
+ "type": "nodetool.code.Code",
474
+ "data": {
475
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-revision-portrait-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The campaign record could not be reread during media validation.\"); }\nconst item = record.revision?.storySvg;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n await output(\"gate\", \"validate-revision-story-svg\");\n} else {\n if (!item || typeof item !== \"object\") throw new Error(\"revision storySvg is missing.\");\n const allowed = \"svg\" === \"svg\" ? [\"image/svg+xml\"] : \"svg\" === \"png\" ? [\"image/png\"] : [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/avif\"];\n if (!allowed.includes(item.mimeType)) throw new Error(`revision storySvg has unsupported MIME type ${item.mimeType || \"unknown\"}.`);\n const width = 1080;\n const height = 1920;\n if (width !== null && (item.width !== width || item.height !== height)) throw new Error(`revision storySvg must be exactly ${width}×${height}.`);\n if (width === null && (!Number.isInteger(item.width) || !Number.isInteger(item.height) || item.width < 1 || item.height < 1 || item.width > 4096 || item.height > 4096)) throw new Error(\"revision storySvg has invalid source dimensions.\");\n if (typeof item.data !== \"string\" || item.data.length === 0 || item.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(item.data)) throw new Error(\"revision storySvg has invalid base64 data.\");\n const bytes = fromBase64(item.data);\n if (bytes.length === 0 || toBase64(bytes) !== item.data) throw new Error(\"revision storySvg has invalid base64 data.\");\n await output(\"gate\", \"validate-revision-story-svg\");\n}"
476
+ },
477
+ "ui_properties": {
478
+ "position": {
479
+ "x": 5680,
480
+ "y": 940
481
+ },
482
+ "width": 420,
483
+ "title": "Validate revision storySvg"
484
+ },
485
+ "dynamic_properties": {
486
+ "record_file": {
487
+ "type": "document",
488
+ "uri": ""
489
+ },
490
+ "gate": ""
491
+ },
492
+ "dynamic_inputs": {
493
+ "record_file": {
494
+ "type": {
495
+ "type": "document"
496
+ }
497
+ },
498
+ "gate": {
499
+ "type": {
500
+ "type": "str"
501
+ }
502
+ }
503
+ },
504
+ "dynamic_outputs": {
505
+ "gate": {
506
+ "type": "str",
507
+ "type_args": [],
508
+ "optional": false
509
+ }
510
+ },
511
+ "sync_mode": "on_all"
512
+ },
513
+ {
514
+ "id": "restore-original-source",
515
+ "type": "nodetool.code.Code",
516
+ "data": {
517
+ "code": "if (String(inputs.gate ?? \"\") !== \"validate-revision-story-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.original?.source;\nlet value;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated original source payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: item.mimeType, });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-original-source\");"
518
+ },
519
+ "ui_properties": {
520
+ "position": {
521
+ "x": 6200,
522
+ "y": 931
523
+ },
524
+ "width": 420,
525
+ "title": "Restore original hero"
526
+ },
527
+ "dynamic_properties": {
528
+ "record_file": {
529
+ "type": "document",
530
+ "uri": ""
531
+ },
532
+ "gate": ""
533
+ },
534
+ "dynamic_inputs": {
535
+ "record_file": {
536
+ "type": {
537
+ "type": "document"
538
+ }
539
+ },
540
+ "gate": {
541
+ "type": {
542
+ "type": "str"
543
+ }
544
+ }
545
+ },
546
+ "dynamic_outputs": {
547
+ "value": {
548
+ "type": "image",
549
+ "type_args": [],
550
+ "optional": false
551
+ },
552
+ "gate": {
553
+ "type": "str",
554
+ "type_args": [],
555
+ "optional": false
556
+ }
557
+ },
558
+ "sync_mode": "on_all"
559
+ },
560
+ {
561
+ "id": "restore-original-portrait",
562
+ "type": "nodetool.code.Code",
563
+ "data": {
564
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-original-source\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.original?.portrait;\nlet value;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated original portrait payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: \"image/png\", });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-original-portrait\");"
565
+ },
566
+ "ui_properties": {
567
+ "position": {
568
+ "x": 6720,
569
+ "y": 760
570
+ },
571
+ "width": 420,
572
+ "title": "Restore original 4:5"
573
+ },
574
+ "dynamic_properties": {
575
+ "record_file": {
576
+ "type": "document",
577
+ "uri": ""
578
+ },
579
+ "gate": ""
580
+ },
581
+ "dynamic_inputs": {
582
+ "record_file": {
583
+ "type": {
584
+ "type": "document"
585
+ }
586
+ },
587
+ "gate": {
588
+ "type": {
589
+ "type": "str"
590
+ }
591
+ }
592
+ },
593
+ "dynamic_outputs": {
594
+ "value": {
595
+ "type": "image",
596
+ "type_args": [],
597
+ "optional": false
598
+ },
599
+ "gate": {
600
+ "type": "str",
601
+ "type_args": [],
602
+ "optional": false
603
+ }
604
+ },
605
+ "sync_mode": "on_all"
606
+ },
607
+ {
608
+ "id": "restore-original-story",
609
+ "type": "nodetool.code.Code",
610
+ "data": {
611
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-original-portrait\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.original?.story;\nlet value;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated original story payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: \"image/png\", });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-original-story\");"
612
+ },
613
+ "ui_properties": {
614
+ "position": {
615
+ "x": 7240,
616
+ "y": 643
617
+ },
618
+ "width": 420,
619
+ "title": "Restore original 9:16"
620
+ },
621
+ "dynamic_properties": {
622
+ "record_file": {
623
+ "type": "document",
624
+ "uri": ""
625
+ },
626
+ "gate": ""
627
+ },
628
+ "dynamic_inputs": {
629
+ "record_file": {
630
+ "type": {
631
+ "type": "document"
632
+ }
633
+ },
634
+ "gate": {
635
+ "type": {
636
+ "type": "str"
637
+ }
638
+ }
639
+ },
640
+ "dynamic_outputs": {
641
+ "value": {
642
+ "type": "image",
643
+ "type_args": [],
644
+ "optional": false
645
+ },
646
+ "gate": {
647
+ "type": "str",
648
+ "type_args": [],
649
+ "optional": false
650
+ }
651
+ },
652
+ "sync_mode": "on_all"
653
+ },
654
+ {
655
+ "id": "restore-original-portrait-svg",
656
+ "type": "nodetool.code.Code",
657
+ "data": {
658
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-original-story\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.original?.portraitSvg;\nlet value;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"document\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated original portraitSvg payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toDocument(bytes, { mimeType: \"image/svg+xml\", filename: \"original-portrait.svg\" });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-original-portrait-svg\");"
659
+ },
660
+ "ui_properties": {
661
+ "position": {
662
+ "x": 7760,
663
+ "y": 924
664
+ },
665
+ "width": 420,
666
+ "title": "Restore original 4:5 SVG"
667
+ },
668
+ "dynamic_properties": {
669
+ "record_file": {
670
+ "type": "document",
671
+ "uri": ""
672
+ },
673
+ "gate": ""
674
+ },
675
+ "dynamic_inputs": {
676
+ "record_file": {
677
+ "type": {
678
+ "type": "document"
679
+ }
680
+ },
681
+ "gate": {
682
+ "type": {
683
+ "type": "str"
684
+ }
685
+ }
686
+ },
687
+ "dynamic_outputs": {
688
+ "value": {
689
+ "type": "document",
690
+ "type_args": [],
691
+ "optional": false
692
+ },
693
+ "gate": {
694
+ "type": "str",
695
+ "type_args": [],
696
+ "optional": false
697
+ }
698
+ },
699
+ "sync_mode": "on_all"
700
+ },
701
+ {
702
+ "id": "restore-original-story-svg",
703
+ "type": "nodetool.code.Code",
704
+ "data": {
705
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-original-portrait-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.original?.storySvg;\nlet value;\nif (!item && \"original\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"document\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated original storySvg payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toDocument(bytes, { mimeType: \"image/svg+xml\", filename: \"original-story.svg\" });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-original-story-svg\");"
706
+ },
707
+ "ui_properties": {
708
+ "position": {
709
+ "x": 8280,
710
+ "y": 751
711
+ },
712
+ "width": 420,
713
+ "title": "Restore original 9:16 SVG"
714
+ },
715
+ "dynamic_properties": {
716
+ "record_file": {
717
+ "type": "document",
718
+ "uri": ""
719
+ },
720
+ "gate": ""
721
+ },
722
+ "dynamic_inputs": {
723
+ "record_file": {
724
+ "type": {
725
+ "type": "document"
726
+ }
727
+ },
728
+ "gate": {
729
+ "type": {
730
+ "type": "str"
731
+ }
732
+ }
733
+ },
734
+ "dynamic_outputs": {
735
+ "value": {
736
+ "type": "document",
737
+ "type_args": [],
738
+ "optional": false
739
+ },
740
+ "gate": {
741
+ "type": "str",
742
+ "type_args": [],
743
+ "optional": false
744
+ }
745
+ },
746
+ "sync_mode": "on_all"
747
+ },
748
+ {
749
+ "id": "restore-revision-source",
750
+ "type": "nodetool.code.Code",
751
+ "data": {
752
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-original-story-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.revision?.source;\nlet value;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated revision source payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: item.mimeType, });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-revision-source\");"
753
+ },
754
+ "ui_properties": {
755
+ "position": {
756
+ "x": 8800,
757
+ "y": 627
758
+ },
759
+ "width": 420,
760
+ "title": "Restore revision hero"
761
+ },
762
+ "dynamic_properties": {
763
+ "record_file": {
764
+ "type": "document",
765
+ "uri": ""
766
+ },
767
+ "gate": ""
768
+ },
769
+ "dynamic_inputs": {
770
+ "record_file": {
771
+ "type": {
772
+ "type": "document"
773
+ }
774
+ },
775
+ "gate": {
776
+ "type": {
777
+ "type": "str"
778
+ }
779
+ }
780
+ },
781
+ "dynamic_outputs": {
782
+ "value": {
783
+ "type": "image",
784
+ "type_args": [],
785
+ "optional": false
786
+ },
787
+ "gate": {
788
+ "type": "str",
789
+ "type_args": [],
790
+ "optional": false
791
+ }
792
+ },
793
+ "sync_mode": "on_all"
794
+ },
795
+ {
796
+ "id": "restore-revision-portrait",
797
+ "type": "nodetool.code.Code",
798
+ "data": {
799
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-revision-source\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.revision?.portrait;\nlet value;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated revision portrait payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: \"image/png\", });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-revision-portrait\");"
800
+ },
801
+ "ui_properties": {
802
+ "position": {
803
+ "x": 9320,
804
+ "y": 901
805
+ },
806
+ "width": 420,
807
+ "title": "Restore revision 4:5"
808
+ },
809
+ "dynamic_properties": {
810
+ "record_file": {
811
+ "type": "document",
812
+ "uri": ""
813
+ },
814
+ "gate": ""
815
+ },
816
+ "dynamic_inputs": {
817
+ "record_file": {
818
+ "type": {
819
+ "type": "document"
820
+ }
821
+ },
822
+ "gate": {
823
+ "type": {
824
+ "type": "str"
825
+ }
826
+ }
827
+ },
828
+ "dynamic_outputs": {
829
+ "value": {
830
+ "type": "image",
831
+ "type_args": [],
832
+ "optional": false
833
+ },
834
+ "gate": {
835
+ "type": "str",
836
+ "type_args": [],
837
+ "optional": false
838
+ }
839
+ },
840
+ "sync_mode": "on_all"
841
+ },
842
+ {
843
+ "id": "restore-revision-story",
844
+ "type": "nodetool.code.Code",
845
+ "data": {
846
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-revision-portrait\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.revision?.story;\nlet value;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"image\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated revision story payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toImage(bytes, { mimeType: \"image/png\", });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-revision-story\");"
847
+ },
848
+ "ui_properties": {
849
+ "position": {
850
+ "x": 9840,
851
+ "y": 734
852
+ },
853
+ "width": 420,
854
+ "title": "Restore revision 9:16"
855
+ },
856
+ "dynamic_properties": {
857
+ "record_file": {
858
+ "type": "document",
859
+ "uri": ""
860
+ },
861
+ "gate": ""
862
+ },
863
+ "dynamic_inputs": {
864
+ "record_file": {
865
+ "type": {
866
+ "type": "document"
867
+ }
868
+ },
869
+ "gate": {
870
+ "type": {
871
+ "type": "str"
872
+ }
873
+ }
874
+ },
875
+ "dynamic_outputs": {
876
+ "value": {
877
+ "type": "image",
878
+ "type_args": [],
879
+ "optional": false
880
+ },
881
+ "gate": {
882
+ "type": "str",
883
+ "type_args": [],
884
+ "optional": false
885
+ }
886
+ },
887
+ "sync_mode": "on_all"
888
+ },
889
+ {
890
+ "id": "restore-revision-portrait-svg",
891
+ "type": "nodetool.code.Code",
892
+ "data": {
893
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-revision-story\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.revision?.portraitSvg;\nlet value;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"document\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated revision portraitSvg payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toDocument(bytes, { mimeType: \"image/svg+xml\", filename: \"revision-portrait.svg\" });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-revision-portrait-svg\");"
894
+ },
895
+ "ui_properties": {
896
+ "position": {
897
+ "x": 10360,
898
+ "y": 613
899
+ },
900
+ "width": 420,
901
+ "title": "Restore revision 4:5 SVG"
902
+ },
903
+ "dynamic_properties": {
904
+ "record_file": {
905
+ "type": "document",
906
+ "uri": ""
907
+ },
908
+ "gate": ""
909
+ },
910
+ "dynamic_inputs": {
911
+ "record_file": {
912
+ "type": {
913
+ "type": "document"
914
+ }
915
+ },
916
+ "gate": {
917
+ "type": {
918
+ "type": "str"
919
+ }
920
+ }
921
+ },
922
+ "dynamic_outputs": {
923
+ "value": {
924
+ "type": "document",
925
+ "type_args": [],
926
+ "optional": false
927
+ },
928
+ "gate": {
929
+ "type": "str",
930
+ "type_args": [],
931
+ "optional": false
932
+ }
933
+ },
934
+ "sync_mode": "on_all"
935
+ },
936
+ {
937
+ "id": "restore-revision-story-svg",
938
+ "type": "nodetool.code.Code",
939
+ "data": {
940
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-revision-portrait-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst item = record.revision?.storySvg;\nlet value;\nif (!item && \"revision\" === \"revision\" && record.acceptedVersion === \"original\") {\n value = { type: \"document\", uri: \"\", asset_id: null };\n} else {\n if (!item || typeof item.data !== \"string\") throw new Error(\"The validated revision storySvg payload is unavailable.\");\n const bytes = fromBase64(item.data);\n value = await media.toDocument(bytes, { mimeType: \"image/svg+xml\", filename: \"revision-story.svg\" });\n}\nawait output(\"value\", value);\nawait output(\"gate\", \"restore-revision-story-svg\");"
941
+ },
942
+ "ui_properties": {
943
+ "position": {
944
+ "x": 10880,
945
+ "y": 887
946
+ },
947
+ "width": 420,
948
+ "title": "Restore revision 9:16 SVG"
949
+ },
950
+ "dynamic_properties": {
951
+ "record_file": {
952
+ "type": "document",
953
+ "uri": ""
954
+ },
955
+ "gate": ""
956
+ },
957
+ "dynamic_inputs": {
958
+ "record_file": {
959
+ "type": {
960
+ "type": "document"
961
+ }
962
+ },
963
+ "gate": {
964
+ "type": {
965
+ "type": "str"
966
+ }
967
+ }
968
+ },
969
+ "dynamic_outputs": {
970
+ "value": {
971
+ "type": "document",
972
+ "type_args": [],
973
+ "optional": false
974
+ },
975
+ "gate": {
976
+ "type": "str",
977
+ "type_args": [],
978
+ "optional": false
979
+ }
980
+ },
981
+ "sync_mode": "on_all"
982
+ },
983
+ {
984
+ "id": "restore-finalize",
985
+ "type": "nodetool.code.Code",
986
+ "data": {
987
+ "code": "if (String(inputs.gate ?? \"\") !== \"restore-revision-story-svg\") return;\nlet record;\ntry { record = JSON.parse(await media.text(inputs.record_file)); } catch { throw new Error(\"The validated campaign record could not be reread.\"); }\nconst originalRecord = JSON.stringify({ schemaVersion: 1, acceptedVersion: \"original\", productName: record.brief.productName, headline: record.approvedCopy.headline, cta: record.approvedCopy.cta, hasRevision: false, portableRecord: \"record_file\" });\nawait output(\"accepted_contract\", JSON.stringify(record.original.contract));\nawait output(\"original_record\", originalRecord);\nawait output(\"revised_contract\", record.acceptedVersion === \"revision\" ? JSON.stringify(record.revision.contract) : \"\");\nawait output(\"ready\", true);\nawait output(\"phase\", record.acceptedVersion === \"revision\" ? \"complete\" : \"campaign_ready\");"
988
+ },
989
+ "ui_properties": {
990
+ "position": {
991
+ "x": 11400,
992
+ "y": 715
993
+ },
994
+ "width": 420,
995
+ "title": "Finish restored campaign"
996
+ },
997
+ "dynamic_properties": {
998
+ "record_file": {
999
+ "type": "document",
1000
+ "uri": ""
1001
+ },
1002
+ "gate": ""
1003
+ },
1004
+ "dynamic_inputs": {
1005
+ "record_file": {
1006
+ "type": {
1007
+ "type": "document"
1008
+ }
1009
+ },
1010
+ "gate": {
1011
+ "type": {
1012
+ "type": "str"
1013
+ }
1014
+ }
1015
+ },
1016
+ "dynamic_outputs": {
1017
+ "accepted_contract": {
1018
+ "type": "str",
1019
+ "type_args": [],
1020
+ "optional": false
1021
+ },
1022
+ "original_record": {
1023
+ "type": "str",
1024
+ "type_args": [],
1025
+ "optional": false
1026
+ },
1027
+ "revised_contract": {
1028
+ "type": "str",
1029
+ "type_args": [],
1030
+ "optional": false
1031
+ },
1032
+ "phase": {
1033
+ "type": "str",
1034
+ "type_args": [],
1035
+ "optional": false
1036
+ },
1037
+ "ready": {
1038
+ "type": "bool",
1039
+ "type_args": [],
1040
+ "optional": false
1041
+ }
1042
+ },
1043
+ "sync_mode": "on_all"
1044
+ },
1045
+ {
1046
+ "id": "restore-record-file",
1047
+ "type": "nodetool.control.If",
1048
+ "data": {
1049
+ "condition": false,
1050
+ "value": null
1051
+ },
1052
+ "ui_properties": {
1053
+ "position": {
1054
+ "x": 11920,
1055
+ "y": 1303
1056
+ },
1057
+ "width": 280,
1058
+ "title": "Release validated record"
1059
+ },
1060
+ "dynamic_properties": {},
1061
+ "dynamic_outputs": {}
1062
+ },
1063
+ {
1064
+ "id": "out-accepted_hero",
1065
+ "type": "nodetool.output.Output",
1066
+ "data": {
1067
+ "name": "accepted_hero"
1068
+ },
1069
+ "ui_properties": {
1070
+ "position": {
1071
+ "x": 6720,
1072
+ "y": 1197
1073
+ },
1074
+ "width": 240,
1075
+ "title": "Accepted hero"
1076
+ },
1077
+ "dynamic_properties": {},
1078
+ "dynamic_outputs": {}
1079
+ },
1080
+ {
1081
+ "id": "out-accepted_contract",
1082
+ "type": "nodetool.output.Output",
1083
+ "data": {
1084
+ "name": "accepted_contract"
1085
+ },
1086
+ "ui_properties": {
1087
+ "position": {
1088
+ "x": 11920,
1089
+ "y": 503
1090
+ },
1091
+ "width": 240,
1092
+ "title": "Accepted contract"
1093
+ },
1094
+ "dynamic_properties": {},
1095
+ "dynamic_outputs": {}
1096
+ },
1097
+ {
1098
+ "id": "out-original_portrait",
1099
+ "type": "nodetool.output.Output",
1100
+ "data": {
1101
+ "name": "original_portrait"
1102
+ },
1103
+ "ui_properties": {
1104
+ "position": {
1105
+ "x": 7240,
1106
+ "y": 1080
1107
+ },
1108
+ "width": 240,
1109
+ "title": "Original 4:5"
1110
+ },
1111
+ "dynamic_properties": {},
1112
+ "dynamic_outputs": {}
1113
+ },
1114
+ {
1115
+ "id": "out-original_story",
1116
+ "type": "nodetool.output.Output",
1117
+ "data": {
1118
+ "name": "original_story"
1119
+ },
1120
+ "ui_properties": {
1121
+ "position": {
1122
+ "x": 7760,
1123
+ "y": 724
1124
+ },
1125
+ "width": 240,
1126
+ "title": "Original 9:16"
1127
+ },
1128
+ "dynamic_properties": {},
1129
+ "dynamic_outputs": {}
1130
+ },
1131
+ {
1132
+ "id": "out-original_portrait_svg",
1133
+ "type": "nodetool.output.Output",
1134
+ "data": {
1135
+ "name": "original_portrait_svg"
1136
+ },
1137
+ "ui_properties": {
1138
+ "position": {
1139
+ "x": 8280,
1140
+ "y": 1188
1141
+ },
1142
+ "width": 240,
1143
+ "title": "Original 4:5 SVG"
1144
+ },
1145
+ "dynamic_properties": {},
1146
+ "dynamic_outputs": {}
1147
+ },
1148
+ {
1149
+ "id": "out-original_story_svg",
1150
+ "type": "nodetool.output.Output",
1151
+ "data": {
1152
+ "name": "original_story_svg"
1153
+ },
1154
+ "ui_properties": {
1155
+ "position": {
1156
+ "x": 8800,
1157
+ "y": 1064
1158
+ },
1159
+ "width": 240,
1160
+ "title": "Original 9:16 SVG"
1161
+ },
1162
+ "dynamic_properties": {},
1163
+ "dynamic_outputs": {}
1164
+ },
1165
+ {
1166
+ "id": "out-original_record",
1167
+ "type": "nodetool.output.Output",
1168
+ "data": {
1169
+ "name": "original_record"
1170
+ },
1171
+ "ui_properties": {
1172
+ "position": {
1173
+ "x": 11920,
1174
+ "y": 903
1175
+ },
1176
+ "width": 240,
1177
+ "title": "Original record"
1178
+ },
1179
+ "dynamic_properties": {},
1180
+ "dynamic_outputs": {}
1181
+ },
1182
+ {
1183
+ "id": "out-revised_hero",
1184
+ "type": "nodetool.output.Output",
1185
+ "data": {
1186
+ "name": "revised_hero"
1187
+ },
1188
+ "ui_properties": {
1189
+ "position": {
1190
+ "x": 9320,
1191
+ "y": 701
1192
+ },
1193
+ "width": 240,
1194
+ "title": "Revised hero"
1195
+ },
1196
+ "dynamic_properties": {},
1197
+ "dynamic_outputs": {}
1198
+ },
1199
+ {
1200
+ "id": "out-revised_contract",
1201
+ "type": "nodetool.output.Output",
1202
+ "data": {
1203
+ "name": "revised_contract"
1204
+ },
1205
+ "ui_properties": {
1206
+ "position": {
1207
+ "x": 11920,
1208
+ "y": 703
1209
+ },
1210
+ "width": 240,
1211
+ "title": "Revised contract"
1212
+ },
1213
+ "dynamic_properties": {},
1214
+ "dynamic_outputs": {}
1215
+ },
1216
+ {
1217
+ "id": "out-revised_portrait",
1218
+ "type": "nodetool.output.Output",
1219
+ "data": {
1220
+ "name": "revised_portrait"
1221
+ },
1222
+ "ui_properties": {
1223
+ "position": {
1224
+ "x": 9840,
1225
+ "y": 1171
1226
+ },
1227
+ "width": 240,
1228
+ "title": "Revised 4:5"
1229
+ },
1230
+ "dynamic_properties": {},
1231
+ "dynamic_outputs": {}
1232
+ },
1233
+ {
1234
+ "id": "out-revised_story",
1235
+ "type": "nodetool.output.Output",
1236
+ "data": {
1237
+ "name": "revised_story"
1238
+ },
1239
+ "ui_properties": {
1240
+ "position": {
1241
+ "x": 10360,
1242
+ "y": 1050
1243
+ },
1244
+ "width": 240,
1245
+ "title": "Revised 9:16"
1246
+ },
1247
+ "dynamic_properties": {},
1248
+ "dynamic_outputs": {}
1249
+ },
1250
+ {
1251
+ "id": "out-revised_portrait_svg",
1252
+ "type": "nodetool.output.Output",
1253
+ "data": {
1254
+ "name": "revised_portrait_svg"
1255
+ },
1256
+ "ui_properties": {
1257
+ "position": {
1258
+ "x": 10880,
1259
+ "y": 687
1260
+ },
1261
+ "width": 240,
1262
+ "title": "Revised 4:5 SVG"
1263
+ },
1264
+ "dynamic_properties": {},
1265
+ "dynamic_outputs": {}
1266
+ },
1267
+ {
1268
+ "id": "out-revised_story_svg",
1269
+ "type": "nodetool.output.Output",
1270
+ "data": {
1271
+ "name": "revised_story_svg"
1272
+ },
1273
+ "ui_properties": {
1274
+ "position": {
1275
+ "x": 11400,
1276
+ "y": 1152
1277
+ },
1278
+ "width": 240,
1279
+ "title": "Revised 9:16 SVG"
1280
+ },
1281
+ "dynamic_properties": {},
1282
+ "dynamic_outputs": {}
1283
+ },
1284
+ {
1285
+ "id": "out-record_file",
1286
+ "type": "nodetool.output.Output",
1287
+ "data": {
1288
+ "name": "record_file"
1289
+ },
1290
+ "ui_properties": {
1291
+ "position": {
1292
+ "x": 12300,
1293
+ "y": 1320
1294
+ },
1295
+ "width": 240,
1296
+ "title": "Campaign record"
1297
+ },
1298
+ "dynamic_properties": {},
1299
+ "dynamic_outputs": {}
1300
+ },
1301
+ {
1302
+ "id": "out-phase",
1303
+ "type": "nodetool.output.Output",
1304
+ "data": {
1305
+ "name": "phase"
1306
+ },
1307
+ "ui_properties": {
1308
+ "position": {
1309
+ "x": 11920,
1310
+ "y": 1103
1311
+ },
1312
+ "width": 240,
1313
+ "title": "Restored phase"
1314
+ },
1315
+ "dynamic_properties": {},
1316
+ "dynamic_outputs": {}
1317
+ }
1318
+ ],
1319
+ "edges": [
1320
+ {
1321
+ "id": "record-to-validator",
1322
+ "source": "in-record_file",
1323
+ "sourceHandle": "output",
1324
+ "target": "validate-record",
1325
+ "targetHandle": "record_file"
1326
+ },
1327
+ {
1328
+ "id": "record-to-restore-original-source",
1329
+ "source": "in-record_file",
1330
+ "sourceHandle": "output",
1331
+ "target": "restore-original-source",
1332
+ "targetHandle": "record_file"
1333
+ },
1334
+ {
1335
+ "id": "record-to-restore-original-portrait",
1336
+ "source": "in-record_file",
1337
+ "sourceHandle": "output",
1338
+ "target": "restore-original-portrait",
1339
+ "targetHandle": "record_file"
1340
+ },
1341
+ {
1342
+ "id": "gate-to-restore-original-portrait",
1343
+ "source": "restore-original-source",
1344
+ "sourceHandle": "gate",
1345
+ "target": "restore-original-portrait",
1346
+ "targetHandle": "gate"
1347
+ },
1348
+ {
1349
+ "id": "record-to-restore-original-story",
1350
+ "source": "in-record_file",
1351
+ "sourceHandle": "output",
1352
+ "target": "restore-original-story",
1353
+ "targetHandle": "record_file"
1354
+ },
1355
+ {
1356
+ "id": "gate-to-restore-original-story",
1357
+ "source": "restore-original-portrait",
1358
+ "sourceHandle": "gate",
1359
+ "target": "restore-original-story",
1360
+ "targetHandle": "gate"
1361
+ },
1362
+ {
1363
+ "id": "record-to-restore-original-portrait-svg",
1364
+ "source": "in-record_file",
1365
+ "sourceHandle": "output",
1366
+ "target": "restore-original-portrait-svg",
1367
+ "targetHandle": "record_file"
1368
+ },
1369
+ {
1370
+ "id": "gate-to-restore-original-portrait-svg",
1371
+ "source": "restore-original-story",
1372
+ "sourceHandle": "gate",
1373
+ "target": "restore-original-portrait-svg",
1374
+ "targetHandle": "gate"
1375
+ },
1376
+ {
1377
+ "id": "record-to-restore-original-story-svg",
1378
+ "source": "in-record_file",
1379
+ "sourceHandle": "output",
1380
+ "target": "restore-original-story-svg",
1381
+ "targetHandle": "record_file"
1382
+ },
1383
+ {
1384
+ "id": "gate-to-restore-original-story-svg",
1385
+ "source": "restore-original-portrait-svg",
1386
+ "sourceHandle": "gate",
1387
+ "target": "restore-original-story-svg",
1388
+ "targetHandle": "gate"
1389
+ },
1390
+ {
1391
+ "id": "record-to-restore-revision-source",
1392
+ "source": "in-record_file",
1393
+ "sourceHandle": "output",
1394
+ "target": "restore-revision-source",
1395
+ "targetHandle": "record_file"
1396
+ },
1397
+ {
1398
+ "id": "gate-to-restore-revision-source",
1399
+ "source": "restore-original-story-svg",
1400
+ "sourceHandle": "gate",
1401
+ "target": "restore-revision-source",
1402
+ "targetHandle": "gate"
1403
+ },
1404
+ {
1405
+ "id": "record-to-restore-revision-portrait",
1406
+ "source": "in-record_file",
1407
+ "sourceHandle": "output",
1408
+ "target": "restore-revision-portrait",
1409
+ "targetHandle": "record_file"
1410
+ },
1411
+ {
1412
+ "id": "gate-to-restore-revision-portrait",
1413
+ "source": "restore-revision-source",
1414
+ "sourceHandle": "gate",
1415
+ "target": "restore-revision-portrait",
1416
+ "targetHandle": "gate"
1417
+ },
1418
+ {
1419
+ "id": "record-to-restore-revision-story",
1420
+ "source": "in-record_file",
1421
+ "sourceHandle": "output",
1422
+ "target": "restore-revision-story",
1423
+ "targetHandle": "record_file"
1424
+ },
1425
+ {
1426
+ "id": "gate-to-restore-revision-story",
1427
+ "source": "restore-revision-portrait",
1428
+ "sourceHandle": "gate",
1429
+ "target": "restore-revision-story",
1430
+ "targetHandle": "gate"
1431
+ },
1432
+ {
1433
+ "id": "record-to-restore-revision-portrait-svg",
1434
+ "source": "in-record_file",
1435
+ "sourceHandle": "output",
1436
+ "target": "restore-revision-portrait-svg",
1437
+ "targetHandle": "record_file"
1438
+ },
1439
+ {
1440
+ "id": "gate-to-restore-revision-portrait-svg",
1441
+ "source": "restore-revision-story",
1442
+ "sourceHandle": "gate",
1443
+ "target": "restore-revision-portrait-svg",
1444
+ "targetHandle": "gate"
1445
+ },
1446
+ {
1447
+ "id": "record-to-restore-revision-story-svg",
1448
+ "source": "in-record_file",
1449
+ "sourceHandle": "output",
1450
+ "target": "restore-revision-story-svg",
1451
+ "targetHandle": "record_file"
1452
+ },
1453
+ {
1454
+ "id": "gate-to-restore-revision-story-svg",
1455
+ "source": "restore-revision-portrait-svg",
1456
+ "sourceHandle": "gate",
1457
+ "target": "restore-revision-story-svg",
1458
+ "targetHandle": "gate"
1459
+ },
1460
+ {
1461
+ "id": "record-to-finalize",
1462
+ "source": "in-record_file",
1463
+ "sourceHandle": "output",
1464
+ "target": "restore-finalize",
1465
+ "targetHandle": "record_file"
1466
+ },
1467
+ {
1468
+ "id": "gate-to-finalize",
1469
+ "source": "restore-revision-story-svg",
1470
+ "sourceHandle": "gate",
1471
+ "target": "restore-finalize",
1472
+ "targetHandle": "gate"
1473
+ },
1474
+ {
1475
+ "id": "restored-accepted_hero",
1476
+ "source": "restore-original-source",
1477
+ "sourceHandle": "value",
1478
+ "target": "out-accepted_hero",
1479
+ "targetHandle": "value"
1480
+ },
1481
+ {
1482
+ "id": "restored-original_portrait",
1483
+ "source": "restore-original-portrait",
1484
+ "sourceHandle": "value",
1485
+ "target": "out-original_portrait",
1486
+ "targetHandle": "value"
1487
+ },
1488
+ {
1489
+ "id": "restored-original_story",
1490
+ "source": "restore-original-story",
1491
+ "sourceHandle": "value",
1492
+ "target": "out-original_story",
1493
+ "targetHandle": "value"
1494
+ },
1495
+ {
1496
+ "id": "restored-original_portrait_svg",
1497
+ "source": "restore-original-portrait-svg",
1498
+ "sourceHandle": "value",
1499
+ "target": "out-original_portrait_svg",
1500
+ "targetHandle": "value"
1501
+ },
1502
+ {
1503
+ "id": "restored-original_story_svg",
1504
+ "source": "restore-original-story-svg",
1505
+ "sourceHandle": "value",
1506
+ "target": "out-original_story_svg",
1507
+ "targetHandle": "value"
1508
+ },
1509
+ {
1510
+ "id": "restored-revised_hero",
1511
+ "source": "restore-revision-source",
1512
+ "sourceHandle": "value",
1513
+ "target": "out-revised_hero",
1514
+ "targetHandle": "value"
1515
+ },
1516
+ {
1517
+ "id": "restored-revised_portrait",
1518
+ "source": "restore-revision-portrait",
1519
+ "sourceHandle": "value",
1520
+ "target": "out-revised_portrait",
1521
+ "targetHandle": "value"
1522
+ },
1523
+ {
1524
+ "id": "restored-revised_story",
1525
+ "source": "restore-revision-story",
1526
+ "sourceHandle": "value",
1527
+ "target": "out-revised_story",
1528
+ "targetHandle": "value"
1529
+ },
1530
+ {
1531
+ "id": "restored-revised_portrait_svg",
1532
+ "source": "restore-revision-portrait-svg",
1533
+ "sourceHandle": "value",
1534
+ "target": "out-revised_portrait_svg",
1535
+ "targetHandle": "value"
1536
+ },
1537
+ {
1538
+ "id": "restored-revised_story_svg",
1539
+ "source": "restore-revision-story-svg",
1540
+ "sourceHandle": "value",
1541
+ "target": "out-revised_story_svg",
1542
+ "targetHandle": "value"
1543
+ },
1544
+ {
1545
+ "id": "restored-accepted_contract",
1546
+ "source": "restore-finalize",
1547
+ "sourceHandle": "accepted_contract",
1548
+ "target": "out-accepted_contract",
1549
+ "targetHandle": "value"
1550
+ },
1551
+ {
1552
+ "id": "restored-original_record",
1553
+ "source": "restore-finalize",
1554
+ "sourceHandle": "original_record",
1555
+ "target": "out-original_record",
1556
+ "targetHandle": "value"
1557
+ },
1558
+ {
1559
+ "id": "restored-revised_contract",
1560
+ "source": "restore-finalize",
1561
+ "sourceHandle": "revised_contract",
1562
+ "target": "out-revised_contract",
1563
+ "targetHandle": "value"
1564
+ },
1565
+ {
1566
+ "id": "restored-phase",
1567
+ "source": "restore-finalize",
1568
+ "sourceHandle": "phase",
1569
+ "target": "out-phase",
1570
+ "targetHandle": "value"
1571
+ },
1572
+ {
1573
+ "id": "record-to-validate-original-source",
1574
+ "source": "in-record_file",
1575
+ "sourceHandle": "output",
1576
+ "target": "validate-original-source",
1577
+ "targetHandle": "record_file"
1578
+ },
1579
+ {
1580
+ "id": "gate-to-validate-original-source",
1581
+ "source": "validate-record",
1582
+ "sourceHandle": "gate",
1583
+ "target": "validate-original-source",
1584
+ "targetHandle": "gate"
1585
+ },
1586
+ {
1587
+ "id": "record-to-validate-original-portrait",
1588
+ "source": "in-record_file",
1589
+ "sourceHandle": "output",
1590
+ "target": "validate-original-portrait",
1591
+ "targetHandle": "record_file"
1592
+ },
1593
+ {
1594
+ "id": "gate-to-validate-original-portrait",
1595
+ "source": "validate-original-source",
1596
+ "sourceHandle": "gate",
1597
+ "target": "validate-original-portrait",
1598
+ "targetHandle": "gate"
1599
+ },
1600
+ {
1601
+ "id": "record-to-validate-original-story",
1602
+ "source": "in-record_file",
1603
+ "sourceHandle": "output",
1604
+ "target": "validate-original-story",
1605
+ "targetHandle": "record_file"
1606
+ },
1607
+ {
1608
+ "id": "gate-to-validate-original-story",
1609
+ "source": "validate-original-portrait",
1610
+ "sourceHandle": "gate",
1611
+ "target": "validate-original-story",
1612
+ "targetHandle": "gate"
1613
+ },
1614
+ {
1615
+ "id": "record-to-validate-original-portrait-svg",
1616
+ "source": "in-record_file",
1617
+ "sourceHandle": "output",
1618
+ "target": "validate-original-portrait-svg",
1619
+ "targetHandle": "record_file"
1620
+ },
1621
+ {
1622
+ "id": "gate-to-validate-original-portrait-svg",
1623
+ "source": "validate-original-story",
1624
+ "sourceHandle": "gate",
1625
+ "target": "validate-original-portrait-svg",
1626
+ "targetHandle": "gate"
1627
+ },
1628
+ {
1629
+ "id": "record-to-validate-original-story-svg",
1630
+ "source": "in-record_file",
1631
+ "sourceHandle": "output",
1632
+ "target": "validate-original-story-svg",
1633
+ "targetHandle": "record_file"
1634
+ },
1635
+ {
1636
+ "id": "gate-to-validate-original-story-svg",
1637
+ "source": "validate-original-portrait-svg",
1638
+ "sourceHandle": "gate",
1639
+ "target": "validate-original-story-svg",
1640
+ "targetHandle": "gate"
1641
+ },
1642
+ {
1643
+ "id": "record-to-validate-revision-source",
1644
+ "source": "in-record_file",
1645
+ "sourceHandle": "output",
1646
+ "target": "validate-revision-source",
1647
+ "targetHandle": "record_file"
1648
+ },
1649
+ {
1650
+ "id": "gate-to-validate-revision-source",
1651
+ "source": "validate-original-story-svg",
1652
+ "sourceHandle": "gate",
1653
+ "target": "validate-revision-source",
1654
+ "targetHandle": "gate"
1655
+ },
1656
+ {
1657
+ "id": "record-to-validate-revision-portrait",
1658
+ "source": "in-record_file",
1659
+ "sourceHandle": "output",
1660
+ "target": "validate-revision-portrait",
1661
+ "targetHandle": "record_file"
1662
+ },
1663
+ {
1664
+ "id": "gate-to-validate-revision-portrait",
1665
+ "source": "validate-revision-source",
1666
+ "sourceHandle": "gate",
1667
+ "target": "validate-revision-portrait",
1668
+ "targetHandle": "gate"
1669
+ },
1670
+ {
1671
+ "id": "record-to-validate-revision-story",
1672
+ "source": "in-record_file",
1673
+ "sourceHandle": "output",
1674
+ "target": "validate-revision-story",
1675
+ "targetHandle": "record_file"
1676
+ },
1677
+ {
1678
+ "id": "gate-to-validate-revision-story",
1679
+ "source": "validate-revision-portrait",
1680
+ "sourceHandle": "gate",
1681
+ "target": "validate-revision-story",
1682
+ "targetHandle": "gate"
1683
+ },
1684
+ {
1685
+ "id": "record-to-validate-revision-portrait-svg",
1686
+ "source": "in-record_file",
1687
+ "sourceHandle": "output",
1688
+ "target": "validate-revision-portrait-svg",
1689
+ "targetHandle": "record_file"
1690
+ },
1691
+ {
1692
+ "id": "gate-to-validate-revision-portrait-svg",
1693
+ "source": "validate-revision-story",
1694
+ "sourceHandle": "gate",
1695
+ "target": "validate-revision-portrait-svg",
1696
+ "targetHandle": "gate"
1697
+ },
1698
+ {
1699
+ "id": "record-to-validate-revision-story-svg",
1700
+ "source": "in-record_file",
1701
+ "sourceHandle": "output",
1702
+ "target": "validate-revision-story-svg",
1703
+ "targetHandle": "record_file"
1704
+ },
1705
+ {
1706
+ "id": "gate-to-validate-revision-story-svg",
1707
+ "source": "validate-revision-portrait-svg",
1708
+ "sourceHandle": "gate",
1709
+ "target": "validate-revision-story-svg",
1710
+ "targetHandle": "gate"
1711
+ },
1712
+ {
1713
+ "id": "gate-to-restore-original-source",
1714
+ "source": "validate-revision-story-svg",
1715
+ "sourceHandle": "gate",
1716
+ "target": "restore-original-source",
1717
+ "targetHandle": "gate"
1718
+ },
1719
+ {
1720
+ "id": "record-file-to-release",
1721
+ "source": "in-record_file",
1722
+ "sourceHandle": "output",
1723
+ "target": "restore-record-file",
1724
+ "targetHandle": "value"
1725
+ },
1726
+ {
1727
+ "id": "ready-to-release",
1728
+ "source": "restore-finalize",
1729
+ "sourceHandle": "ready",
1730
+ "target": "restore-record-file",
1731
+ "targetHandle": "condition"
1732
+ },
1733
+ {
1734
+ "id": "restored-record_file",
1735
+ "source": "restore-record-file",
1736
+ "sourceHandle": "if_true",
1737
+ "target": "out-record_file",
1738
+ "targetHandle": "value"
1739
+ }
1740
+ ]
1741
+ },
1742
+ "input_schema": null,
1743
+ "output_schema": null,
1744
+ "settings": null,
1745
+ "package_name": "nodetool-base",
1746
+ "path": null,
1747
+ "run_mode": null
1748
+ }