fundus 0.0.2 → 0.0.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 (303) hide show
  1. package/README.md +73 -0
  2. package/build/client/_app/immutable/assets/0.DLhIsYA_.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.DLhIsYA_.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/2.ohi-TClG.css +1 -0
  5. package/build/client/_app/immutable/assets/2.ohi-TClG.css.br +0 -0
  6. package/build/client/_app/immutable/assets/2.ohi-TClG.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/3.JwgtcTEd.css +1 -0
  8. package/build/client/_app/immutable/assets/3.JwgtcTEd.css.br +0 -0
  9. package/build/client/_app/immutable/assets/3.JwgtcTEd.css.gz +0 -0
  10. package/build/client/_app/immutable/assets/4.p004hmEN.css +1 -0
  11. package/build/client/_app/immutable/assets/4.p004hmEN.css.br +0 -0
  12. package/build/client/_app/immutable/assets/4.p004hmEN.css.gz +0 -0
  13. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css +1 -0
  14. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.br +0 -0
  15. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.gz +0 -0
  16. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css +1 -0
  17. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.br +0 -0
  18. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.gz +0 -0
  19. package/build/client/_app/immutable/chunks/Bjy-W4x2.js.br +0 -0
  20. package/build/client/_app/immutable/chunks/Bjy-W4x2.js.gz +0 -0
  21. package/build/client/_app/immutable/chunks/CNRz3sSw.js.br +0 -0
  22. package/build/client/_app/immutable/chunks/CNRz3sSw.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/CO4RBJfa.js.br +0 -0
  24. package/build/client/_app/immutable/chunks/CO4RBJfa.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/DOpBpjxS.js +108 -0
  26. package/build/client/_app/immutable/chunks/DOpBpjxS.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/DOpBpjxS.js.gz +0 -0
  28. package/{dist/editor/_app/immutable/chunks/BfPE5wdt.js → build/client/_app/immutable/chunks/DVEQFclp.js} +1 -1
  29. package/build/client/_app/immutable/chunks/DVEQFclp.js.br +1 -0
  30. package/build/client/_app/immutable/chunks/DVEQFclp.js.gz +0 -0
  31. package/{dist/editor/_app/immutable/chunks/DjvOiDmq.js → build/client/_app/immutable/chunks/DiFoWL2c.js} +1 -1
  32. package/build/client/_app/immutable/chunks/DiFoWL2c.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/DiFoWL2c.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/DkADHpZF.js +1 -0
  35. package/build/client/_app/immutable/chunks/DkADHpZF.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/DkADHpZF.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/xihTtKlq.js.br +1 -0
  38. package/build/client/_app/immutable/chunks/xihTtKlq.js.gz +0 -0
  39. package/{dist/editor/_app/immutable/entry/app.BkqbfI8n.js → build/client/_app/immutable/entry/app.CKgtYXQb.js} +2 -2
  40. package/build/client/_app/immutable/entry/app.CKgtYXQb.js.br +0 -0
  41. package/build/client/_app/immutable/entry/app.CKgtYXQb.js.gz +0 -0
  42. package/build/client/_app/immutable/entry/start.CYZu5qE1.js +1 -0
  43. package/build/client/_app/immutable/entry/start.CYZu5qE1.js.br +0 -0
  44. package/build/client/_app/immutable/entry/start.CYZu5qE1.js.gz +0 -0
  45. package/{dist/editor/_app/immutable/nodes/0.xdMI5GEU.js → build/client/_app/immutable/nodes/0.I_1SApYQ.js} +1 -1
  46. package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.br +0 -0
  47. package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.gz +0 -0
  48. package/{dist/editor/_app/immutable/nodes/1.D9p2tfoJ.js → build/client/_app/immutable/nodes/1.v6eUTnWq.js} +1 -1
  49. package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.br +2 -0
  50. package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.gz +0 -0
  51. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js +3 -0
  52. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.br +0 -0
  53. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.gz +0 -0
  54. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js +1 -0
  55. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.br +0 -0
  56. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.gz +0 -0
  57. package/build/client/_app/immutable/nodes/4.CALOuSyz.js +2 -0
  58. package/build/client/_app/immutable/nodes/4.CALOuSyz.js.br +0 -0
  59. package/build/client/_app/immutable/nodes/4.CALOuSyz.js.gz +0 -0
  60. package/build/client/_app/version.json +1 -0
  61. package/build/client/_app/version.json.br +0 -0
  62. package/build/client/_app/version.json.gz +0 -0
  63. package/build/env.js +100 -0
  64. package/build/env.js.map +1 -0
  65. package/build/handler.js +22 -0
  66. package/build/handler.js.map +1 -0
  67. package/build/index.js +145 -0
  68. package/build/index.js.map +1 -0
  69. package/build/server/chunks/chunks/context.js-BUZqFfWG.js +429 -0
  70. package/build/server/chunks/chunks/context.js-BUZqFfWG.js.map +1 -0
  71. package/build/server/chunks/chunks/core.js-xfrkaQpS.js +458 -0
  72. package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +1 -0
  73. package/build/server/chunks/chunks/env.js-l0sNRNKZ.js +2 -0
  74. package/build/server/chunks/chunks/env.js-l0sNRNKZ.js.map +1 -0
  75. package/build/server/chunks/chunks/exports.js-C0rwMMtk.js +152 -0
  76. package/build/server/chunks/chunks/exports.js-C0rwMMtk.js.map +1 -0
  77. package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js +64 -0
  78. package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js.map +1 -0
  79. package/build/server/chunks/chunks/http.js-DvopzwKh.js +100 -0
  80. package/build/server/chunks/chunks/http.js-DvopzwKh.js.map +1 -0
  81. package/build/server/chunks/chunks/internal.js-DGo2jPcg.js +5753 -0
  82. package/build/server/chunks/chunks/internal.js-DGo2jPcg.js.map +1 -0
  83. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js +22 -0
  84. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -0
  85. package/{dist/operations-DYM-5KVl.js → build/server/chunks/chunks/library-store.js-ZLudCJlT.js} +463 -492
  86. package/build/server/chunks/chunks/library-store.js-ZLudCJlT.js.map +1 -0
  87. package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js +146 -0
  88. package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js.map +1 -0
  89. package/build/server/chunks/chunks/routing.js-y-tryuip.js +213 -0
  90. package/build/server/chunks/chunks/routing.js-y-tryuip.js.map +1 -0
  91. package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js +255 -0
  92. package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js.map +1 -0
  93. package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js +1048 -0
  94. package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js.map +1 -0
  95. package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js +27 -0
  96. package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js.map +1 -0
  97. package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js +24 -0
  98. package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js.map +1 -0
  99. package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js +45 -0
  100. package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js.map +1 -0
  101. package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js +31 -0
  102. package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js.map +1 -0
  103. package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js +31 -0
  104. package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js.map +1 -0
  105. package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js +38 -0
  106. package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js.map +1 -0
  107. package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js +31 -0
  108. package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js.map +1 -0
  109. package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js +24 -0
  110. package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js.map +1 -0
  111. package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js +31 -0
  112. package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js.map +1 -0
  113. package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js +24 -0
  114. package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js.map +1 -0
  115. package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js +27 -0
  116. package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js.map +1 -0
  117. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js +24 -0
  118. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js.map +1 -0
  119. package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js +24 -0
  120. package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js.map +1 -0
  121. package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js +23 -0
  122. package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js.map +1 -0
  123. package/build/server/chunks/handler-DnbS2t1B.js +1475 -0
  124. package/build/server/chunks/handler-DnbS2t1B.js.map +1 -0
  125. package/build/server/chunks/index.js-g77q3Boj.js +3789 -0
  126. package/build/server/chunks/index.js-g77q3Boj.js.map +1 -0
  127. package/build/server/chunks/manifest.js-qmEXbstS.js +149 -0
  128. package/build/server/chunks/manifest.js-qmEXbstS.js.map +1 -0
  129. package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js +11 -0
  130. package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js.map +1 -0
  131. package/build/server/chunks/nodes/1.js-H-qwo4GJ.js +7 -0
  132. package/build/server/chunks/nodes/1.js-H-qwo4GJ.js.map +1 -0
  133. package/build/server/chunks/nodes/2.js-DRpXVLwp.js +7 -0
  134. package/build/server/chunks/nodes/2.js-DRpXVLwp.js.map +1 -0
  135. package/build/server/chunks/nodes/3.js-CDEzNFeU.js +7 -0
  136. package/build/server/chunks/nodes/3.js-CDEzNFeU.js.map +1 -0
  137. package/build/server/chunks/nodes/4.js-BKL7LSSm.js +7 -0
  138. package/build/server/chunks/nodes/4.js-BKL7LSSm.js.map +1 -0
  139. package/build/shims.js +33 -0
  140. package/build/shims.js.map +1 -0
  141. package/dist/cli.d.ts +2 -1
  142. package/dist/cli.js +116 -1336
  143. package/dist/config.d.ts +3 -2
  144. package/dist/config.js +2 -2
  145. package/dist/core/api-types.d.ts +141 -0
  146. package/dist/core/api-types.js +1 -0
  147. package/dist/core/asset-id.d.ts +28 -0
  148. package/dist/core/asset-id.js +59 -0
  149. package/dist/core/asset-reference.d.ts +23 -0
  150. package/dist/core/asset-reference.js +15 -0
  151. package/dist/core/canonicalize.d.ts +9 -0
  152. package/dist/core/canonicalize.js +36 -0
  153. package/dist/core/chroma-key.d.ts +30 -0
  154. package/dist/core/chroma-key.js +55 -0
  155. package/dist/core/config.d.ts +35 -0
  156. package/dist/core/config.js +4 -0
  157. package/dist/core/folder-path.d.ts +19 -0
  158. package/dist/core/folder-path.js +86 -0
  159. package/dist/core/index.d.ts +33 -499
  160. package/dist/core/index.js +17 -962
  161. package/dist/core/ingest.d.ts +24 -0
  162. package/dist/core/ingest.js +42 -0
  163. package/dist/core/library.d.ts +54 -0
  164. package/dist/core/library.js +138 -0
  165. package/dist/core/manifest-appearance.d.ts +35 -0
  166. package/dist/core/manifest-appearance.js +123 -0
  167. package/dist/core/manifest-entry.d.ts +104 -0
  168. package/dist/core/manifest-entry.js +1 -0
  169. package/dist/core/manifest-membership.d.ts +56 -0
  170. package/dist/core/manifest-membership.js +54 -0
  171. package/dist/core/plugins/audio.d.ts +26 -0
  172. package/dist/core/plugins/audio.js +44 -0
  173. package/dist/core/plugins/chroma-key-video.d.ts +29 -0
  174. package/dist/core/plugins/chroma-key-video.js +69 -0
  175. package/dist/core/plugins/contract.d.ts +35 -0
  176. package/dist/core/plugins/contract.js +1 -0
  177. package/dist/core/plugins/image.d.ts +30 -0
  178. package/dist/core/plugins/image.js +45 -0
  179. package/dist/core/plugins/registry.d.ts +16 -0
  180. package/dist/core/plugins/registry.js +52 -0
  181. package/dist/core/plugins/slice.d.ts +22 -0
  182. package/dist/core/plugins/slice.js +60 -0
  183. package/dist/core/plugins/video.d.ts +45 -0
  184. package/dist/core/plugins/video.js +77 -0
  185. package/dist/core/reference-graph.d.ts +41 -0
  186. package/dist/core/reference-graph.js +138 -0
  187. package/dist/core/slice-data.d.ts +61 -0
  188. package/dist/core/slice-data.js +49 -0
  189. package/dist/runtime/chroma-key-gl.d.ts +40 -22
  190. package/dist/runtime/chroma-key-gl.js +189 -183
  191. package/dist/runtime/components/ChromaKeyVideo.svelte +3 -3
  192. package/dist/runtime/components/ChromaKeyVideo.svelte.d.ts +21 -0
  193. package/dist/runtime/components/Image.svelte +1 -1
  194. package/dist/runtime/components/Image.svelte.d.ts +9 -0
  195. package/dist/runtime/components/Slice.svelte +1 -1
  196. package/dist/runtime/components/Slice.svelte.d.ts +19 -0
  197. package/dist/runtime/components/SliceCanvas.svelte +3 -3
  198. package/dist/runtime/components/SliceCanvas.svelte.d.ts +10 -0
  199. package/dist/runtime/components/SliceDom.svelte +3 -3
  200. package/dist/runtime/components/SliceDom.svelte.d.ts +10 -0
  201. package/dist/runtime/components/Video.svelte +2 -2
  202. package/dist/runtime/components/Video.svelte.d.ts +18 -0
  203. package/dist/runtime/geometry.d.ts +55 -35
  204. package/dist/runtime/geometry.js +186 -191
  205. package/dist/runtime/index.d.ts +9 -9
  206. package/dist/runtime/index.js +9 -9
  207. package/dist/runtime/kind-handlers.d.ts +33 -30
  208. package/dist/runtime/kind-handlers.js +85 -67
  209. package/dist/runtime/manifest.d.ts +53 -47
  210. package/dist/runtime/manifest.js +133 -113
  211. package/dist/runtime/observe-border-box-size.d.ts +1 -4
  212. package/dist/runtime/observe-border-box-size.js +9 -10
  213. package/dist/runtime/preload-registry.d.ts +22 -8
  214. package/dist/runtime/preload-registry.js +65 -62
  215. package/dist/server/build.d.ts +8 -0
  216. package/dist/server/build.js +25 -0
  217. package/dist/server/check.d.ts +24 -0
  218. package/dist/server/check.js +199 -0
  219. package/dist/server/cli-commands.d.ts +25 -0
  220. package/dist/server/cli-commands.js +1178 -0
  221. package/dist/server/cli-init.d.ts +3 -0
  222. package/dist/server/cli-init.js +53 -0
  223. package/dist/server/cli-io.d.ts +46 -0
  224. package/dist/server/cli-io.js +105 -0
  225. package/dist/server/codegen.d.ts +58 -0
  226. package/dist/server/codegen.js +185 -0
  227. package/dist/server/config-loader.d.ts +35 -0
  228. package/dist/server/config-loader.js +79 -0
  229. package/dist/server/context.d.ts +8 -0
  230. package/dist/server/context.js +19 -0
  231. package/dist/server/folder-filesystem.d.ts +7 -0
  232. package/dist/server/folder-filesystem.js +39 -0
  233. package/dist/server/http.d.ts +12 -0
  234. package/dist/server/http.js +100 -0
  235. package/dist/server/index.d.ts +17 -0
  236. package/dist/server/index.js +9 -0
  237. package/dist/server/job-hash.d.ts +10 -0
  238. package/dist/server/job-hash.js +23 -0
  239. package/dist/server/library-store.d.ts +82 -0
  240. package/dist/server/library-store.js +763 -0
  241. package/dist/server/operations.d.ts +87 -0
  242. package/dist/server/operations.js +427 -0
  243. package/dist/server/plugins/audio.d.ts +8 -0
  244. package/dist/server/plugins/audio.js +41 -0
  245. package/dist/server/plugins/chroma-key-video.d.ts +9 -0
  246. package/dist/server/plugins/chroma-key-video.js +40 -0
  247. package/dist/server/plugins/config.d.ts +13 -0
  248. package/dist/server/plugins/config.js +32 -0
  249. package/dist/server/plugins/contract.d.ts +114 -0
  250. package/dist/server/plugins/contract.js +16 -0
  251. package/dist/server/plugins/image.d.ts +9 -0
  252. package/dist/server/plugins/image.js +43 -0
  253. package/dist/server/plugins/slice.d.ts +15 -0
  254. package/dist/server/plugins/slice.js +76 -0
  255. package/dist/server/plugins/video.d.ts +7 -0
  256. package/dist/server/plugins/video.js +54 -0
  257. package/dist/server/plugins.d.ts +23 -0
  258. package/dist/server/plugins.js +35 -0
  259. package/dist/server/processing/audio-processing.d.ts +17 -0
  260. package/dist/server/processing/audio-processing.js +46 -0
  261. package/dist/server/processing/ffmpeg.d.ts +9 -0
  262. package/dist/server/processing/ffmpeg.js +50 -0
  263. package/dist/server/processing/image-processing.d.ts +16 -0
  264. package/dist/server/processing/image-processing.js +26 -0
  265. package/dist/server/processing/media-probe.d.ts +15 -0
  266. package/dist/server/processing/media-probe.js +27 -0
  267. package/dist/server/processing/processor.d.ts +50 -0
  268. package/dist/server/processing/processor.js +158 -0
  269. package/dist/server/processing/video-processing.d.ts +35 -0
  270. package/dist/server/processing/video-processing.js +87 -0
  271. package/dist/server/server.d.ts +13 -0
  272. package/dist/server/server.js +57 -0
  273. package/dist/server/start.d.ts +13 -0
  274. package/dist/server/start.js +80 -0
  275. package/package.json +31 -16
  276. package/dist/build-CZYFLLvl.js +0 -191
  277. package/dist/core/image-Dqmz8FXS.js +0 -54
  278. package/dist/core/reference-graph-CjuHbLDw.d.ts +0 -280
  279. package/dist/core/testing.d.ts +0 -27
  280. package/dist/core/testing.js +0 -80
  281. package/dist/editor/200.html +0 -37
  282. package/dist/editor/_app/immutable/assets/2.DIbbW7U1.css +0 -1
  283. package/dist/editor/_app/immutable/assets/3.C-9vFQ9A.css +0 -1
  284. package/dist/editor/_app/immutable/assets/4.BS8ElpOV.css +0 -1
  285. package/dist/editor/_app/immutable/assets/ConfirmDialog.CHEISE-L.css +0 -1
  286. package/dist/editor/_app/immutable/assets/LibraryContextMenu.CDjCmdV-.css +0 -1
  287. package/dist/editor/_app/immutable/chunks/Cx6qtxJg.js +0 -1
  288. package/dist/editor/_app/immutable/chunks/DjdrdRzT.js +0 -1
  289. package/dist/editor/_app/immutable/chunks/n7mvYc42.js +0 -5
  290. package/dist/editor/_app/immutable/entry/start.C8HHOLly.js +0 -1
  291. package/dist/editor/_app/immutable/nodes/2.BrA0Hyzu.js +0 -3
  292. package/dist/editor/_app/immutable/nodes/3.WO7tZH_d.js +0 -104
  293. package/dist/editor/_app/immutable/nodes/4.DQtx17aW.js +0 -2
  294. package/dist/editor/_app/version.json +0 -1
  295. package/dist/index.d.ts +0 -370
  296. package/dist/index.js +0 -4
  297. package/dist/start-Bhnqt9Dc.js +0 -279
  298. package/dist/start-D2IXOrni.js +0 -2
  299. /package/{dist/editor → build/client}/_app/immutable/assets/0.DLhIsYA_.css +0 -0
  300. /package/{dist/editor → build/client}/_app/immutable/chunks/Bjy-W4x2.js +0 -0
  301. /package/{dist/editor → build/client}/_app/immutable/chunks/CNRz3sSw.js +0 -0
  302. /package/{dist/editor → build/client}/_app/immutable/chunks/CO4RBJfa.js +0 -0
  303. /package/{dist/editor → build/client}/_app/immutable/chunks/xihTtKlq.js +0 -0
@@ -1,87 +1,330 @@
1
- import { mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
2
- import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
3
- import { AUDIO_BUILT_IN_PRESETS, FOLDER_SENTINEL, IMAGE_BUILT_IN_PRESETS, SLICE_BUILT_IN_PRESETS, VIDEO_BUILT_IN_PRESETS, allDeliveredAssetIds, assetIdError, audioPlugin, canonicalizeParameters, chromaKeyVideoPlugin, collectAllReferences, compareCodeUnits, computeLibraryMembership, createEmptyLibrary, defaultInsetsForSize, folderNameError, folderNameOf, folderOfRawPath, folderPathError, imagePlugin, isAssetReference, isFolderOrDescendant, joinFolderPath, manifestColorError, manifestEmojiError, manifestNameError, normalizeFolderPath, parentFolderPath, parseLibrary, portableFolderPathKey, rawFileNameOfPath, referenceErrors, replaceFolderPrefix, serializeLibrary, slicePlugin, topologicalReferenceOrder, validateAudioPreset, validateImagePreset, validateVideoPreset, videoPlugin } from "#fundus/core";
4
- import { existsSync } from "node:fs";
5
- import { createJiti } from "jiti";
6
- import { createHash } from "node:crypto";
7
- import ffmpegStaticPath from "ffmpeg-static";
8
- import { path } from "ffprobe-static";
9
- import { execFile } from "node:child_process";
10
- import { promisify } from "node:util";
11
- import sharp from "sharp";
12
- //#region src/config-loader.ts
1
+ import { p as plugins, a as audioPlugin, c as chromaKeyVideoPlugin, v as videoPlugin, s as slicePlugin, i as imagePlugin, b as assetIdError, d as defaultInsetsForSize, m as manifestNameError, e as isAssetReference, f as validateVideoPreset, V as VIDEO_BUILT_IN_PRESETS, g as validateAudioPreset, A as AUDIO_BUILT_IN_PRESETS, h as validateImagePreset, I as IMAGE_BUILT_IN_PRESETS, S as SLICE_BUILT_IN_PRESETS } from './core.js-xfrkaQpS.js';
2
+ import { n as normalizeFolderPath, f as folderOfRawPath, r as rawFileNameOfPath, a as folderPathError, p as parentFolderPath, b as folderNameError, j as joinFolderPath, c as folderNameOf, i as isFolderOrDescendant, d as replaceFolderPrefix, e as portableFolderPathKey, F as FOLDER_SENTINEL } from './folder-path.js-ytx9ucgg.js';
3
+ import { m as manifestColorError, a as manifestEmojiError } from './manifest-appearance.js-DTk355LQ.js';
4
+ import { join, extname, dirname, basename, isAbsolute, resolve } from 'node:path';
5
+ import { existsSync } from 'node:fs';
6
+ import { readFile, mkdir, writeFile, rename, rm, unlink, stat, readdir } from 'node:fs/promises';
7
+ import ffmpegStaticPath from 'ffmpeg-static';
8
+ import { path } from 'ffprobe-static';
9
+ import { execFile } from 'node:child_process';
10
+ import { promisify } from 'node:util';
11
+ import sharp from 'sharp';
12
+ import { createHash } from 'node:crypto';
13
+
14
+ //#region src/lib/core/library.ts
15
+ /** A fresh, empty library. */
16
+ function createEmptyLibrary() {
17
+ return {
18
+ version: 1,
19
+ folders: [],
20
+ assets: {},
21
+ manifests: {}
22
+ };
23
+ }
24
+ function isRecord$1(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
13
27
  /**
14
- * Loads and resolves the host's `fundus.config.ts` through jiti, validates
15
- * the shape, resolves all paths to
16
- * absolute, and merges the built-in plugin preset defaults under the host's.
28
+ * Keys that are prototype-pollution hazards as record keys: assigning
29
+ * `assets["__proto__"] = …` on a plain object replaces its prototype instead
30
+ * of adding an entry. Rejected outright at parse time id *validity* beyond
31
+ * this is a validator concern (reported as issues, not parse failures).
17
32
  */
18
- function isRecord(value) {
19
- return typeof value === "object" && value !== null && !Array.isArray(value);
33
+ var DANGEROUS_KEYS = [
34
+ "__proto__",
35
+ "constructor",
36
+ "prototype"
37
+ ];
38
+ function assertSafeKey(kind, key) {
39
+ if (DANGEROUS_KEYS.includes(key)) throw new Error(`Library ${kind} key "${key}" is not allowed.`);
40
+ }
41
+ function parseAssetRecord(id, value) {
42
+ if (!isRecord$1(value)) throw new Error(`Library asset "${id}" must be an object.`);
43
+ if (typeof value.type !== "string" || value.type.length === 0) throw new Error(`Library asset "${id}" is missing a string "type".`);
44
+ if (typeof value.raw !== "string" || value.raw.length === 0) throw new Error(`Library asset "${id}" is missing a string "raw" file name.`);
45
+ if (typeof value.rawHash !== "string" || value.rawHash.length === 0) throw new Error(`Library asset "${id}" is missing a string "rawHash".`);
46
+ if (!("parameters" in value)) throw new Error(`Library asset "${id}" is missing "parameters".`);
47
+ if (!Array.isArray(value.manifests) || value.manifests.some((entry) => typeof entry !== "string")) throw new Error(`Library asset "${id}" must have a "manifests" array of strings.`);
48
+ return {
49
+ type: value.type,
50
+ raw: value.raw,
51
+ rawHash: value.rawHash,
52
+ parameters: value.parameters,
53
+ manifests: value.manifests
54
+ };
20
55
  }
21
- function requiredString(config, key) {
22
- const value = config[key];
23
- if (typeof value !== "string" || value.length === 0) throw new Error(`fundus.config: "${key}" must be a non-empty string.`);
56
+ function parseManifestRecord(name, value) {
57
+ const record = {};
58
+ if ("color" in value && value.color !== void 0) {
59
+ if (typeof value.color !== "string") throw new Error(`Library manifest "${name}" has a non-string "color".`);
60
+ record.color = value.color;
61
+ }
62
+ if ("emoji" in value && value.emoji !== void 0) {
63
+ if (typeof value.emoji !== "string") throw new Error(`Library manifest "${name}" has a non-string "emoji".`);
64
+ record.emoji = value.emoji;
65
+ }
66
+ return record;
67
+ }
68
+ /**
69
+ * Parse and structurally validate a library file's JSON text. Throws with a
70
+ * clear message on any malformed part; plugin parameter validation is a
71
+ * separate, plugin-owned step.
72
+ */
73
+ function parseLibrary(json) {
74
+ let value;
75
+ try {
76
+ value = JSON.parse(json);
77
+ } catch (cause) {
78
+ throw new Error(`Library file is not valid JSON: ${cause instanceof Error ? cause.message : String(cause)}`);
79
+ }
80
+ if (!isRecord$1(value)) throw new Error("Library file must contain a JSON object.");
81
+ if (value.version !== 1) throw new Error(`Unsupported library version ${JSON.stringify(value.version)} (expected 1).`);
82
+ if (!isRecord$1(value.assets)) throw new Error("Library \"assets\" must be an object.");
83
+ if (!isRecord$1(value.manifests)) throw new Error("Library \"manifests\" must be an object.");
84
+ if (value.folders !== void 0 && (!Array.isArray(value.folders) || value.folders.some((entry) => typeof entry !== "string"))) throw new Error("Library \"folders\" must be an array of strings.");
85
+ const folders = value.folders === void 0 ? [] : value.folders;
86
+ for (const folder of folders) {
87
+ const problem = folderPathError(folder);
88
+ if (problem) throw new Error(`Invalid library folder: ${problem}`);
89
+ }
90
+ const assets = {};
91
+ for (const [id, record] of Object.entries(value.assets)) {
92
+ assertSafeKey("asset", id);
93
+ assets[id] = parseAssetRecord(id, record);
94
+ }
95
+ const manifests = {};
96
+ for (const [name, record] of Object.entries(value.manifests)) {
97
+ assertSafeKey("manifest", name);
98
+ if (!isRecord$1(record)) throw new Error(`Library manifest "${name}" must be an object.`);
99
+ manifests[name] = parseManifestRecord(name, record);
100
+ }
101
+ return {
102
+ version: 1,
103
+ folders,
104
+ assets,
105
+ manifests
106
+ };
107
+ }
108
+ /** Recursively sort object keys so serialization is order-independent. */
109
+ function sortValue(value) {
110
+ if (Array.isArray(value)) return value.map(sortValue);
111
+ if (typeof value === "object" && value !== null) {
112
+ const sorted = {};
113
+ for (const key of Object.keys(value).sort()) {
114
+ const entry = value[key];
115
+ if (entry !== void 0) sorted[key] = sortValue(entry);
116
+ }
117
+ return sorted;
118
+ }
24
119
  return value;
25
120
  }
26
- function resolvePath(projectRoot, value) {
27
- return isAbsolute(value) ? value : resolve(projectRoot, value);
121
+ /**
122
+ * Serialize a library deterministically: recursively sorted keys, tab-indented,
123
+ * trailing newline. Same library → byte-identical file, whatever the in-memory
124
+ * insertion order was.
125
+ */
126
+ function serializeLibrary(library) {
127
+ return `${JSON.stringify(sortValue({
128
+ ...library,
129
+ folders: [...library.folders].sort()
130
+ }), null, " ")}\n`;
28
131
  }
29
- function mergePresets(label, builtIn, pluginConfig, validatePreset) {
30
- const merged = { ...builtIn };
31
- if (pluginConfig === void 0) return merged;
32
- if (!isRecord(pluginConfig)) throw new Error(`fundus.config: "plugins.${label}" must be an object.`);
33
- if (pluginConfig.presets === void 0) return merged;
34
- if (!isRecord(pluginConfig.presets)) throw new Error(`fundus.config: "plugins.${label}.presets" must be an object.`);
35
- for (const [name, preset] of Object.entries(pluginConfig.presets)) merged[name] = validatePreset(name, preset);
36
- return merged;
132
+ //#endregion
133
+ //#region src/lib/core/canonicalize.ts
134
+ /**
135
+ * Stable JSON for hash inputs: recursively sorted keys, no whitespace,
136
+ * `undefined` object values dropped. Two parameter objects that differ only in
137
+ * key order (or in explicitly-undefined optionals) canonicalize identically,
138
+ * so the derived job hash and thus the proxy file name — stays stable.
139
+ */
140
+ function canonicalizeParameters(parameters) {
141
+ return stringify(parameters);
37
142
  }
38
- /** `plugins.video.ffmpegPath` / `ffprobePath`: optional system-binary overrides. */
39
- function binaryPathOverride(projectRoot, pluginConfig, key) {
40
- if (!isRecord(pluginConfig) || pluginConfig[key] === void 0) return null;
41
- const value = pluginConfig[key];
42
- if (typeof value !== "string" || value.length === 0) throw new Error(`fundus.config: "plugins.video.${key}" must be a non-empty string.`);
43
- return resolvePath(projectRoot, value);
44
- }
45
- /** Pure resolution step (testable without a config file on disk). */
46
- function resolveConfig(projectRoot, value) {
47
- if (!isRecord(value)) throw new Error("fundus.config must default-export a configuration object.");
48
- const proxyBasePath = requiredString(value, "proxyBasePath");
49
- if (!proxyBasePath.endsWith("/")) throw new Error("fundus.config: \"proxyBasePath\" must end with \"/\".");
50
- const plugins = value.plugins === void 0 ? {} : value.plugins;
51
- if (!isRecord(plugins)) throw new Error("fundus.config: \"plugins\" must be an object.");
52
- return {
53
- rawDir: resolvePath(projectRoot, requiredString(value, "rawDir")),
54
- proxyDir: resolvePath(projectRoot, requiredString(value, "proxyDir")),
55
- library: resolvePath(projectRoot, requiredString(value, "library")),
56
- manifestsDir: resolvePath(projectRoot, requiredString(value, "manifestsDir")),
57
- proxyBasePath,
58
- plugins: {
59
- image: { presets: mergePresets("image", IMAGE_BUILT_IN_PRESETS, plugins.image, validateImagePreset) },
60
- slice: { presets: mergePresets("slice", SLICE_BUILT_IN_PRESETS, plugins.slice, validateImagePreset) },
61
- video: {
62
- presets: mergePresets("video", VIDEO_BUILT_IN_PRESETS, plugins.video, validateVideoPreset),
63
- ffmpegPath: binaryPathOverride(projectRoot, plugins.video, "ffmpegPath"),
64
- ffprobePath: binaryPathOverride(projectRoot, plugins.video, "ffprobePath")
65
- },
66
- audio: { presets: mergePresets("audio", AUDIO_BUILT_IN_PRESETS, plugins.audio, validateAudioPreset) }
143
+ /** Deterministic UTF-16 code-unit ordering, independent of host locale. */
144
+ function compareCodeUnits(left, right) {
145
+ return left < right ? -1 : left > right ? 1 : 0;
146
+ }
147
+ function stringify(value) {
148
+ if (value === void 0) return "null";
149
+ if (value === null || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
150
+ if (typeof value === "string") return JSON.stringify(value);
151
+ if (Array.isArray(value)) return `[${value.map(stringify).join(",")}]`;
152
+ if (typeof value === "object") {
153
+ const record = value;
154
+ const parts = [];
155
+ for (const key of Object.keys(record).sort()) {
156
+ if (record[key] === void 0) continue;
157
+ parts.push(`${JSON.stringify(key)}:${stringify(record[key])}`);
67
158
  }
68
- };
159
+ return `{${parts.join(",")}}`;
160
+ }
161
+ throw new Error(`Cannot canonicalize a ${typeof value} value.`);
69
162
  }
70
- /** Load `fundus.config.ts` from the host project root. */
71
- async function loadConfig(projectRoot) {
72
- const root = resolve(projectRoot);
73
- const configPath = resolve(root, "fundus.config.ts");
74
- if (!existsSync(configPath)) throw new Error(`No fundus.config.ts found in ${root}.`);
75
- let module;
76
- try {
77
- module = await createJiti(import.meta.url, { interopDefault: false }).import(configPath);
78
- } catch (cause) {
79
- throw new Error(`Cannot load fundus.config.ts: ${cause instanceof Error ? cause.message : String(cause)}`);
163
+ //#endregion
164
+ //#region src/lib/core/reference-graph.ts
165
+ /**
166
+ * Ask each asset's owning plugin for the references in its parameters. Assets
167
+ * whose type is unknown or whose parameters do not validate contribute no
168
+ * references — those problems are reported by the regular validation pass.
169
+ */
170
+ function collectAllReferences(library, plugins) {
171
+ const referencesByAsset = {};
172
+ for (const [assetId, record] of Object.entries(library.assets)) {
173
+ referencesByAsset[assetId] = [];
174
+ const plugin = plugins[record.type];
175
+ if (!plugin?.collectReferences) continue;
176
+ try {
177
+ referencesByAsset[assetId] = plugin.collectReferences(plugin.validateParameters(record.parameters));
178
+ } catch {}
179
+ }
180
+ return referencesByAsset;
181
+ }
182
+ /**
183
+ * Reference *errors* (they block build/codegen): a reference to an unknown
184
+ * asset, a target whose type is not among the field's expected types, and
185
+ * cycles (composition is acyclic by nature).
186
+ */
187
+ function referenceErrors(library, referencesByAsset) {
188
+ const issues = [];
189
+ for (const [assetId, references] of Object.entries(referencesByAsset)) for (const reference of references) {
190
+ if (!Object.hasOwn(library.assets, reference.assetId)) {
191
+ issues.push({
192
+ assetId,
193
+ message: `Reference "${reference.field}" points at unknown asset "${reference.assetId}".`
194
+ });
195
+ continue;
196
+ }
197
+ const targetType = library.assets[reference.assetId].type;
198
+ if (reference.expectedTypes.length > 0 && !reference.expectedTypes.includes(targetType)) issues.push({
199
+ assetId,
200
+ message: `Reference "${reference.field}" points at "${reference.assetId}" of type "${targetType}" — expected ${reference.expectedTypes.map((type) => `"${type}"`).join(" or ")}.`
201
+ });
202
+ }
203
+ issues.push(...cycleErrors(referencesByAsset));
204
+ return issues;
205
+ }
206
+ /** DFS cycle detection with an explicit stack path; each cycle reported once. */
207
+ function cycleErrors(referencesByAsset) {
208
+ const issues = [];
209
+ const finished = /* @__PURE__ */ new Set();
210
+ const reportedCycles = /* @__PURE__ */ new Set();
211
+ const stack = [];
212
+ const onStack = /* @__PURE__ */ new Set();
213
+ function visit(assetId) {
214
+ if (finished.has(assetId)) return;
215
+ if (onStack.has(assetId)) {
216
+ const cyclePath = [...stack.slice(stack.indexOf(assetId)), assetId];
217
+ const cycleKey = [...new Set(cyclePath)].sort().join("\0");
218
+ if (!reportedCycles.has(cycleKey)) {
219
+ reportedCycles.add(cycleKey);
220
+ issues.push({
221
+ assetId: cyclePath[0],
222
+ message: `Reference cycle: ${cyclePath.map((id) => `"${id}"`).join(" → ")} — references express composition and must stay acyclic.`
223
+ });
224
+ }
225
+ return;
226
+ }
227
+ stack.push(assetId);
228
+ onStack.add(assetId);
229
+ for (const reference of referencesByAsset[assetId] ?? []) if (Object.hasOwn(referencesByAsset, reference.assetId)) visit(reference.assetId);
230
+ stack.pop();
231
+ onStack.delete(assetId);
232
+ finished.add(assetId);
233
+ }
234
+ for (const assetId of Object.keys(referencesByAsset)) visit(assetId);
235
+ return issues;
236
+ }
237
+ /**
238
+ * The transitive reference closure of a set of assets, including the starting
239
+ * set — the assets that must be delivered so the starting set renders as
240
+ * authored. Tolerates cycles and ignores dangling targets (both are reported
241
+ * as errors by `referenceErrors`).
242
+ */
243
+ function referenceClosure(assetIds, referencesByAsset) {
244
+ const closure = /* @__PURE__ */ new Set();
245
+ const pending = [...assetIds];
246
+ while (pending.length > 0) {
247
+ const assetId = pending.pop();
248
+ if (closure.has(assetId) || !Object.hasOwn(referencesByAsset, assetId)) continue;
249
+ closure.add(assetId);
250
+ for (const reference of referencesByAsset[assetId]) pending.push(reference.assetId);
251
+ }
252
+ return closure;
253
+ }
254
+ /**
255
+ * A dependencies-first order over `assetIds` and everything they transitively
256
+ * reference — the build order that lets each entry inline its references from
257
+ * already-built entries. Throws on a cycle (with the cycle path).
258
+ */
259
+ function topologicalReferenceOrder(assetIds, referencesByAsset) {
260
+ const order = [];
261
+ const finished = /* @__PURE__ */ new Set();
262
+ const stack = [];
263
+ const onStack = /* @__PURE__ */ new Set();
264
+ function visit(assetId) {
265
+ if (finished.has(assetId) || !Object.hasOwn(referencesByAsset, assetId)) return;
266
+ if (onStack.has(assetId)) {
267
+ const cyclePath = [...stack.slice(stack.indexOf(assetId)), assetId];
268
+ throw new Error(`Reference cycle: ${cyclePath.map((id) => `"${id}"`).join(" → ")} — cannot order entries.`);
269
+ }
270
+ stack.push(assetId);
271
+ onStack.add(assetId);
272
+ for (const reference of referencesByAsset[assetId]) visit(reference.assetId);
273
+ stack.pop();
274
+ onStack.delete(assetId);
275
+ finished.add(assetId);
276
+ order.push(assetId);
277
+ }
278
+ for (const assetId of assetIds) visit(assetId);
279
+ return order;
280
+ }
281
+ //#endregion
282
+ //#region src/lib/core/manifest-membership.ts
283
+ /**
284
+ * Compute the full membership picture from a library and its collected
285
+ * references: for each manifest the explicit members, the passive members
286
+ * (`referenceClosure(explicit) − explicit`) with their via-attribution, and
287
+ * the resulting delivery set.
288
+ */
289
+ function computeLibraryMembership(library, referencesByAsset) {
290
+ const manifestNames = new Set(Object.keys(library.manifests));
291
+ for (const record of Object.values(library.assets)) for (const manifestName of record.manifests) manifestNames.add(manifestName);
292
+ const membership = {};
293
+ for (const manifestName of [...manifestNames].sort(compareCodeUnits)) {
294
+ const explicitMemberIds = Object.entries(library.assets).filter(([, record]) => record.manifests.includes(manifestName)).map(([assetId]) => assetId).sort(compareCodeUnits);
295
+ const explicitMemberSet = new Set(explicitMemberIds);
296
+ const deliveredAssetIds = new Set(explicitMemberIds);
297
+ const viaByPassiveAssetId = /* @__PURE__ */ new Map();
298
+ for (const explicitMemberId of explicitMemberIds) for (const reachedAssetId of referenceClosure([explicitMemberId], referencesByAsset)) {
299
+ deliveredAssetIds.add(reachedAssetId);
300
+ if (explicitMemberSet.has(reachedAssetId)) continue;
301
+ const via = viaByPassiveAssetId.get(reachedAssetId) ?? [];
302
+ via.push(explicitMemberId);
303
+ viaByPassiveAssetId.set(reachedAssetId, via);
304
+ }
305
+ membership[manifestName] = {
306
+ explicitMemberIds,
307
+ passiveMembers: [...viaByPassiveAssetId.entries()].map(([assetId, via]) => ({
308
+ assetId,
309
+ via
310
+ })).sort((a, b) => compareCodeUnits(a.assetId, b.assetId)),
311
+ deliveredAssetIds
312
+ };
80
313
  }
81
- return resolveConfig(root, isRecord(module) && "default" in module ? module.default : module);
314
+ return membership;
315
+ }
316
+ /**
317
+ * Every asset delivered by at least one manifest, explicitly or passively —
318
+ * the processing set (assets outside it are inert). An asset is
319
+ * *unreachable* (nothing delivers it) exactly when it is missing here.
320
+ */
321
+ function allDeliveredAssetIds(membership) {
322
+ const delivered = /* @__PURE__ */ new Set();
323
+ for (const manifestMembership of Object.values(membership)) for (const assetId of manifestMembership.deliveredAssetIds) delivered.add(assetId);
324
+ return delivered;
82
325
  }
83
326
  //#endregion
84
- //#region src/processing/ffmpeg.ts
327
+ //#region src/lib/server/processing/ffmpeg.ts
85
328
  /**
86
329
  * The ffmpeg/ffprobe toolchain layer: binary resolution (bundled `-static`
87
330
  * packages by default, with host-config overrides) and a spawn helper.
@@ -117,7 +360,7 @@ async function runToolchainBinary(binaryPath, commandArguments) {
117
360
  }
118
361
  }
119
362
  //#endregion
120
- //#region src/processing/media-probe.ts
363
+ //#region src/lib/server/processing/media-probe.ts
121
364
  /** Parse ffprobe's JSON output (pure, testable without a binary). */
122
365
  function parseProbeOutput(json, filePath) {
123
366
  const output = JSON.parse(json);
@@ -144,13 +387,13 @@ async function probeMedia(filePath, config) {
144
387
  ]), filePath);
145
388
  }
146
389
  //#endregion
147
- //#region src/processing/audio-processing.ts
390
+ //#region src/lib/server/processing/audio-processing.ts
148
391
  /**
149
392
  * The ffmpeg transcode step of the audio plugin: one audio-only proxy per
150
393
  * preset (opus/webm or aac/m4a). Deterministic like the video step:
151
394
  * metadata stripped, bitexact muxing.
152
395
  */
153
- const ENCODER_BY_CODEC = {
396
+ var ENCODER_BY_CODEC = {
154
397
  opus: "libopus",
155
398
  aac: "aac"
156
399
  };
@@ -187,7 +430,29 @@ async function processAudioFile(options) {
187
430
  return { durationMs: (await probeMedia(options.targetPath, options.config)).durationMs };
188
431
  }
189
432
  //#endregion
190
- //#region src/plugins/contract.ts
433
+ //#region src/lib/server/plugins/config.ts
434
+ function isRecord(value) {
435
+ return typeof value === "object" && value !== null && !Array.isArray(value);
436
+ }
437
+ function mergePresets(options) {
438
+ const { label, builtIn, pluginConfig, validatePreset } = options;
439
+ const merged = { ...builtIn };
440
+ if (pluginConfig === void 0) return merged;
441
+ if (!isRecord(pluginConfig)) throw new Error(`fundus.config: "plugins.${label}" must be an object.`);
442
+ if (pluginConfig.presets === void 0) return merged;
443
+ if (!isRecord(pluginConfig.presets)) throw new Error(`fundus.config: "plugins.${label}.presets" must be an object.`);
444
+ for (const [name, preset] of Object.entries(pluginConfig.presets)) merged[name] = validatePreset(name, preset);
445
+ return merged;
446
+ }
447
+ function binaryPathOverride(options) {
448
+ const { projectRoot, pluginConfig, label, key } = options;
449
+ if (!isRecord(pluginConfig) || pluginConfig[key] === void 0) return null;
450
+ const value = pluginConfig[key];
451
+ if (typeof value !== "string" || value.length === 0) throw new Error(`fundus.config: "plugins.${label}.${key}" must be a non-empty string.`);
452
+ return isAbsolute(value) ? value : resolve(projectRoot, value);
453
+ }
454
+ //#endregion
455
+ //#region src/lib/server/plugins/contract.ts
191
456
  /** Resolve a named preset out of a plugin's merged preset map, or throw listing the known names. */
192
457
  function resolveNamedPreset(label, presetName, presets) {
193
458
  const preset = presets[presetName];
@@ -201,32 +466,7 @@ function requireProxyField(proxy, field) {
201
466
  return value;
202
467
  }
203
468
  //#endregion
204
- //#region src/plugins/audio.ts
205
- /**
206
- * Audio plugin (server part): one ffmpeg transcode per preset.
207
- * No dimensions — the proxy's only output metadata is its duration.
208
- */
209
- const audioServerPlugin = {
210
- descriptor: audioPlugin,
211
- pluginConfig: (config) => config.plugins.audio,
212
- presets: (config) => config.plugins.audio.presets,
213
- resolvePreset: (parameters, config) => resolveNamedPreset("audio", parameters.preset, config.plugins.audio.presets),
214
- proxyExtension: (preset) => preset.container,
215
- process: (job) => processAudioFile({
216
- sourcePath: job.sourcePath,
217
- targetPath: job.targetPath,
218
- preset: job.preset,
219
- config: job.config
220
- }),
221
- readProxyInfo: (proxyPath, config) => probeMedia(proxyPath, config),
222
- buildManifestEntry: ({ src, proxy }) => ({
223
- kind: "audio",
224
- src,
225
- durationMs: requireProxyField(proxy, "durationMs")
226
- })
227
- };
228
- //#endregion
229
- //#region src/processing/video-processing.ts
469
+ //#region src/lib/server/processing/video-processing.ts
230
470
  /**
231
471
  * The ffmpeg transcode step shared by the video and chroma-key-video plugins
232
472
  * (chroma-key-video builds on video's processing; its keying data is
@@ -309,15 +549,95 @@ async function processVideoFile(options) {
309
549
  };
310
550
  }
311
551
  //#endregion
312
- //#region src/plugins/chroma-key-video.ts
552
+ //#region src/lib/server/plugins/video.ts
553
+ /**
554
+ * Video plugin (server part): one ffmpeg transcode per preset.
555
+ */
556
+ var videoConfigDefinition = {
557
+ namespace: "video",
558
+ resolve: ({ projectRoot, value }) => ({
559
+ presets: mergePresets({
560
+ label: "video",
561
+ builtIn: VIDEO_BUILT_IN_PRESETS,
562
+ pluginConfig: value,
563
+ validatePreset: validateVideoPreset
564
+ }),
565
+ ffmpegPath: binaryPathOverride({
566
+ projectRoot,
567
+ pluginConfig: value,
568
+ label: "video",
569
+ key: "ffmpegPath"
570
+ }),
571
+ ffprobePath: binaryPathOverride({
572
+ projectRoot,
573
+ pluginConfig: value,
574
+ label: "video",
575
+ key: "ffprobePath"
576
+ })
577
+ })
578
+ };
579
+ var videoServerPlugin = {
580
+ descriptor: videoPlugin,
581
+ configDefinitions: [videoConfigDefinition],
582
+ pluginConfig: (config) => config.plugins.video,
583
+ presets: (config) => config.plugins.video.presets,
584
+ resolvePreset: (parameters, config) => resolveNamedPreset("video", parameters.preset, config.plugins.video.presets),
585
+ proxyExtension: (preset) => preset.container,
586
+ process: (job) => processVideoFile({
587
+ sourcePath: job.sourcePath,
588
+ targetPath: job.targetPath,
589
+ preset: job.preset,
590
+ maxWidth: job.parameters.maxWidth,
591
+ config: job.config
592
+ }),
593
+ readProxyInfo: (proxyPath, config) => probeMedia(proxyPath, config),
594
+ buildManifestEntry: ({ src, proxy }) => ({
595
+ kind: "video",
596
+ src,
597
+ width: requireProxyField(proxy, "width"),
598
+ height: requireProxyField(proxy, "height"),
599
+ durationMs: requireProxyField(proxy, "durationMs")
600
+ })
601
+ };
602
+ var audioServerPlugin = {
603
+ descriptor: audioPlugin,
604
+ configDefinitions: [{
605
+ namespace: "audio",
606
+ resolve: ({ value }) => ({ presets: mergePresets({
607
+ label: "audio",
608
+ builtIn: AUDIO_BUILT_IN_PRESETS,
609
+ pluginConfig: value,
610
+ validatePreset: validateAudioPreset
611
+ }) })
612
+ }, videoConfigDefinition],
613
+ pluginConfig: (config) => config.plugins.audio,
614
+ presets: (config) => config.plugins.audio.presets,
615
+ resolvePreset: (parameters, config) => resolveNamedPreset("audio", parameters.preset, config.plugins.audio.presets),
616
+ proxyExtension: (preset) => preset.container,
617
+ process: (job) => processAudioFile({
618
+ sourcePath: job.sourcePath,
619
+ targetPath: job.targetPath,
620
+ preset: job.preset,
621
+ config: job.config
622
+ }),
623
+ readProxyInfo: (proxyPath, config) => probeMedia(proxyPath, config),
624
+ buildManifestEntry: ({ src, proxy }) => ({
625
+ kind: "audio",
626
+ src,
627
+ durationMs: requireProxyField(proxy, "durationMs")
628
+ })
629
+ };
630
+ //#endregion
631
+ //#region src/lib/server/plugins/chroma-key-video.ts
313
632
  /**
314
633
  * Chroma-key-video plugin (server part): shares the video plugin's transcode
315
634
  * pipeline and preset namespace — the keying parameters and the
316
635
  * mask/fallback references never reach ffmpeg, they are entry-only data
317
636
  * (the descriptor's `processingParameters` keeps them out of the job hash).
318
637
  */
319
- const chromaKeyVideoServerPlugin = {
638
+ var chromaKeyVideoServerPlugin = {
320
639
  descriptor: chromaKeyVideoPlugin,
640
+ configDefinitions: [videoConfigDefinition],
321
641
  pluginConfig: (config) => config.plugins.video,
322
642
  presets: (config) => config.plugins.video.presets,
323
643
  resolvePreset: (parameters, config) => resolveNamedPreset("video", parameters.preset, config.plugins.video.presets),
@@ -342,7 +662,7 @@ const chromaKeyVideoServerPlugin = {
342
662
  })
343
663
  };
344
664
  //#endregion
345
- //#region src/processing/image-processing.ts
665
+ //#region src/lib/server/processing/image-processing.ts
346
666
  /**
347
667
  * The sharp step shared by the image and slice plugins: optional resize to a
348
668
  * target width, format conversion per preset, write the proxy file. Returns
@@ -369,16 +689,26 @@ async function readImageDimensions(filePath) {
369
689
  };
370
690
  }
371
691
  //#endregion
372
- //#region src/plugins/image.ts
692
+ //#region src/lib/server/plugins/image.ts
373
693
  /**
374
694
  * Image plugin (server part): sharp scale + convert per preset.
375
695
  */
696
+ var imageConfigDefinition = {
697
+ namespace: "image",
698
+ resolve: ({ value }) => ({ presets: mergePresets({
699
+ label: "image",
700
+ builtIn: IMAGE_BUILT_IN_PRESETS,
701
+ pluginConfig: value,
702
+ validatePreset: validateImagePreset
703
+ }) })
704
+ };
376
705
  /** The delivered file extension of an image-preset format. */
377
706
  function extensionForImageFormat(format) {
378
707
  return format === "jpeg" ? "jpg" : format;
379
708
  }
380
- const imageServerPlugin = {
709
+ var imageServerPlugin = {
381
710
  descriptor: imagePlugin,
711
+ configDefinitions: [imageConfigDefinition],
382
712
  pluginConfig: (config) => config.plugins.image,
383
713
  presets: (config) => config.plugins.image.presets,
384
714
  resolvePreset: (parameters, config) => resolveNamedPreset("image", parameters.preset, config.plugins.image.presets),
@@ -398,12 +728,21 @@ const imageServerPlugin = {
398
728
  })
399
729
  };
400
730
  //#endregion
401
- //#region src/plugins/slice.ts
731
+ //#region src/lib/server/plugins/slice.ts
402
732
  /**
403
733
  * Slice plugin (server part): the same sharp conversion as the image plugin,
404
734
  * never resized — insets are authored in source pixels, so the delivered proxy
405
735
  * keeps the source dimensions. The slicing data is entry-only.
406
736
  */
737
+ var sliceConfigDefinition = {
738
+ namespace: "slice",
739
+ resolve: ({ value }) => ({ presets: mergePresets({
740
+ label: "slice",
741
+ builtIn: SLICE_BUILT_IN_PRESETS,
742
+ pluginConfig: value,
743
+ validatePreset: validateImagePreset
744
+ }) })
745
+ };
407
746
  function hasAnySide(insets) {
408
747
  if (!insets) return false;
409
748
  return insets.top > 0 || insets.right > 0 || insets.bottom > 0 || insets.left > 0;
@@ -418,8 +757,9 @@ function detectPixelRatio(fileName) {
418
757
  const ratio = match ? Number(match[1]) : 1;
419
758
  return ratio >= 1 ? ratio : 1;
420
759
  }
421
- const sliceServerPlugin = {
760
+ var sliceServerPlugin = {
422
761
  descriptor: slicePlugin,
762
+ configDefinitions: [sliceConfigDefinition],
423
763
  pluginConfig: (config) => config.plugins.slice,
424
764
  presets: (config) => config.plugins.slice.presets,
425
765
  resolvePreset: (parameters, config) => resolveNamedPreset("slice", parameters.preset, config.plugins.slice.presets),
@@ -454,41 +794,15 @@ const sliceServerPlugin = {
454
794
  }
455
795
  };
456
796
  //#endregion
457
- //#region src/plugins/video.ts
458
- /**
459
- * Video plugin (server part): one ffmpeg transcode per preset.
460
- */
461
- const videoServerPlugin = {
462
- descriptor: videoPlugin,
463
- pluginConfig: (config) => config.plugins.video,
464
- presets: (config) => config.plugins.video.presets,
465
- resolvePreset: (parameters, config) => resolveNamedPreset("video", parameters.preset, config.plugins.video.presets),
466
- proxyExtension: (preset) => preset.container,
467
- process: (job) => processVideoFile({
468
- sourcePath: job.sourcePath,
469
- targetPath: job.targetPath,
470
- preset: job.preset,
471
- maxWidth: job.parameters.maxWidth,
472
- config: job.config
473
- }),
474
- readProxyInfo: (proxyPath, config) => probeMedia(proxyPath, config),
475
- buildManifestEntry: ({ src, proxy }) => ({
476
- kind: "video",
477
- src,
478
- width: requireProxyField(proxy, "width"),
479
- height: requireProxyField(proxy, "height"),
480
- durationMs: requireProxyField(proxy, "durationMs")
481
- })
482
- };
483
- //#endregion
484
- //#region src/plugins.ts
485
- const serverPluginsByType = {
797
+ //#region src/lib/server/plugins.ts
798
+ var serverPluginCatalogue = {
486
799
  [imageServerPlugin.descriptor.type]: imageServerPlugin,
487
800
  [sliceServerPlugin.descriptor.type]: sliceServerPlugin,
488
801
  [videoServerPlugin.descriptor.type]: videoServerPlugin,
489
802
  [chromaKeyVideoServerPlugin.descriptor.type]: chromaKeyVideoServerPlugin,
490
803
  [audioServerPlugin.descriptor.type]: audioServerPlugin
491
804
  };
805
+ var serverPluginsByType = plugins.project(serverPluginCatalogue, "server implementation");
492
806
  function serverPluginForType(type, registry = serverPluginsByType) {
493
807
  const plugin = registry[type];
494
808
  if (!plugin) throw new Error(`Unknown asset type "${type}" (known: ${Object.keys(registry).sort().join(", ")}).`);
@@ -498,8 +812,7 @@ function serverPluginForType(type, registry = serverPluginsByType) {
498
812
  function descriptorsByType(registry) {
499
813
  return Object.fromEntries(Object.entries(registry).map(([type, plugin]) => [type, plugin.descriptor]));
500
814
  }
501
- /** The built-in registry's descriptor map (the common case; registry-aware code derives its own). */
502
- const pluginsByType = descriptorsByType(serverPluginsByType);
815
+ descriptorsByType(serverPluginsByType);
503
816
  /**
504
817
  * The job-hash parameter input for an asset: the plugin-declared
505
818
  * processing-relevant subset when the plugin distinguishes one (chroma-key-
@@ -509,7 +822,7 @@ function processingParametersOf(plugin, parameters) {
509
822
  return plugin.processingParameters ? plugin.processingParameters(parameters) : parameters;
510
823
  }
511
824
  //#endregion
512
- //#region src/codegen.ts
825
+ //#region src/lib/server/codegen.ts
513
826
  /**
514
827
  * Manifest module codegen: one `<manifest-name-kebab>.generated.ts`
515
828
  * per manifest in the configured manifests directory, containing data only —
@@ -517,7 +830,7 @@ function processingParametersOf(plugin, parameters) {
517
830
  * sorted asset ids, fixed property order, tab indentation. Generated modules
518
831
  * for deleted manifests are removed (only files carrying the fundus header).
519
832
  */
520
- const GENERATED_HEADER = "// generated by fundus — do not edit\n";
833
+ var GENERATED_HEADER = "// generated by fundus — do not edit\n";
521
834
  /** `mainCatalog` → `main-catalog`. */
522
835
  function kebabCaseFromCamel(name) {
523
836
  return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
@@ -664,7 +977,7 @@ function jobHash(rawHash, parameters, pipelineVersion = 1) {
664
977
  return createHash("sha256").update(rawHash + canonicalizeParameters(parameters) + String(pipelineVersion)).digest("hex").slice(0, 8);
665
978
  }
666
979
  //#endregion
667
- //#region src/folder-filesystem.ts
980
+ //#region src/lib/server/folder-filesystem.ts
668
981
  async function ensureManagedFolder(root, folder) {
669
982
  const directory = join(root, folder);
670
983
  await mkdir(directory, { recursive: true });
@@ -691,7 +1004,7 @@ function encodeRelativePathForUrl(path) {
691
1004
  return path.split("/").map(encodeURIComponent).join("/");
692
1005
  }
693
1006
  //#endregion
694
- //#region src/processing/processor.ts
1007
+ //#region src/lib/server/processing/processor.ts
695
1008
  /**
696
1009
  * The processing pipeline: every delivered asset gets exactly one
697
1010
  * proxy named `<assetId>.<jobHash>.<extension>`. Delivered = explicit or
@@ -708,7 +1021,7 @@ function encodeRelativePathForUrl(path) {
708
1021
  * drive the pipeline with fixture asset types.
709
1022
  */
710
1023
  /** Proxy file name shape; its id segment is pinned to core's asset-id grammar. */
711
- const PROXY_FILE_PATTERN = /^[a-z][A-Za-z0-9]*\.[0-9a-f]{8}\.[a-z0-9]+$/;
1024
+ var PROXY_FILE_PATTERN = /^[a-z][A-Za-z0-9]*\.[0-9a-f]{8}\.[a-z0-9]+$/;
712
1025
  /** Whether a file name in the proxy directory is (shaped like) a fundus proxy. */
713
1026
  function isProxyFileName(fileName) {
714
1027
  return PROXY_FILE_PATTERN.test(fileName);
@@ -845,7 +1158,7 @@ async function processAssets(config, library, registry = serverPluginsByType) {
845
1158
  return report;
846
1159
  }
847
1160
  //#endregion
848
- //#region src/library-store.ts
1161
+ //#region src/lib/server/library-store.ts
849
1162
  /**
850
1163
  * The fs-backed store over the library file (SSOT) and the raw directory.
851
1164
  * Every mutation is load → modify → deterministic write, so the file on disk
@@ -853,7 +1166,7 @@ async function processAssets(config, library, registry = serverPluginsByType) {
853
1166
  * store only removes them on asset deletion (the pipeline also prunes orphans).
854
1167
  */
855
1168
  /** A single path segment with no traversal — guards against `../` escapes. */
856
- const SAFE_SEGMENT = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
1169
+ var SAFE_SEGMENT = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
857
1170
  function assertSafeFileName(fileName) {
858
1171
  if (!SAFE_SEGMENT.test(fileName) || fileName.includes("/") || fileName.includes("\\")) throw new Error(`Unsafe file name "${fileName}".`);
859
1172
  }
@@ -1450,348 +1763,6 @@ var LibraryStore = class {
1450
1763
  if (collision !== void 0) throw new Error(`Folder "${path}" collides with existing folder "${collision}".`);
1451
1764
  }
1452
1765
  };
1453
- //#endregion
1454
- //#region src/operations.ts
1455
- /**
1456
- * The one operations layer: builds the state payload and runs
1457
- * every mutation through the autosave chain — write library → process affected
1458
- * assets → regenerate codegen → return fresh state. Both the REST server
1459
- * and the CLI consume this class, so every
1460
- * operation has exactly one implementation and there is no other save path.
1461
- */
1462
- /**
1463
- * The CLI's `--parameters` merge semantics (M7): a SHALLOW merge at the top
1464
- * level onto the current parameters — a nested object value replaces the whole
1465
- * current object under that key, a top-level `null` clears an optional field,
1466
- * and an asset reference is set with the normal `{"$asset": "<id>"}` marker.
1467
- * The merged result still passes the owning plugin's full validation.
1468
- */
1469
- function mergeParameterPatch(currentParameters, patch) {
1470
- if (typeof patch !== "object" || patch === null || Array.isArray(patch)) throw new Error("The parameter patch must be a JSON object, e.g. {\"preset\": \"default\"}.");
1471
- const merged = typeof currentParameters === "object" && currentParameters !== null && !Array.isArray(currentParameters) ? { ...currentParameters } : {};
1472
- for (const [key, value] of Object.entries(patch)) if (value === null) delete merged[key];
1473
- else merged[key] = value;
1474
- return merged;
1475
- }
1476
- var FundusOperations = class {
1477
- #config;
1478
- #registry;
1479
- #store;
1480
- constructor(config, registry = serverPluginsByType) {
1481
- this.#config = config;
1482
- this.#registry = registry;
1483
- this.#store = new LibraryStore(config, registry);
1484
- }
1485
- get config() {
1486
- return this.#config;
1487
- }
1488
- get store() {
1489
- return this.#store;
1490
- }
1491
- /** Read-only state snapshot (no processing). */
1492
- async state() {
1493
- return this.#buildState(await this.#store.read(), []);
1494
- }
1495
- /**
1496
- * Ingest an upload: store the raw file + record, then let the owning
1497
- * plugin's optional `seedIngestParameters` derive better starting
1498
- * parameters from the stored raw file (slices: insets from the image
1499
- * dimensions, pixel ratio from the file name — the schema default can't
1500
- * know either). The CLI additionally applies a parameter patch and/or an
1501
- * explicit membership set in the same operation; a failing application
1502
- * rolls the whole ingest back, so a failed ingest leaves nothing behind.
1503
- */
1504
- async ingest(options) {
1505
- let library = await this.#store.ingest(options);
1506
- try {
1507
- const plugin = serverPluginForType(options.type, this.#registry);
1508
- if (plugin.seedIngestParameters) {
1509
- const record = library.assets[options.id];
1510
- const seeded = await plugin.seedIngestParameters({
1511
- rawPath: join(this.#config.rawDir, record.raw),
1512
- rawFileName: rawFileNameOfPath(record.raw),
1513
- parameters: plugin.descriptor.validateParameters(record.parameters),
1514
- config: this.#config
1515
- });
1516
- if (seeded !== void 0) library = await this.#store.updateParameters(options.id, seeded);
1517
- }
1518
- if (options.parametersPatch !== void 0 || options.manifests !== void 0) library = await this.#applyAssetChanges(library, options.id, {
1519
- parametersPatch: options.parametersPatch,
1520
- manifests: options.manifests
1521
- });
1522
- } catch (cause) {
1523
- await this.#store.deleteAsset(options.id);
1524
- throw cause;
1525
- }
1526
- return this.#afterMutation(library);
1527
- }
1528
- /** Apply a PATCH body: parameters and/or manifests and/or a rename — in that order. */
1529
- async patchAsset(id, body) {
1530
- let library = await this.#store.read();
1531
- this.#requireAsset(library, id);
1532
- if (body.parameters !== void 0) library = await this.#store.updateParameters(id, body.parameters);
1533
- if (body.manifests !== void 0) {
1534
- if (!Array.isArray(body.manifests) || body.manifests.some((name) => typeof name !== "string")) throw new Error("\"manifests\" must be an array of manifest names.");
1535
- library = await this.#store.setManifests(id, body.manifests);
1536
- }
1537
- if (body.renameTo !== void 0) {
1538
- if (typeof body.renameTo !== "string") throw new Error("\"renameTo\" must be a string.");
1539
- library = await this.#store.renameAsset(id, body.renameTo);
1540
- }
1541
- if (body.folder !== void 0) {
1542
- if (typeof body.folder !== "string") throw new Error("\"folder\" must be a string.");
1543
- library = await this.#store.moveAsset(body.renameTo ?? id, body.folder);
1544
- }
1545
- return this.#afterMutation(library);
1546
- }
1547
- async moveAsset(id, folder) {
1548
- return this.#afterMutation(await this.#store.moveAsset(id, folder));
1549
- }
1550
- /** Move a selected asset set as one editor action and roll back on any failure. */
1551
- async moveAssets(assetIds, folder) {
1552
- const uniqueIds = [...new Set(assetIds)];
1553
- if (uniqueIds.length === 0) throw new Error("At least one asset id is required.");
1554
- const originalFolders = /* @__PURE__ */ new Map();
1555
- const library = await this.#store.read();
1556
- for (const id of uniqueIds) {
1557
- this.#requireAsset(library, id);
1558
- originalFolders.set(id, folderOfRawPath(library.assets[id].raw));
1559
- }
1560
- const movedIds = [];
1561
- try {
1562
- let updated = library;
1563
- for (const id of uniqueIds) {
1564
- if (originalFolders.get(id) === folder) continue;
1565
- updated = await this.#store.moveAsset(id, folder);
1566
- movedIds.push(id);
1567
- }
1568
- return this.#afterMutation(updated);
1569
- } catch (cause) {
1570
- for (const id of movedIds.reverse()) await this.#store.moveAsset(id, originalFolders.get(id) ?? "");
1571
- throw cause;
1572
- }
1573
- }
1574
- async createFolder(path) {
1575
- return this.#afterMutation(await this.#store.createFolder(path));
1576
- }
1577
- async renameFolder(path, newName) {
1578
- return this.#afterMutation(await this.#store.renameFolder(path, newName));
1579
- }
1580
- async moveFolder(path, parent) {
1581
- return this.#afterMutation(await this.#store.moveFolder(path, parent));
1582
- }
1583
- async deleteFolder(path) {
1584
- const result = await this.#store.deleteFolder(path);
1585
- return this.#afterMutation(result.library);
1586
- }
1587
- /**
1588
- * CLI-facing asset update (M7): shallow-merge a parameter patch and/or
1589
- * replace the explicit manifest membership — validating the *result* before
1590
- * anything is persisted, so a change that would leave the library invalid
1591
- * (dangling or cyclic reference, unknown manifest, invalid parameters) is
1592
- * rejected with nothing written. The REST `patchAsset` keeps the editor's
1593
- * lenient replace semantics.
1594
- */
1595
- async setAsset(id, changes) {
1596
- const library = await this.#store.read();
1597
- this.#requireAsset(library, id);
1598
- return this.#afterMutation(await this.#applyAssetChanges(library, id, changes));
1599
- }
1600
- /**
1601
- * CLI-facing rename (M7): rejects a rename that would break references —
1602
- * a referrer keeps pointing at the old id, so renaming a referenced asset
1603
- * introduces dangling-reference errors. Update the referrers first.
1604
- */
1605
- async renameAsset(id, newId) {
1606
- const library = await this.#store.read();
1607
- this.#requireAsset(library, id);
1608
- if (newId !== id && !Object.hasOwn(library.assets, newId)) {
1609
- const candidate = structuredClone(library);
1610
- candidate.assets[newId] = candidate.assets[id];
1611
- delete candidate.assets[id];
1612
- await this.#assertNoIntroducedIssues(library, candidate);
1613
- }
1614
- return this.#afterMutation(await this.#store.renameAsset(id, newId));
1615
- }
1616
- /**
1617
- * Delete an asset: the record, its raw file, and its proxies. With
1618
- * `failOnIntroducedIssues` (the CLI), a delete that would break references
1619
- * (the asset is a reference target) is rejected before anything —
1620
- * including the raw file — is removed; the REST route stays lenient.
1621
- */
1622
- async deleteAsset(id, options) {
1623
- if (options?.failOnIntroducedIssues) {
1624
- const library = await this.#store.read();
1625
- this.#requireAsset(library, id);
1626
- const candidate = structuredClone(library);
1627
- delete candidate.assets[id];
1628
- await this.#assertNoIntroducedIssues(library, candidate);
1629
- }
1630
- return this.#afterMutation(await this.#store.deleteAsset(id));
1631
- }
1632
- /** Delete a selected asset set as one lenient editor mutation. */
1633
- async deleteAssets(assetIds) {
1634
- const uniqueIds = [...new Set(assetIds)];
1635
- if (uniqueIds.length === 0) throw new Error("At least one asset id is required.");
1636
- return this.#afterMutation(await this.#store.deleteAssets(uniqueIds));
1637
- }
1638
- async createManifest(name) {
1639
- return this.#afterMutation(await this.#store.createManifest(name));
1640
- }
1641
- async setManifestAppearance(name, appearance) {
1642
- return this.#afterMutation(await this.#store.setManifestAppearance(name, appearance));
1643
- }
1644
- /** Apply a PATCH body: appearance fields and/or a rename — in that order. */
1645
- async patchManifest(name, body) {
1646
- let library = await this.#store.setManifestAppearance(name, {
1647
- color: body.color,
1648
- emoji: body.emoji
1649
- });
1650
- if (body.renameTo !== void 0) {
1651
- if (typeof body.renameTo !== "string") throw new Error("\"renameTo\" must be a string.");
1652
- library = await this.#store.renameManifest(name, body.renameTo);
1653
- }
1654
- return this.#afterMutation(library);
1655
- }
1656
- /**
1657
- * Rename a manifest: rewrites every asset's membership and replaces the
1658
- * generated module (the old one is pruned as a stray). Host code importing
1659
- * the module must be updated by hand — both the file name and the exported
1660
- * const change with the manifest name.
1661
- */
1662
- async renameManifest(name, newName) {
1663
- return this.#afterMutation(await this.#store.renameManifest(name, newName));
1664
- }
1665
- async deleteManifest(name) {
1666
- return this.#afterMutation(await this.#store.deleteManifest(name));
1667
- }
1668
- /** The autosave chain tail: process + regenerate, then report fresh state. */
1669
- async #afterMutation(library) {
1670
- const pipelineIssues = [];
1671
- const issues = await this.#store.validate(library);
1672
- if (issues.length === 0) try {
1673
- const report = await processAssets(this.#config, library, this.#registry);
1674
- await generateManifestModules(this.#config, library, report.proxies, this.#registry);
1675
- } catch (cause) {
1676
- pipelineIssues.push({ message: `Processing failed: ${cause instanceof Error ? cause.message : String(cause)}` });
1677
- }
1678
- return this.#buildState(library, pipelineIssues, issues);
1679
- }
1680
- async #buildState(library, extraIssues, precomputedIssues) {
1681
- const issues = [...precomputedIssues ?? await this.#store.validate(library), ...extraIssues];
1682
- const referencesByAsset = collectAllReferences(library, descriptorsByType(this.#registry));
1683
- const membershipByManifest = computeLibraryMembership(library, referencesByAsset);
1684
- const delivered = allDeliveredAssetIds(membershipByManifest);
1685
- const passiveManifestsByAsset = /* @__PURE__ */ new Map();
1686
- for (const manifestName of Object.keys(library.manifests).sort(compareCodeUnits)) for (const passiveMember of membershipByManifest[manifestName].passiveMembers) {
1687
- const passiveManifests = passiveManifestsByAsset.get(passiveMember.assetId) ?? [];
1688
- passiveManifests.push({
1689
- manifest: manifestName,
1690
- via: passiveMember.via
1691
- });
1692
- passiveManifestsByAsset.set(passiveMember.assetId, passiveManifests);
1693
- }
1694
- const incomingByAsset = /* @__PURE__ */ new Map();
1695
- for (const [referrerId, references] of Object.entries(referencesByAsset)) for (const reference of references) {
1696
- const incoming = incomingByAsset.get(reference.assetId) ?? [];
1697
- incoming.push({
1698
- field: reference.field,
1699
- assetId: referrerId
1700
- });
1701
- incomingByAsset.set(reference.assetId, incoming);
1702
- }
1703
- const assets = [];
1704
- for (const assetId of Object.keys(library.assets).sort(compareCodeUnits)) {
1705
- const record = library.assets[assetId];
1706
- const warnings = [];
1707
- for (const issue of issues) if (issue.assetId === assetId) warnings.push(issue.message);
1708
- if (record.manifests.length === 0 && !delivered.has(assetId)) warnings.push("In no manifest and not referenced by any delivered asset — nothing is processed or generated for it.");
1709
- let proxy = null;
1710
- if (delivered.has(assetId)) try {
1711
- const currentProxy = await readCurrentProxy(assetId, record, this.#config, this.#registry);
1712
- if (currentProxy) proxy = {
1713
- url: `/files/proxy/${encodeRelativePathForUrl(currentProxy.relativePath)}`,
1714
- upToDate: true,
1715
- ...currentProxy
1716
- };
1717
- else warnings.push("Proxy is stale or missing — it will be (re)processed on the next save.");
1718
- } catch (cause) {
1719
- warnings.push(cause instanceof Error ? cause.message : String(cause));
1720
- }
1721
- assets.push({
1722
- id: assetId,
1723
- type: record.type,
1724
- raw: record.raw,
1725
- folder: folderOfRawPath(record.raw),
1726
- rawUrl: `/files/raw/${encodeRelativePathForUrl(record.raw)}`,
1727
- rawHash: record.rawHash,
1728
- parameters: record.parameters,
1729
- manifests: record.manifests,
1730
- passiveManifests: passiveManifestsByAsset.get(assetId) ?? [],
1731
- proxy,
1732
- warnings,
1733
- references: {
1734
- outgoing: referencesByAsset[assetId].map((reference) => ({
1735
- field: reference.field,
1736
- assetId: reference.assetId
1737
- })),
1738
- incoming: incomingByAsset.get(assetId) ?? []
1739
- }
1740
- });
1741
- }
1742
- const proxySizeByAssetId = new Map(assets.filter((asset) => asset.proxy).map((asset) => [asset.id, asset.proxy?.fileSize ?? 0]));
1743
- const manifestMemberships = {};
1744
- const manifestDeliveredBytes = {};
1745
- for (const manifestName of Object.keys(library.manifests).sort(compareCodeUnits)) {
1746
- const membership = membershipByManifest[manifestName];
1747
- manifestMemberships[manifestName] = {
1748
- explicitMemberIds: membership.explicitMemberIds,
1749
- passiveMembers: membership.passiveMembers
1750
- };
1751
- let deliveredBytes = 0;
1752
- for (const deliveredAssetId of membership.deliveredAssetIds) deliveredBytes += proxySizeByAssetId.get(deliveredAssetId) ?? 0;
1753
- manifestDeliveredBytes[manifestName] = deliveredBytes;
1754
- }
1755
- const presetNames = {};
1756
- for (const [type, plugin] of Object.entries(this.#registry)) presetNames[type] = Object.keys(plugin.presets(this.#config)).sort();
1757
- return {
1758
- library,
1759
- assets,
1760
- issues,
1761
- presetNames,
1762
- manifestMemberships,
1763
- manifestDeliveredBytes
1764
- };
1765
- }
1766
- #requireAsset(library, id) {
1767
- if (!Object.hasOwn(library.assets, id)) throw new Error(`Unknown asset "${id}".`);
1768
- }
1769
- /**
1770
- * Apply a CLI asset change to the store, validating the resulting library
1771
- * on an in-memory candidate first — nothing is persisted when the change
1772
- * would introduce validation issues. Pre-existing issues never block an
1773
- * unrelated mutation (only *introduced* issues are rejected).
1774
- */
1775
- async #applyAssetChanges(library, id, changes) {
1776
- const candidate = structuredClone(library);
1777
- const candidateRecord = candidate.assets[id];
1778
- if (changes.parametersPatch !== void 0) candidateRecord.parameters = serverPluginForType(candidateRecord.type, this.#registry).descriptor.validateParameters(mergeParameterPatch(candidateRecord.parameters, changes.parametersPatch));
1779
- if (changes.manifests !== void 0) candidateRecord.manifests = [...new Set(changes.manifests)].sort();
1780
- await this.#assertNoIntroducedIssues(library, candidate);
1781
- let updated = library;
1782
- if (changes.parametersPatch !== void 0) updated = await this.#store.updateParameters(id, candidateRecord.parameters);
1783
- if (changes.manifests !== void 0) updated = await this.#store.setManifests(id, changes.manifests);
1784
- return updated;
1785
- }
1786
- /** Reject a candidate library that has validation issues the current one lacks. */
1787
- async #assertNoIntroducedIssues(library, candidate) {
1788
- const existingIssueKeys = new Set((await this.#store.validate(library)).map((issue) => `${issue.assetId ?? ""}|${issue.message}`));
1789
- const introduced = (await this.#store.validate(candidate)).filter((issue) => !existingIssueKeys.has(`${issue.assetId ?? ""}|${issue.message}`));
1790
- if (introduced.length > 0) {
1791
- const lines = introduced.map((issue) => ` - ${issue.assetId ? `[${issue.assetId}] ` : ""}${issue.message}`);
1792
- throw new Error(`Rejected — the change would leave the library invalid (nothing was written):\n${lines.join("\n")}`);
1793
- }
1794
- }
1795
- };
1796
- //#endregion
1797
- export { runToolchainBinary as C, ffmpegBinaryPath as S, resolveConfig as T, strayGeneratedModuleNames as _, deliveredAssetIds as a, serverPluginForType as b, isProxyFileName as c, readProxyOutputInfo as d, listRelativeFiles as f, renderManifestModule as g, manifestModuleFileName as h, sha256Hex as i, processAssets as l, generateManifestModules as m, LibraryStore as n, expectedProxyFileName as o, buildManifestEntries as p, assertSafeRelativeFilePath as r, expectedProxyRelativePath as s, FundusOperations as t, readCurrentProxy as u, descriptorsByType as v, loadConfig as w, serverPluginsByType as x, pluginsByType as y };
1766
+
1767
+ export { LibraryStore as L, serverPluginForType as a, computeLibraryMembership as b, collectAllReferences as c, descriptorsByType as d, allDeliveredAssetIds as e, compareCodeUnits as f, generateManifestModules as g, encodeRelativePathForUrl as h, isRecord as i, assertSafeRelativeFilePath as j, processAssets as p, readCurrentProxy as r, serverPluginsByType as s };
1768
+ //# sourceMappingURL=library-store.js-ZLudCJlT.js.map