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.
- package/README.md +73 -0
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.gz +0 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css +1 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css.br +0 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css +1 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css.br +0 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css +1 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css.br +0 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css.gz +0 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css +1 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.br +0 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.gz +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css +1 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.gz +0 -0
- package/build/client/_app/immutable/chunks/Bjy-W4x2.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bjy-W4x2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.br +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CO4RBJfa.js.br +0 -0
- package/build/client/_app/immutable/chunks/CO4RBJfa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js +108 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js.br +0 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js.gz +0 -0
- package/{dist/editor/_app/immutable/chunks/BfPE5wdt.js → build/client/_app/immutable/chunks/DVEQFclp.js} +1 -1
- package/build/client/_app/immutable/chunks/DVEQFclp.js.br +1 -0
- package/build/client/_app/immutable/chunks/DVEQFclp.js.gz +0 -0
- package/{dist/editor/_app/immutable/chunks/DjvOiDmq.js → build/client/_app/immutable/chunks/DiFoWL2c.js} +1 -1
- package/build/client/_app/immutable/chunks/DiFoWL2c.js.br +0 -0
- package/build/client/_app/immutable/chunks/DiFoWL2c.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js +1 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js.br +0 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.br +1 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.gz +0 -0
- package/{dist/editor/_app/immutable/entry/app.BkqbfI8n.js → build/client/_app/immutable/entry/app.CKgtYXQb.js} +2 -2
- package/build/client/_app/immutable/entry/app.CKgtYXQb.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CKgtYXQb.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js +1 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js.br +0 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/0.xdMI5GEU.js → build/client/_app/immutable/nodes/0.I_1SApYQ.js} +1 -1
- package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/1.D9p2tfoJ.js → build/client/_app/immutable/nodes/1.v6eUTnWq.js} +1 -1
- package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js +3 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js +1 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js +2 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js.gz +0 -0
- package/build/client/_app/version.json +1 -0
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/env.js +100 -0
- package/build/env.js.map +1 -0
- package/build/handler.js +22 -0
- package/build/handler.js.map +1 -0
- package/build/index.js +145 -0
- package/build/index.js.map +1 -0
- package/build/server/chunks/chunks/context.js-BUZqFfWG.js +429 -0
- package/build/server/chunks/chunks/context.js-BUZqFfWG.js.map +1 -0
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js +458 -0
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +1 -0
- package/build/server/chunks/chunks/env.js-l0sNRNKZ.js +2 -0
- package/build/server/chunks/chunks/env.js-l0sNRNKZ.js.map +1 -0
- package/build/server/chunks/chunks/exports.js-C0rwMMtk.js +152 -0
- package/build/server/chunks/chunks/exports.js-C0rwMMtk.js.map +1 -0
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js +64 -0
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js.map +1 -0
- package/build/server/chunks/chunks/http.js-DvopzwKh.js +100 -0
- package/build/server/chunks/chunks/http.js-DvopzwKh.js.map +1 -0
- package/build/server/chunks/chunks/internal.js-DGo2jPcg.js +5753 -0
- package/build/server/chunks/chunks/internal.js-DGo2jPcg.js.map +1 -0
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js +22 -0
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -0
- package/{dist/operations-DYM-5KVl.js → build/server/chunks/chunks/library-store.js-ZLudCJlT.js} +463 -492
- package/build/server/chunks/chunks/library-store.js-ZLudCJlT.js.map +1 -0
- package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js +146 -0
- package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js.map +1 -0
- package/build/server/chunks/chunks/routing.js-y-tryuip.js +213 -0
- package/build/server/chunks/chunks/routing.js-y-tryuip.js.map +1 -0
- package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js +255 -0
- package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js.map +1 -0
- package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js +1048 -0
- package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js +27 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js +24 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js +45 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js +38 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js +31 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js +24 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js +31 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js +24 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js +27 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js +24 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js +24 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js.map +1 -0
- package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js +23 -0
- package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js.map +1 -0
- package/build/server/chunks/handler-DnbS2t1B.js +1475 -0
- package/build/server/chunks/handler-DnbS2t1B.js.map +1 -0
- package/build/server/chunks/index.js-g77q3Boj.js +3789 -0
- package/build/server/chunks/index.js-g77q3Boj.js.map +1 -0
- package/build/server/chunks/manifest.js-qmEXbstS.js +149 -0
- package/build/server/chunks/manifest.js-qmEXbstS.js.map +1 -0
- package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js +11 -0
- package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js.map +1 -0
- package/build/server/chunks/nodes/1.js-H-qwo4GJ.js +7 -0
- package/build/server/chunks/nodes/1.js-H-qwo4GJ.js.map +1 -0
- package/build/server/chunks/nodes/2.js-DRpXVLwp.js +7 -0
- package/build/server/chunks/nodes/2.js-DRpXVLwp.js.map +1 -0
- package/build/server/chunks/nodes/3.js-CDEzNFeU.js +7 -0
- package/build/server/chunks/nodes/3.js-CDEzNFeU.js.map +1 -0
- package/build/server/chunks/nodes/4.js-BKL7LSSm.js +7 -0
- package/build/server/chunks/nodes/4.js-BKL7LSSm.js.map +1 -0
- package/build/shims.js +33 -0
- package/build/shims.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +116 -1336
- package/dist/config.d.ts +3 -2
- package/dist/config.js +2 -2
- package/dist/core/api-types.d.ts +141 -0
- package/dist/core/api-types.js +1 -0
- package/dist/core/asset-id.d.ts +28 -0
- package/dist/core/asset-id.js +59 -0
- package/dist/core/asset-reference.d.ts +23 -0
- package/dist/core/asset-reference.js +15 -0
- package/dist/core/canonicalize.d.ts +9 -0
- package/dist/core/canonicalize.js +36 -0
- package/dist/core/chroma-key.d.ts +30 -0
- package/dist/core/chroma-key.js +55 -0
- package/dist/core/config.d.ts +35 -0
- package/dist/core/config.js +4 -0
- package/dist/core/folder-path.d.ts +19 -0
- package/dist/core/folder-path.js +86 -0
- package/dist/core/index.d.ts +33 -499
- package/dist/core/index.js +17 -962
- package/dist/core/ingest.d.ts +24 -0
- package/dist/core/ingest.js +42 -0
- package/dist/core/library.d.ts +54 -0
- package/dist/core/library.js +138 -0
- package/dist/core/manifest-appearance.d.ts +35 -0
- package/dist/core/manifest-appearance.js +123 -0
- package/dist/core/manifest-entry.d.ts +104 -0
- package/dist/core/manifest-entry.js +1 -0
- package/dist/core/manifest-membership.d.ts +56 -0
- package/dist/core/manifest-membership.js +54 -0
- package/dist/core/plugins/audio.d.ts +26 -0
- package/dist/core/plugins/audio.js +44 -0
- package/dist/core/plugins/chroma-key-video.d.ts +29 -0
- package/dist/core/plugins/chroma-key-video.js +69 -0
- package/dist/core/plugins/contract.d.ts +35 -0
- package/dist/core/plugins/contract.js +1 -0
- package/dist/core/plugins/image.d.ts +30 -0
- package/dist/core/plugins/image.js +45 -0
- package/dist/core/plugins/registry.d.ts +16 -0
- package/dist/core/plugins/registry.js +52 -0
- package/dist/core/plugins/slice.d.ts +22 -0
- package/dist/core/plugins/slice.js +60 -0
- package/dist/core/plugins/video.d.ts +45 -0
- package/dist/core/plugins/video.js +77 -0
- package/dist/core/reference-graph.d.ts +41 -0
- package/dist/core/reference-graph.js +138 -0
- package/dist/core/slice-data.d.ts +61 -0
- package/dist/core/slice-data.js +49 -0
- package/dist/runtime/chroma-key-gl.d.ts +40 -22
- package/dist/runtime/chroma-key-gl.js +189 -183
- package/dist/runtime/components/ChromaKeyVideo.svelte +3 -3
- package/dist/runtime/components/ChromaKeyVideo.svelte.d.ts +21 -0
- package/dist/runtime/components/Image.svelte +1 -1
- package/dist/runtime/components/Image.svelte.d.ts +9 -0
- package/dist/runtime/components/Slice.svelte +1 -1
- package/dist/runtime/components/Slice.svelte.d.ts +19 -0
- package/dist/runtime/components/SliceCanvas.svelte +3 -3
- package/dist/runtime/components/SliceCanvas.svelte.d.ts +10 -0
- package/dist/runtime/components/SliceDom.svelte +3 -3
- package/dist/runtime/components/SliceDom.svelte.d.ts +10 -0
- package/dist/runtime/components/Video.svelte +2 -2
- package/dist/runtime/components/Video.svelte.d.ts +18 -0
- package/dist/runtime/geometry.d.ts +55 -35
- package/dist/runtime/geometry.js +186 -191
- package/dist/runtime/index.d.ts +9 -9
- package/dist/runtime/index.js +9 -9
- package/dist/runtime/kind-handlers.d.ts +33 -30
- package/dist/runtime/kind-handlers.js +85 -67
- package/dist/runtime/manifest.d.ts +53 -47
- package/dist/runtime/manifest.js +133 -113
- package/dist/runtime/observe-border-box-size.d.ts +1 -4
- package/dist/runtime/observe-border-box-size.js +9 -10
- package/dist/runtime/preload-registry.d.ts +22 -8
- package/dist/runtime/preload-registry.js +65 -62
- package/dist/server/build.d.ts +8 -0
- package/dist/server/build.js +25 -0
- package/dist/server/check.d.ts +24 -0
- package/dist/server/check.js +199 -0
- package/dist/server/cli-commands.d.ts +25 -0
- package/dist/server/cli-commands.js +1178 -0
- package/dist/server/cli-init.d.ts +3 -0
- package/dist/server/cli-init.js +53 -0
- package/dist/server/cli-io.d.ts +46 -0
- package/dist/server/cli-io.js +105 -0
- package/dist/server/codegen.d.ts +58 -0
- package/dist/server/codegen.js +185 -0
- package/dist/server/config-loader.d.ts +35 -0
- package/dist/server/config-loader.js +79 -0
- package/dist/server/context.d.ts +8 -0
- package/dist/server/context.js +19 -0
- package/dist/server/folder-filesystem.d.ts +7 -0
- package/dist/server/folder-filesystem.js +39 -0
- package/dist/server/http.d.ts +12 -0
- package/dist/server/http.js +100 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +9 -0
- package/dist/server/job-hash.d.ts +10 -0
- package/dist/server/job-hash.js +23 -0
- package/dist/server/library-store.d.ts +82 -0
- package/dist/server/library-store.js +763 -0
- package/dist/server/operations.d.ts +87 -0
- package/dist/server/operations.js +427 -0
- package/dist/server/plugins/audio.d.ts +8 -0
- package/dist/server/plugins/audio.js +41 -0
- package/dist/server/plugins/chroma-key-video.d.ts +9 -0
- package/dist/server/plugins/chroma-key-video.js +40 -0
- package/dist/server/plugins/config.d.ts +13 -0
- package/dist/server/plugins/config.js +32 -0
- package/dist/server/plugins/contract.d.ts +114 -0
- package/dist/server/plugins/contract.js +16 -0
- package/dist/server/plugins/image.d.ts +9 -0
- package/dist/server/plugins/image.js +43 -0
- package/dist/server/plugins/slice.d.ts +15 -0
- package/dist/server/plugins/slice.js +76 -0
- package/dist/server/plugins/video.d.ts +7 -0
- package/dist/server/plugins/video.js +54 -0
- package/dist/server/plugins.d.ts +23 -0
- package/dist/server/plugins.js +35 -0
- package/dist/server/processing/audio-processing.d.ts +17 -0
- package/dist/server/processing/audio-processing.js +46 -0
- package/dist/server/processing/ffmpeg.d.ts +9 -0
- package/dist/server/processing/ffmpeg.js +50 -0
- package/dist/server/processing/image-processing.d.ts +16 -0
- package/dist/server/processing/image-processing.js +26 -0
- package/dist/server/processing/media-probe.d.ts +15 -0
- package/dist/server/processing/media-probe.js +27 -0
- package/dist/server/processing/processor.d.ts +50 -0
- package/dist/server/processing/processor.js +158 -0
- package/dist/server/processing/video-processing.d.ts +35 -0
- package/dist/server/processing/video-processing.js +87 -0
- package/dist/server/server.d.ts +13 -0
- package/dist/server/server.js +57 -0
- package/dist/server/start.d.ts +13 -0
- package/dist/server/start.js +80 -0
- package/package.json +31 -16
- package/dist/build-CZYFLLvl.js +0 -191
- package/dist/core/image-Dqmz8FXS.js +0 -54
- package/dist/core/reference-graph-CjuHbLDw.d.ts +0 -280
- package/dist/core/testing.d.ts +0 -27
- package/dist/core/testing.js +0 -80
- package/dist/editor/200.html +0 -37
- package/dist/editor/_app/immutable/assets/2.DIbbW7U1.css +0 -1
- package/dist/editor/_app/immutable/assets/3.C-9vFQ9A.css +0 -1
- package/dist/editor/_app/immutable/assets/4.BS8ElpOV.css +0 -1
- package/dist/editor/_app/immutable/assets/ConfirmDialog.CHEISE-L.css +0 -1
- package/dist/editor/_app/immutable/assets/LibraryContextMenu.CDjCmdV-.css +0 -1
- package/dist/editor/_app/immutable/chunks/Cx6qtxJg.js +0 -1
- package/dist/editor/_app/immutable/chunks/DjdrdRzT.js +0 -1
- package/dist/editor/_app/immutable/chunks/n7mvYc42.js +0 -5
- package/dist/editor/_app/immutable/entry/start.C8HHOLly.js +0 -1
- package/dist/editor/_app/immutable/nodes/2.BrA0Hyzu.js +0 -3
- package/dist/editor/_app/immutable/nodes/3.WO7tZH_d.js +0 -104
- package/dist/editor/_app/immutable/nodes/4.DQtx17aW.js +0 -2
- package/dist/editor/_app/version.json +0 -1
- package/dist/index.d.ts +0 -370
- package/dist/index.js +0 -4
- package/dist/start-Bhnqt9Dc.js +0 -279
- package/dist/start-D2IXOrni.js +0 -2
- /package/{dist/editor → build/client}/_app/immutable/assets/0.DLhIsYA_.css +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/Bjy-W4x2.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/CNRz3sSw.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/CO4RBJfa.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/xihTtKlq.js +0 -0
package/{dist/operations-DYM-5KVl.js → build/server/chunks/chunks/library-store.js-ZLudCJlT.js}
RENAMED
|
@@ -1,87 +1,330 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import ffmpegStaticPath from
|
|
8
|
-
import { path } from
|
|
9
|
-
import { execFile } from
|
|
10
|
-
import { promisify } from
|
|
11
|
-
import sharp from
|
|
12
|
-
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
-
|
|
19
|
-
|
|
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
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
/**
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1454
|
-
|
|
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
|