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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type AssetId, type AssetRecord, type Library } from '../../core/index.js';
|
|
2
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
3
|
+
import type { AssetProxyInfo, ProxyOutputInfo, ServerPluginRegistry } from '../plugins/contract.js';
|
|
4
|
+
export type { AssetProxyInfo, ProxyOutputInfo } from '../plugins/contract.js';
|
|
5
|
+
/** Whether a file name in the proxy directory is (shaped like) a fundus proxy. */
|
|
6
|
+
export declare function isProxyFileName(fileName: string): boolean;
|
|
7
|
+
/** Whether a shaped proxy file belongs to one specific asset id. */
|
|
8
|
+
export declare function isProxyFileNameFor(assetId: string, fileName: string): boolean;
|
|
9
|
+
export interface ProcessingReport {
|
|
10
|
+
/** Asset ids whose proxy was (re)generated in this run. */
|
|
11
|
+
processed: string[];
|
|
12
|
+
/** Asset ids whose proxy was already current (name check hit). */
|
|
13
|
+
reused: string[];
|
|
14
|
+
/** Orphaned proxy file names removed from the proxy directory. */
|
|
15
|
+
pruned: string[];
|
|
16
|
+
/** Per delivered asset: the current proxy and its output metadata. */
|
|
17
|
+
proxies: Record<string, AssetProxyInfo>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The proxy file name an asset's current raw content + parameters demand.
|
|
21
|
+
* Throws when the parameters are invalid or reference an unknown preset.
|
|
22
|
+
*
|
|
23
|
+
* The hash input is the *effective* processing input: the asset's
|
|
24
|
+
* processing-relevant parameters (the plugin-declared subset — entry-only data
|
|
25
|
+
* like chroma-key tuning and references must not rename the proxy)
|
|
26
|
+
* plus the preset's **resolved settings** — parameters only carry the preset
|
|
27
|
+
* *name*, so hashing them alone would keep serving stale proxy bytes after a
|
|
28
|
+
* preset's quality/format is edited in the host config.
|
|
29
|
+
*/
|
|
30
|
+
export declare function expectedProxyFileName(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): string;
|
|
31
|
+
/** Expected proxy path relative to proxyDir, mirroring the raw asset folder. */
|
|
32
|
+
export declare function expectedProxyRelativePath(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): string;
|
|
33
|
+
/**
|
|
34
|
+
* Output metadata of an existing proxy file, probed per the owning plugin's
|
|
35
|
+
* strategy: image-backed proxies via sharp, media proxies via ffprobe.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readProxyOutputInfo(type: string, proxyPath: string, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<ProxyOutputInfo>;
|
|
38
|
+
/** Read one asset's current proxy projection, or null when its expected file is absent. */
|
|
39
|
+
export declare function readCurrentProxy(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<AssetProxyInfo | null>;
|
|
40
|
+
/**
|
|
41
|
+
* The delivered set: every explicit or passive member of ≥ 1 manifest —
|
|
42
|
+
* derived by the shared membership computation. Assets outside
|
|
43
|
+
* it are inert.
|
|
44
|
+
*/
|
|
45
|
+
export declare function deliveredAssetIds(library: Library, registry?: ServerPluginRegistry): Set<AssetId>;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure every delivered asset's proxy exists (processing stale/missing ones)
|
|
48
|
+
* and prune orphans. Assets outside the delivered set are skipped entirely.
|
|
49
|
+
*/
|
|
50
|
+
export declare function processAssets(config: ResolvedFundusConfig, library: Library, registry?: ServerPluginRegistry): Promise<ProcessingReport>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The processing pipeline: every delivered asset gets exactly one
|
|
3
|
+
* proxy named `<assetId>.<jobHash>.<extension>`. Delivered = explicit or
|
|
4
|
+
* passive member of ≥ 1 manifest (codegen inlines referenced
|
|
5
|
+
* entries, so a passive member's proxy must exist even when the asset is in
|
|
6
|
+
* no manifest itself). Because the name is computable without running the job,
|
|
7
|
+
* staleness is a name check: expected name exists → up to date, else →
|
|
8
|
+
* process. Afterwards, files in the proxy directory that no asset expects are
|
|
9
|
+
* pruned.
|
|
10
|
+
*
|
|
11
|
+
* Type-agnostic: every per-type decision (preset resolution, extension, the
|
|
12
|
+
* sharp/ffmpeg step, proxy probing) lives on the server plugins; this module
|
|
13
|
+
* only consults the registry — which it takes as a parameter, so tests can
|
|
14
|
+
* drive the pipeline with fixture asset types.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { mkdir, rename, stat, unlink } from 'node:fs/promises';
|
|
18
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
19
|
+
import { allDeliveredAssetIds, collectAllReferences, computeLibraryMembership, folderOfRawPath, rawFileNameOfPath } from '../../core/index.js';
|
|
20
|
+
import { jobHash } from '../job-hash.js';
|
|
21
|
+
import { descriptorsByType, processingParametersOf, serverPluginForType, serverPluginsByType } from '../plugins.js';
|
|
22
|
+
import { ensureManagedFolder, listRelativeFiles } from '../folder-filesystem.js';
|
|
23
|
+
/** Proxy file name shape; its id segment is pinned to core's asset-id grammar. */
|
|
24
|
+
const PROXY_FILE_PATTERN = /^[a-z][A-Za-z0-9]*\.[0-9a-f]{8}\.[a-z0-9]+$/;
|
|
25
|
+
/** Whether a file name in the proxy directory is (shaped like) a fundus proxy. */
|
|
26
|
+
export function isProxyFileName(fileName) {
|
|
27
|
+
return PROXY_FILE_PATTERN.test(fileName);
|
|
28
|
+
}
|
|
29
|
+
/** Whether a shaped proxy file belongs to one specific asset id. */
|
|
30
|
+
export function isProxyFileNameFor(assetId, fileName) {
|
|
31
|
+
return isProxyFileName(fileName) && fileName.startsWith(`${assetId}.`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The proxy file name an asset's current raw content + parameters demand.
|
|
35
|
+
* Throws when the parameters are invalid or reference an unknown preset.
|
|
36
|
+
*
|
|
37
|
+
* The hash input is the *effective* processing input: the asset's
|
|
38
|
+
* processing-relevant parameters (the plugin-declared subset — entry-only data
|
|
39
|
+
* like chroma-key tuning and references must not rename the proxy)
|
|
40
|
+
* plus the preset's **resolved settings** — parameters only carry the preset
|
|
41
|
+
* *name*, so hashing them alone would keep serving stale proxy bytes after a
|
|
42
|
+
* preset's quality/format is edited in the host config.
|
|
43
|
+
*/
|
|
44
|
+
export function expectedProxyFileName(assetId, record, config, registry = serverPluginsByType) {
|
|
45
|
+
const plugin = serverPluginForType(record.type, registry);
|
|
46
|
+
const parameters = plugin.descriptor.validateParameters(record.parameters);
|
|
47
|
+
const preset = plugin.resolvePreset(parameters, config);
|
|
48
|
+
const hash = jobHash(record.rawHash, {
|
|
49
|
+
parameters: processingParametersOf(plugin.descriptor, parameters),
|
|
50
|
+
presetSettings: preset
|
|
51
|
+
});
|
|
52
|
+
return `${assetId}.${hash}.${plugin.proxyExtension(preset)}`;
|
|
53
|
+
}
|
|
54
|
+
/** Expected proxy path relative to proxyDir, mirroring the raw asset folder. */
|
|
55
|
+
export function expectedProxyRelativePath(assetId, record, config, registry = serverPluginsByType) {
|
|
56
|
+
const fileName = expectedProxyFileName(assetId, record, config, registry);
|
|
57
|
+
const folder = folderOfRawPath(record.raw);
|
|
58
|
+
return folder === '' ? fileName : `${folder}/${fileName}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Output metadata of an existing proxy file, probed per the owning plugin's
|
|
62
|
+
* strategy: image-backed proxies via sharp, media proxies via ffprobe.
|
|
63
|
+
*/
|
|
64
|
+
export async function readProxyOutputInfo(type, proxyPath, config, registry = serverPluginsByType) {
|
|
65
|
+
return serverPluginForType(type, registry).readProxyInfo(proxyPath, config);
|
|
66
|
+
}
|
|
67
|
+
/** Read one asset's current proxy projection, or null when its expected file is absent. */
|
|
68
|
+
export async function readCurrentProxy(assetId, record, config, registry = serverPluginsByType) {
|
|
69
|
+
const fileName = expectedProxyFileName(assetId, record, config, registry);
|
|
70
|
+
const relativePath = expectedProxyRelativePath(assetId, record, config, registry);
|
|
71
|
+
const proxyPath = join(config.proxyDir, relativePath);
|
|
72
|
+
if (!existsSync(proxyPath))
|
|
73
|
+
return null;
|
|
74
|
+
const [outputInfo, proxyStat] = await Promise.all([
|
|
75
|
+
readProxyOutputInfo(record.type, proxyPath, config, registry),
|
|
76
|
+
stat(proxyPath)
|
|
77
|
+
]);
|
|
78
|
+
return { assetId, fileName, relativePath, ...outputInfo, fileSize: proxyStat.size };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The delivered set: every explicit or passive member of ≥ 1 manifest —
|
|
82
|
+
* derived by the shared membership computation. Assets outside
|
|
83
|
+
* it are inert.
|
|
84
|
+
*/
|
|
85
|
+
export function deliveredAssetIds(library, registry = serverPluginsByType) {
|
|
86
|
+
return allDeliveredAssetIds(computeLibraryMembership(library, collectAllReferences(library, descriptorsByType(registry))));
|
|
87
|
+
}
|
|
88
|
+
/** Run the owning plugin's processing job for one stale/missing proxy. */
|
|
89
|
+
async function processAssetFile(record, rawPath, proxyPath, config, registry) {
|
|
90
|
+
const plugin = serverPluginForType(record.type, registry);
|
|
91
|
+
const parameters = plugin.descriptor.validateParameters(record.parameters);
|
|
92
|
+
const extension = extname(proxyPath);
|
|
93
|
+
const stem = basename(proxyPath, extension);
|
|
94
|
+
const temporaryPath = join(dirname(proxyPath), `.${stem}.${crypto.randomUUID()}.tmp${extension}`);
|
|
95
|
+
try {
|
|
96
|
+
const output = await plugin.process({
|
|
97
|
+
sourcePath: rawPath,
|
|
98
|
+
targetPath: temporaryPath,
|
|
99
|
+
parameters,
|
|
100
|
+
preset: plugin.resolvePreset(parameters, config),
|
|
101
|
+
config
|
|
102
|
+
});
|
|
103
|
+
await rename(temporaryPath, proxyPath);
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
catch (cause) {
|
|
107
|
+
await unlink(temporaryPath).catch(() => { });
|
|
108
|
+
throw cause;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Ensure every delivered asset's proxy exists (processing stale/missing ones)
|
|
113
|
+
* and prune orphans. Assets outside the delivered set are skipped entirely.
|
|
114
|
+
*/
|
|
115
|
+
export async function processAssets(config, library, registry = serverPluginsByType) {
|
|
116
|
+
const report = { processed: [], reused: [], pruned: [], proxies: {} };
|
|
117
|
+
await mkdir(config.proxyDir, { recursive: true });
|
|
118
|
+
await Promise.all(library.folders.map((folder) => ensureManagedFolder(config.proxyDir, folder)));
|
|
119
|
+
const delivered = deliveredAssetIds(library, registry);
|
|
120
|
+
const expectedRelativePaths = new Set();
|
|
121
|
+
for (const [assetId, record] of Object.entries(library.assets)) {
|
|
122
|
+
if (!delivered.has(assetId))
|
|
123
|
+
continue;
|
|
124
|
+
const fileName = expectedProxyFileName(assetId, record, config, registry);
|
|
125
|
+
const relativePath = expectedProxyRelativePath(assetId, record, config, registry);
|
|
126
|
+
expectedRelativePaths.add(relativePath);
|
|
127
|
+
const proxyPath = join(config.proxyDir, relativePath);
|
|
128
|
+
const rawPath = join(config.rawDir, record.raw);
|
|
129
|
+
await mkdir(dirname(proxyPath), { recursive: true });
|
|
130
|
+
let proxy = await readCurrentProxy(assetId, record, config, registry);
|
|
131
|
+
if (proxy) {
|
|
132
|
+
report.reused.push(assetId);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (!existsSync(rawPath)) {
|
|
136
|
+
throw new Error(`Cannot process "${assetId}": raw file "${record.raw}" is missing.`);
|
|
137
|
+
}
|
|
138
|
+
const outputInfo = await processAssetFile(record, rawPath, proxyPath, config, registry);
|
|
139
|
+
report.processed.push(assetId);
|
|
140
|
+
const proxyStat = await stat(proxyPath);
|
|
141
|
+
proxy = { assetId, fileName, relativePath, ...outputInfo, fileSize: proxyStat.size };
|
|
142
|
+
}
|
|
143
|
+
report.proxies[assetId] = proxy;
|
|
144
|
+
}
|
|
145
|
+
// Prune orphans: only files matching the proxy naming pattern are touched,
|
|
146
|
+
// so a stray non-fundus file in the directory is left alone.
|
|
147
|
+
for (const relativePath of await listRelativeFiles(config.proxyDir)) {
|
|
148
|
+
const fileName = rawFileNameOfPath(relativePath);
|
|
149
|
+
if (!isProxyFileName(fileName))
|
|
150
|
+
continue;
|
|
151
|
+
if (expectedRelativePaths.has(relativePath))
|
|
152
|
+
continue;
|
|
153
|
+
await unlink(join(config.proxyDir, relativePath));
|
|
154
|
+
report.pruned.push(relativePath);
|
|
155
|
+
}
|
|
156
|
+
report.pruned.sort();
|
|
157
|
+
return report;
|
|
158
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { VideoPreset } from '../../core/index.js';
|
|
2
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
3
|
+
/**
|
|
4
|
+
* The delivered width the scale filter must produce, or null when no filter is
|
|
5
|
+
* needed. `maxWidth` is a downscale-only cap; yuv420p demands even dimensions,
|
|
6
|
+
* so an odd (capped or source) width rounds down and an odd source height
|
|
7
|
+
* forces a filter even without a cap (the `-2` idiom rounds it).
|
|
8
|
+
*/
|
|
9
|
+
export declare function videoScaleWidth(sourceWidth: number, sourceHeight: number, maxWidth?: number): number | null;
|
|
10
|
+
/**
|
|
11
|
+
* The full ffmpeg argument list for one video transcode (pure, testable
|
|
12
|
+
* without a binary). `-map_metadata -1` + bitexact flags keep the output
|
|
13
|
+
* byte-stable across runs (no embedded timestamps or encoder version tags);
|
|
14
|
+
* a source audio track is kept (re-encoded as AAC — a UI video may carry
|
|
15
|
+
* sound; nothing is stripped silently).
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildVideoTranscodeArguments(options: {
|
|
18
|
+
sourcePath: string;
|
|
19
|
+
targetPath: string;
|
|
20
|
+
preset: VideoPreset;
|
|
21
|
+
/** From `videoScaleWidth`; null = no scaling. */
|
|
22
|
+
scaleWidth: number | null;
|
|
23
|
+
}): string[];
|
|
24
|
+
export interface VideoProcessingOutput {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function processVideoFile(options: {
|
|
30
|
+
sourcePath: string;
|
|
31
|
+
targetPath: string;
|
|
32
|
+
preset: VideoPreset;
|
|
33
|
+
maxWidth?: number;
|
|
34
|
+
config: ResolvedFundusConfig;
|
|
35
|
+
}): Promise<VideoProcessingOutput>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ffmpeg transcode step shared by the video and chroma-key-video plugins
|
|
3
|
+
* (chroma-key-video builds on video's processing; its keying data is
|
|
4
|
+
* entry-only and never reaches ffmpeg). Deterministic output: all
|
|
5
|
+
* metadata is stripped and bitexact muxing is forced, so re-running a job
|
|
6
|
+
* yields byte-identical proxy files.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir } from 'node:fs/promises';
|
|
9
|
+
import { dirname } from 'node:path';
|
|
10
|
+
import { ffmpegBinaryPath, runToolchainBinary } from './ffmpeg.js';
|
|
11
|
+
import { probeMedia } from './media-probe.js';
|
|
12
|
+
/**
|
|
13
|
+
* The delivered width the scale filter must produce, or null when no filter is
|
|
14
|
+
* needed. `maxWidth` is a downscale-only cap; yuv420p demands even dimensions,
|
|
15
|
+
* so an odd (capped or source) width rounds down and an odd source height
|
|
16
|
+
* forces a filter even without a cap (the `-2` idiom rounds it).
|
|
17
|
+
*/
|
|
18
|
+
export function videoScaleWidth(sourceWidth, sourceHeight, maxWidth) {
|
|
19
|
+
const cappedWidth = maxWidth !== undefined ? Math.min(maxWidth, sourceWidth) : sourceWidth;
|
|
20
|
+
const evenWidth = Math.max(2, cappedWidth - (cappedWidth % 2));
|
|
21
|
+
if (evenWidth === sourceWidth && sourceHeight % 2 === 0)
|
|
22
|
+
return null;
|
|
23
|
+
return evenWidth;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The full ffmpeg argument list for one video transcode (pure, testable
|
|
27
|
+
* without a binary). `-map_metadata -1` + bitexact flags keep the output
|
|
28
|
+
* byte-stable across runs (no embedded timestamps or encoder version tags);
|
|
29
|
+
* a source audio track is kept (re-encoded as AAC — a UI video may carry
|
|
30
|
+
* sound; nothing is stripped silently).
|
|
31
|
+
*/
|
|
32
|
+
export function buildVideoTranscodeArguments(options) {
|
|
33
|
+
const { sourcePath, targetPath, preset, scaleWidth } = options;
|
|
34
|
+
return [
|
|
35
|
+
'-hide_banner',
|
|
36
|
+
'-loglevel',
|
|
37
|
+
'error',
|
|
38
|
+
'-y',
|
|
39
|
+
'-i',
|
|
40
|
+
sourcePath,
|
|
41
|
+
...(scaleWidth !== null ? ['-vf', `scale=${scaleWidth}:-2`] : []),
|
|
42
|
+
'-c:v',
|
|
43
|
+
'libx264',
|
|
44
|
+
'-preset',
|
|
45
|
+
preset.encoderPreset,
|
|
46
|
+
...(preset.tune !== undefined ? ['-tune', preset.tune] : []),
|
|
47
|
+
'-crf',
|
|
48
|
+
String(preset.crf),
|
|
49
|
+
'-pix_fmt',
|
|
50
|
+
preset.pixelFormat,
|
|
51
|
+
'-c:a',
|
|
52
|
+
'aac',
|
|
53
|
+
'-b:a',
|
|
54
|
+
'128k',
|
|
55
|
+
'-map_metadata',
|
|
56
|
+
'-1',
|
|
57
|
+
'-fflags',
|
|
58
|
+
'+bitexact',
|
|
59
|
+
'-flags:v',
|
|
60
|
+
'+bitexact',
|
|
61
|
+
'-flags:a',
|
|
62
|
+
'+bitexact',
|
|
63
|
+
'-movflags',
|
|
64
|
+
'+faststart',
|
|
65
|
+
targetPath
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
export async function processVideoFile(options) {
|
|
69
|
+
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
70
|
+
const source = await probeMedia(options.sourcePath, options.config);
|
|
71
|
+
if (source.width === undefined || source.height === undefined) {
|
|
72
|
+
throw new Error(`${options.sourcePath} has no video stream.`);
|
|
73
|
+
}
|
|
74
|
+
const scaleWidth = videoScaleWidth(source.width, source.height, options.maxWidth);
|
|
75
|
+
await runToolchainBinary(ffmpegBinaryPath(options.config), buildVideoTranscodeArguments({
|
|
76
|
+
sourcePath: options.sourcePath,
|
|
77
|
+
targetPath: options.targetPath,
|
|
78
|
+
preset: options.preset,
|
|
79
|
+
scaleWidth
|
|
80
|
+
}));
|
|
81
|
+
// Probe the output — the delivered dimensions/duration are what the entry carries.
|
|
82
|
+
const output = await probeMedia(options.targetPath, options.config);
|
|
83
|
+
if (output.width === undefined || output.height === undefined) {
|
|
84
|
+
throw new Error(`Transcoding ${options.sourcePath} produced no video stream.`);
|
|
85
|
+
}
|
|
86
|
+
return { width: output.width, height: output.height, durationMs: output.durationMs };
|
|
87
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
2
|
+
export { parseByteRange } from './http.js';
|
|
3
|
+
export type FundusHandler = (request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void) => void;
|
|
4
|
+
export interface FundusServerOptions {
|
|
5
|
+
handler: FundusHandler;
|
|
6
|
+
port: number;
|
|
7
|
+
forceCloseAfterMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FundusServer {
|
|
10
|
+
port: number;
|
|
11
|
+
stop(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function startServer(options: FundusServerOptions): Promise<FundusServer>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
export { parseByteRange } from './http.js';
|
|
3
|
+
export async function startServer(options) {
|
|
4
|
+
const server = createServer((request, response) => {
|
|
5
|
+
// adapter-node otherwise assumes HTTPS when a custom server does not
|
|
6
|
+
// provide protocol metadata, which makes same-origin multipart uploads
|
|
7
|
+
// fail SvelteKit's CSRF check on this intentionally HTTP-only local server.
|
|
8
|
+
request.headers['x-fundus-proto'] = 'http';
|
|
9
|
+
options.handler(request, response, (cause) => {
|
|
10
|
+
if (response.writableEnded)
|
|
11
|
+
return;
|
|
12
|
+
response.statusCode = 500;
|
|
13
|
+
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
14
|
+
response.end(JSON.stringify({
|
|
15
|
+
error: {
|
|
16
|
+
message: cause instanceof Error ? cause.message : 'Fundus handler declined the request.'
|
|
17
|
+
}
|
|
18
|
+
}));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
await new Promise((resolve, reject) => {
|
|
22
|
+
const onError = (cause) => {
|
|
23
|
+
server.off('listening', onListening);
|
|
24
|
+
reject(cause);
|
|
25
|
+
};
|
|
26
|
+
const onListening = () => {
|
|
27
|
+
server.off('error', onError);
|
|
28
|
+
resolve();
|
|
29
|
+
};
|
|
30
|
+
server.once('error', onError);
|
|
31
|
+
server.once('listening', onListening);
|
|
32
|
+
server.listen(options.port, '127.0.0.1');
|
|
33
|
+
});
|
|
34
|
+
const address = server.address();
|
|
35
|
+
if (!address || typeof address === 'string') {
|
|
36
|
+
throw new Error('Fundus server did not expose a TCP listening address.');
|
|
37
|
+
}
|
|
38
|
+
let stopping;
|
|
39
|
+
return {
|
|
40
|
+
port: address.port,
|
|
41
|
+
stop() {
|
|
42
|
+
stopping ??= new Promise((resolve, reject) => {
|
|
43
|
+
const timeout = setTimeout(() => server.closeAllConnections(), options.forceCloseAfterMs ?? 1000);
|
|
44
|
+
timeout.unref();
|
|
45
|
+
server.closeIdleConnections();
|
|
46
|
+
server.close((cause) => {
|
|
47
|
+
clearTimeout(timeout);
|
|
48
|
+
if (cause)
|
|
49
|
+
reject(cause);
|
|
50
|
+
else
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
return stopping;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const DEFAULT_PORT = 7583;
|
|
2
|
+
interface StartBannerOptions {
|
|
3
|
+
port: number;
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
color?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface RunStartOptions {
|
|
8
|
+
openBrowser?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Format the human startup summary without pulling a terminal UI dependency into the CLI. */
|
|
11
|
+
export declare function formatStartBanner({ port, projectRoot, color }: StartBannerOptions): string;
|
|
12
|
+
export declare function runStart(projectRoot: string, { openBrowser }?: RunStartOptions): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Start the packaged SvelteKit Node editor and API for one host project. */
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import open from 'open';
|
|
6
|
+
import { startServer } from './server.js';
|
|
7
|
+
export const DEFAULT_PORT = 7583;
|
|
8
|
+
function ansi(code, value, enabled) {
|
|
9
|
+
return enabled ? `\u001b[${code}m${value}\u001b[0m` : value;
|
|
10
|
+
}
|
|
11
|
+
/** Format the human startup summary without pulling a terminal UI dependency into the CLI. */
|
|
12
|
+
export function formatStartBanner({ port, projectRoot, color = false }) {
|
|
13
|
+
const url = `http://127.0.0.1:${port}`;
|
|
14
|
+
const brand = '◆ Fundus';
|
|
15
|
+
const editor = `Editor ${url}`;
|
|
16
|
+
const project = `Project ${projectRoot}`;
|
|
17
|
+
const ready = 'Ready · press Ctrl+C to stop';
|
|
18
|
+
const contentWidth = Math.max(42, brand.length, editor.length, project.length, ready.length);
|
|
19
|
+
const border = (value) => ansi('2', value, color);
|
|
20
|
+
const row = (plain, styled = plain) => `${border('│')} ${styled}${' '.repeat(contentWidth - plain.length)} ${border('│')}`;
|
|
21
|
+
const empty = row('');
|
|
22
|
+
return [
|
|
23
|
+
'',
|
|
24
|
+
border(`╭${'─'.repeat(contentWidth + 2)}╮`),
|
|
25
|
+
empty,
|
|
26
|
+
row(brand, `${ansi('38;2;24;152;243', '◆', color)} ${ansi('1', 'Fundus', color)}`),
|
|
27
|
+
empty,
|
|
28
|
+
row(editor, `${ansi('2', 'Editor', color)} ${ansi('4;36', url, color)}`),
|
|
29
|
+
row(project, `${ansi('2', 'Project', color)} ${projectRoot}`),
|
|
30
|
+
empty,
|
|
31
|
+
row(ready, ansi('32', ready, color)),
|
|
32
|
+
empty,
|
|
33
|
+
border(`╰${'─'.repeat(contentWidth + 2)}╯`)
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
36
|
+
export async function runStart(projectRoot, { openBrowser = true } = {}) {
|
|
37
|
+
const resolvedProjectRoot = resolve(projectRoot);
|
|
38
|
+
process.env.FUNDUS_PROJECT_ROOT = resolvedProjectRoot;
|
|
39
|
+
process.env.FUNDUS_BODY_SIZE_LIMIT ??= 'Infinity';
|
|
40
|
+
process.env.FUNDUS_ADAPTER_BODY_SIZE_LIMIT = process.env.FUNDUS_BODY_SIZE_LIMIT;
|
|
41
|
+
process.env.FUNDUS_ADAPTER_PROTOCOL_HEADER = 'x-fundus-proto';
|
|
42
|
+
const handlerUrl = new URL('../../build/handler.js', import.meta.url);
|
|
43
|
+
if (!existsSync(handlerUrl)) {
|
|
44
|
+
throw new Error(`The packaged Fundus SvelteKit handler is missing from ${fileURLToPath(handlerUrl)}. Reinstall the fundus package or report a packaging error.`);
|
|
45
|
+
}
|
|
46
|
+
const module = (await import(handlerUrl.href));
|
|
47
|
+
if (typeof module.handler !== 'function') {
|
|
48
|
+
throw new Error(`The packaged Fundus handler at ${fileURLToPath(handlerUrl)} is invalid.`);
|
|
49
|
+
}
|
|
50
|
+
const configuredPort = Number(process.env.FUNDUS_API_PORT ?? DEFAULT_PORT);
|
|
51
|
+
const server = await startServer({ handler: module.handler, port: configuredPort });
|
|
52
|
+
const editorUrl = `http://127.0.0.1:${server.port}`;
|
|
53
|
+
console.log(formatStartBanner({
|
|
54
|
+
port: server.port,
|
|
55
|
+
projectRoot: resolvedProjectRoot,
|
|
56
|
+
color: process.stdout.isTTY === true &&
|
|
57
|
+
process.env.NO_COLOR === undefined &&
|
|
58
|
+
process.env.TERM !== 'dumb'
|
|
59
|
+
}));
|
|
60
|
+
if (openBrowser) {
|
|
61
|
+
try {
|
|
62
|
+
await open(editorUrl);
|
|
63
|
+
}
|
|
64
|
+
catch (cause) {
|
|
65
|
+
console.warn(`Fundus could not open the editor in your browser. Open ${editorUrl} manually.\n` +
|
|
66
|
+
(cause instanceof Error ? cause.message : String(cause)));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
let signalStop;
|
|
70
|
+
const stopForSignal = () => {
|
|
71
|
+
signalStop ??= server.stop().then(() => {
|
|
72
|
+
process.exitCode = 0;
|
|
73
|
+
}, (cause) => {
|
|
74
|
+
console.error(cause);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
process.once('SIGINT', stopForSignal);
|
|
79
|
+
process.once('SIGTERM', stopForSignal);
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fundus",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/grischaerbe/fundus.git"
|
|
8
8
|
},
|
|
9
|
-
"imports": {
|
|
10
|
-
"#fundus/core": {
|
|
11
|
-
"types": "./dist/core/index.d.ts",
|
|
12
|
-
"default": "./dist/core/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
9
|
"bin": {
|
|
16
10
|
"fundus": "./dist/cli.js"
|
|
17
11
|
},
|
|
@@ -23,8 +17,8 @@
|
|
|
23
17
|
"default": "./dist/runtime/index.js"
|
|
24
18
|
},
|
|
25
19
|
"./server": {
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
27
|
-
"default": "./dist/index.js"
|
|
20
|
+
"types": "./dist/server/index.d.ts",
|
|
21
|
+
"default": "./dist/server/index.js"
|
|
28
22
|
},
|
|
29
23
|
"./config": {
|
|
30
24
|
"types": "./dist/config.d.ts",
|
|
@@ -32,34 +26,55 @@
|
|
|
32
26
|
}
|
|
33
27
|
},
|
|
34
28
|
"files": [
|
|
35
|
-
"dist"
|
|
29
|
+
"dist",
|
|
30
|
+
"build"
|
|
36
31
|
],
|
|
37
32
|
"engines": {
|
|
38
33
|
"node": ">=20.19.0"
|
|
39
34
|
},
|
|
40
35
|
"scripts": {
|
|
41
|
-
"
|
|
42
|
-
"
|
|
36
|
+
"dev": "vite dev",
|
|
37
|
+
"build:package": "svelte-package --input src/lib --output dist && node scripts/postbuild-package.mjs && publint",
|
|
38
|
+
"build:app": "vite build",
|
|
39
|
+
"build": "npm run build:package && npm run build:app",
|
|
43
40
|
"test": "npm run build && vitest run",
|
|
44
|
-
"
|
|
41
|
+
"smoke:pack": "node scripts/smoke-packed.mjs",
|
|
42
|
+
"smoke:e2e": "npm run build && node scripts/smoke-sveltekit.mjs",
|
|
43
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
45
44
|
"lint": "prettier --check .",
|
|
46
|
-
"format": "prettier --write ."
|
|
45
|
+
"format": "prettier --write .",
|
|
46
|
+
"prepack": "npm run build",
|
|
47
|
+
"changeset": "changeset",
|
|
48
|
+
"version-packages": "changeset version",
|
|
49
|
+
"release": "changeset publish"
|
|
47
50
|
},
|
|
48
51
|
"dependencies": {
|
|
49
52
|
"ffmpeg-static": "^5.3.0",
|
|
50
53
|
"ffprobe-static": "^3.1.0",
|
|
51
54
|
"jiti": "^2.5.1",
|
|
55
|
+
"open": "^11.0.0",
|
|
52
56
|
"sharp": "^0.34.4"
|
|
53
57
|
},
|
|
54
58
|
"peerDependencies": {
|
|
55
59
|
"svelte": "^5.0.0"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
62
|
+
"@changesets/changelog-github": "^0.7.0",
|
|
63
|
+
"@changesets/cli": "^2.31.0",
|
|
64
|
+
"@sveltejs/adapter-node": "^5.5.7",
|
|
65
|
+
"@sveltejs/kit": "^2.69.2",
|
|
66
|
+
"@sveltejs/package": "^2.5.8",
|
|
67
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
58
68
|
"@types/ffprobe-static": "^2.0.3",
|
|
59
|
-
"@types/node": "^24.
|
|
69
|
+
"@types/node": "^24.13.3",
|
|
70
|
+
"fuse.js": "^7.4.2",
|
|
71
|
+
"prettier": "^3.8.3",
|
|
72
|
+
"prettier-plugin-svelte": "^4.1.0",
|
|
73
|
+
"publint": "^0.3.21",
|
|
60
74
|
"svelte": "^5.56.1",
|
|
61
|
-
"
|
|
75
|
+
"svelte-check": "^4.6.0",
|
|
62
76
|
"typescript": "^6.0.3",
|
|
77
|
+
"vite": "^8.0.16",
|
|
63
78
|
"vitest": "^3.2.4"
|
|
64
79
|
}
|
|
65
80
|
}
|