fundus 0.0.3 → 0.1.0
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 +28 -3
- package/build/client/_app/immutable/assets/{2.wavcNhPC.css → 2.ohi-TClG.css} +1 -1
- 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.C-toFWI4.css +1 -0
- package/build/client/_app/immutable/assets/3.C-toFWI4.css.br +0 -0
- package/build/client/_app/immutable/assets/3.C-toFWI4.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/BA62n9FX.js +108 -0
- package/build/client/_app/immutable/chunks/BA62n9FX.js.br +0 -0
- package/build/client/_app/immutable/chunks/BA62n9FX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BiIto6ft.js → CQsKwm8E.js} +1 -1
- package/build/client/_app/immutable/chunks/CQsKwm8E.js.br +0 -0
- package/build/client/_app/immutable/chunks/CQsKwm8E.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DQ8SXFaX.js → D9-155C9.js} +1 -1
- package/build/client/_app/immutable/chunks/D9-155C9.js.br +0 -0
- package/build/client/_app/immutable/chunks/D9-155C9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CvY6YsFE.js → DVEDrSOn.js} +1 -1
- package/build/client/_app/immutable/chunks/DVEDrSOn.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVEDrSOn.js.gz +0 -0
- package/build/client/_app/immutable/chunks/cznBUb-E.js +1 -0
- package/build/client/_app/immutable/chunks/cznBUb-E.js.br +0 -0
- package/build/client/_app/immutable/chunks/cznBUb-E.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.ubN0v9bN.js → app.DwQWZs1F.js} +2 -2
- package/build/client/_app/immutable/entry/app.DwQWZs1F.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DwQWZs1F.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js +1 -0
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js.br +0 -0
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.Dr5ijCIl.js → 0.Sr1WDgiY.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Sr1WDgiY.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Sr1WDgiY.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.DiIGIRAb.js → 1.BwHqv728.js} +1 -1
- package/build/client/_app/immutable/nodes/1.BwHqv728.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.BwHqv728.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js +3 -0
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js +1 -0
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js +2 -0
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/handler.js +7 -7
- package/build/index.js +7 -7
- package/build/server/chunks/chunks/{context.js-Bw4WWLhG.js → context.js-CGD-b_vR.js} +24 -34
- package/build/server/chunks/chunks/context.js-CGD-b_vR.js.map +1 -0
- package/build/server/chunks/chunks/{core.js-DDntpb72.js → core.js-xfrkaQpS.js} +97 -48
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +1 -0
- package/build/server/chunks/chunks/{exports.js-CLTzaOxn.js → exports.js-B4E0NJIt.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-CLTzaOxn.js.map → exports.js-B4E0NJIt.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-Dxoa-jtP.js → http.js-Bu-Ao0Vk.js} +2 -2
- package/build/server/chunks/chunks/{http.js-Dxoa-jtP.js.map → http.js-Bu-Ao0Vk.js.map} +1 -1
- package/build/server/chunks/chunks/{internal.js-BDBSzJId.js → internal.js-ClSxY3uX.js} +3 -3
- package/build/server/chunks/chunks/{internal.js-BDBSzJId.js.map → internal.js-ClSxY3uX.js.map} +1 -1
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{library-store.js-BP8cL-qD.js → library-store.js-CZCDpai0.js} +187 -65
- package/build/server/chunks/chunks/library-store.js-CZCDpai0.js.map +1 -0
- package/build/server/chunks/chunks/{shared.js-BWXgl1Xf.js → shared.js-CHQOW_GN.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-BWXgl1Xf.js.map → shared.js-CHQOW_GN.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-9oI5Oz0w.js → utils.js-B9L6I-_K.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-9oI5Oz0w.js.map → utils.js-B9L6I-_K.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-WknZVW_S.js → _server.ts.js-PO5RpNzP.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-WknZVW_S.js.map → _server.ts.js-PO5RpNzP.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BrZ__-HZ.js → _server.ts.js-BmgqGEXs.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BrZ__-HZ.js.map → _server.ts.js-BmgqGEXs.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/_server.ts.js-DnTOlfHs.js +35 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/_server.ts.js-DnTOlfHs.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-PqADGPXc.js → _server.ts.js-DzdC6OJE.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-PqADGPXc.js.map → _server.ts.js-DzdC6OJE.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-OqoE5PN-.js → _server.ts.js-DXJNoYAs.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-OqoE5PN-.js.map → _server.ts.js-DXJNoYAs.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-CJ08e1ho.js → _server.ts.js-CqdKXoZL.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-CJ08e1ho.js.map → _server.ts.js-CqdKXoZL.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DdF3eK2I.js → _server.ts.js-DIuY66jG.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DdF3eK2I.js.map → _server.ts.js-DIuY66jG.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-Cxt6jsJ5.js → _server.ts.js-D9p-cdJQ.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-Cxt6jsJ5.js.map → _server.ts.js-D9p-cdJQ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-BWVM2Ntl.js → _server.ts.js-DP1x-xAW.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-BWVM2Ntl.js.map → _server.ts.js-DP1x-xAW.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-Z2bM-qHM.js → _server.ts.js-r1eNMvPd.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-Z2bM-qHM.js.map → _server.ts.js-r1eNMvPd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-BX9H2Z34.js → _server.ts.js-D-m3BwRj.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-BX9H2Z34.js.map → _server.ts.js-D-m3BwRj.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-BWHDZ8mz.js → _server.ts.js-3nyAQ3f0.js} +5 -5
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-BWHDZ8mz.js.map → _server.ts.js-3nyAQ3f0.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-CR_7mndO.js → _server.ts.js-Ba285FiJ.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-CR_7mndO.js.map → _server.ts.js-Ba285FiJ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-Cw1PDBvb.js → _server.ts.js-X354l2NP.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-Cw1PDBvb.js.map → _server.ts.js-X354l2NP.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-BHoE0yBi.js → hooks.server.js-eZ7H27L4.js} +5 -5
- package/build/server/chunks/entries/{hooks.server.js-BHoE0yBi.js.map → hooks.server.js-eZ7H27L4.js.map} +1 -1
- package/build/server/chunks/{handler-10cx4zHn.js → handler-Dcc_-rVh.js} +5 -5
- package/build/server/chunks/{handler-10cx4zHn.js.map → handler-Dcc_-rVh.js.map} +1 -1
- package/build/server/chunks/{index.js-DvPfiC00.js → index.js-DoVHd4mf.js} +5 -5
- package/build/server/chunks/{index.js-DvPfiC00.js.map → index.js-DoVHd4mf.js.map} +1 -1
- package/build/server/chunks/{manifest.js-DoPjUnzD.js → manifest.js-C_MsDhaa.js} +27 -20
- package/build/server/chunks/manifest.js-C_MsDhaa.js.map +1 -0
- package/build/server/chunks/nodes/0.js-BP6hHAK9.js +11 -0
- package/build/server/chunks/nodes/{0.js-Di3qgAtW.js.map → 0.js-BP6hHAK9.js.map} +1 -1
- package/build/server/chunks/nodes/1.js-DKlcrAQP.js +7 -0
- package/build/server/chunks/nodes/{1.js-BlWSFIA8.js.map → 1.js-DKlcrAQP.js.map} +1 -1
- package/build/server/chunks/nodes/2.js-B-8TTZH_.js +7 -0
- package/build/server/chunks/nodes/2.js-B-8TTZH_.js.map +1 -0
- package/build/server/chunks/nodes/3.js-Cf7H6K5p.js +7 -0
- package/build/server/chunks/nodes/3.js-Cf7H6K5p.js.map +1 -0
- package/build/server/chunks/nodes/4.js-JQQZnHWZ.js +7 -0
- package/build/server/chunks/nodes/4.js-JQQZnHWZ.js.map +1 -0
- package/dist/cli.js +34 -8
- package/dist/core/api-types.d.ts +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/ingest.d.ts +9 -1
- package/dist/core/ingest.js +10 -19
- package/dist/core/plugins/registry.d.ts +16 -0
- package/dist/core/plugins/registry.js +52 -0
- package/dist/runtime/kind-handlers.d.ts +0 -6
- package/dist/runtime/kind-handlers.js +16 -7
- package/dist/server/cli-commands.d.ts +2 -1
- package/dist/server/cli-commands.js +42 -1
- package/dist/server/cli-init.d.ts +1 -1
- package/dist/server/cli-init.js +23 -5
- package/dist/server/config-loader.d.ts +3 -2
- package/dist/server/config-loader.js +24 -48
- package/dist/server/index.d.ts +2 -1
- package/dist/server/library-store.d.ts +11 -0
- package/dist/server/library-store.js +57 -0
- package/dist/server/operations.d.ts +3 -1
- package/dist/server/operations.js +5 -1
- package/dist/server/plugins/audio.d.ts +2 -1
- package/dist/server/plugins/audio.js +15 -1
- package/dist/server/plugins/chroma-key-video.js +2 -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 +17 -3
- package/dist/server/plugins/image.d.ts +2 -1
- package/dist/server/plugins/image.js +14 -1
- package/dist/server/plugins/slice.d.ts +2 -1
- package/dist/server/plugins/slice.js +14 -1
- package/dist/server/plugins/video.d.ts +2 -1
- package/dist/server/plugins/video.js +26 -1
- package/dist/server/plugins.d.ts +6 -6
- package/dist/server/plugins.js +3 -1
- package/dist/server/start.d.ts +15 -1
- package/dist/server/start.js +84 -5
- package/package.json +3 -1
- package/build/client/_app/immutable/assets/2.wavcNhPC.css.br +0 -0
- package/build/client/_app/immutable/assets/2.wavcNhPC.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.css +0 -1
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.css.br +0 -0
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.css.gz +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.zyZtpKzC.css +0 -1
- package/build/client/_app/immutable/assets/LibraryContextMenu.zyZtpKzC.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.zyZtpKzC.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BiIto6ft.js.br +0 -0
- package/build/client/_app/immutable/chunks/BiIto6ft.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CO4RBJfa.js +0 -1
- 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/CvY6YsFE.js.br +0 -0
- package/build/client/_app/immutable/chunks/CvY6YsFE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DL0_39Ys.js +0 -5
- package/build/client/_app/immutable/chunks/DL0_39Ys.js.br +0 -0
- package/build/client/_app/immutable/chunks/DL0_39Ys.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DQ8SXFaX.js.br +0 -0
- package/build/client/_app/immutable/chunks/DQ8SXFaX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DjdrdRzT.js +0 -1
- package/build/client/_app/immutable/chunks/DjdrdRzT.js.br +0 -0
- package/build/client/_app/immutable/chunks/DjdrdRzT.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.ubN0v9bN.js.br +0 -0
- package/build/client/_app/immutable/entry/app.ubN0v9bN.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js +0 -1
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Dr5ijCIl.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Dr5ijCIl.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.DiIGIRAb.js.br +0 -3
- package/build/client/_app/immutable/nodes/1.DiIGIRAb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.B8KYmQcV.js +0 -3
- package/build/client/_app/immutable/nodes/2.B8KYmQcV.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.B8KYmQcV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js +0 -104
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.js +0 -2
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.js.gz +0 -0
- package/build/server/chunks/chunks/context.js-Bw4WWLhG.js.map +0 -1
- package/build/server/chunks/chunks/core.js-DDntpb72.js.map +0 -1
- package/build/server/chunks/chunks/library-store.js-BP8cL-qD.js.map +0 -1
- package/build/server/chunks/manifest.js-DoPjUnzD.js.map +0 -1
- package/build/server/chunks/nodes/0.js-Di3qgAtW.js +0 -11
- package/build/server/chunks/nodes/1.js-BlWSFIA8.js +0 -7
- package/build/server/chunks/nodes/2.js-CO9nSPUN.js +0 -7
- package/build/server/chunks/nodes/2.js-CO9nSPUN.js.map +0 -1
- package/build/server/chunks/nodes/3.js-C2D6xpEO.js +0 -7
- package/build/server/chunks/nodes/3.js-C2D6xpEO.js.map +0 -1
- package/build/server/chunks/nodes/4.js-B3IuagJW.js +0 -7
- package/build/server/chunks/nodes/4.js-B3IuagJW.js.map +0 -1
|
@@ -202,6 +202,7 @@ Subcommands:
|
|
|
202
202
|
list list assets (--manifest, --type, --warnings-only)
|
|
203
203
|
show one asset in detail
|
|
204
204
|
ingest copy a file into the raw dir as a typed asset
|
|
205
|
+
replace replace an asset's raw file while preserving its metadata
|
|
205
206
|
set merge parameters and/or replace manifest membership
|
|
206
207
|
rename rename an asset id
|
|
207
208
|
move move an asset to a folder or root
|
|
@@ -285,6 +286,22 @@ Example:
|
|
|
285
286
|
fundus asset ingest ./exports/navigation_panel@2x.png --type slice \\
|
|
286
287
|
--manifests mainCatalog --json
|
|
287
288
|
`;
|
|
289
|
+
export const ASSET_REPLACE_HELP = `fundus asset replace — replace an asset's raw file
|
|
290
|
+
|
|
291
|
+
Usage: fundus asset replace <id> <file> [--json]
|
|
292
|
+
|
|
293
|
+
Replaces the stored raw file and content hash while preserving the asset's
|
|
294
|
+
id, type, folder, parameters, references, and manifest memberships. The new
|
|
295
|
+
file must have an extension accepted by the existing asset type. Runs the full
|
|
296
|
+
autosave chain, so delivered assets are reprocessed and manifests regenerated.
|
|
297
|
+
|
|
298
|
+
Flags:
|
|
299
|
+
--json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
|
|
300
|
+
--help this help
|
|
301
|
+
|
|
302
|
+
Example:
|
|
303
|
+
fundus asset replace navigationPanel ./exports/navigation_panel_v2.png --json
|
|
304
|
+
`;
|
|
288
305
|
export const ASSET_SET_HELP = `fundus asset set — update an asset's parameters and/or memberships
|
|
289
306
|
|
|
290
307
|
Usage: fundus asset set <id> [--parameters <json>] [--manifests <a,b>] [--json]
|
|
@@ -368,6 +385,7 @@ const ASSET_SUBCOMMAND_HELP = {
|
|
|
368
385
|
list: ASSET_LIST_HELP,
|
|
369
386
|
show: ASSET_SHOW_HELP,
|
|
370
387
|
ingest: ASSET_INGEST_HELP,
|
|
388
|
+
replace: ASSET_REPLACE_HELP,
|
|
371
389
|
set: ASSET_SET_HELP,
|
|
372
390
|
rename: ASSET_RENAME_HELP,
|
|
373
391
|
move: ASSET_MOVE_HELP,
|
|
@@ -490,6 +508,27 @@ async function runAssetIngest(argumentList) {
|
|
|
490
508
|
}
|
|
491
509
|
printMutationResult(`Ingested "${fileName}" as ${type} asset "${id}".`, asset, state);
|
|
492
510
|
}
|
|
511
|
+
async function runAssetReplace(argumentList) {
|
|
512
|
+
const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset replace');
|
|
513
|
+
const [id, filePath] = requirePositionals(parsed, ['<id>', '<file>'], 'fundus asset replace');
|
|
514
|
+
const absolutePath = resolve(process.cwd(), filePath);
|
|
515
|
+
let bytes;
|
|
516
|
+
try {
|
|
517
|
+
bytes = new Uint8Array(await readFile(absolutePath));
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
throw new Error(`Cannot read file "${filePath}".`);
|
|
521
|
+
}
|
|
522
|
+
const fileName = basename(absolutePath);
|
|
523
|
+
const operations = await createOperations();
|
|
524
|
+
const state = await operations.replaceRawFile(id, { fileName, bytes });
|
|
525
|
+
const asset = assetById(state, id);
|
|
526
|
+
if (parsed.flags.json) {
|
|
527
|
+
printJsonDocument({ ok: true, asset, issues: state.issues });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
printMutationResult(`Replaced raw file for asset "${id}" with "${fileName}".`, asset, state);
|
|
531
|
+
}
|
|
493
532
|
async function runAssetSet(argumentList) {
|
|
494
533
|
const parsed = parseArguments(argumentList, { ...COMMON_FLAGS, parameters: { takesValue: true }, manifests: { takesValue: true } }, 'fundus asset set');
|
|
495
534
|
const [id] = requirePositionals(parsed, ['<id>'], 'fundus asset set');
|
|
@@ -554,7 +593,7 @@ export async function runAssetCommand(argumentList) {
|
|
|
554
593
|
if (subcommand === undefined || subcommand === '--help') {
|
|
555
594
|
console.log(ASSET_HELP);
|
|
556
595
|
if (subcommand === undefined) {
|
|
557
|
-
throw new CliUsageError('`fundus asset` needs a subcommand: list, show, ingest, set, rename, move, delete.');
|
|
596
|
+
throw new CliUsageError('`fundus asset` needs a subcommand: list, show, ingest, replace, set, rename, move, delete.');
|
|
558
597
|
}
|
|
559
598
|
return;
|
|
560
599
|
}
|
|
@@ -570,6 +609,8 @@ export async function runAssetCommand(argumentList) {
|
|
|
570
609
|
return runAssetShow(rest);
|
|
571
610
|
case 'ingest':
|
|
572
611
|
return runAssetIngest(rest);
|
|
612
|
+
case 'replace':
|
|
613
|
+
return runAssetReplace(rest);
|
|
573
614
|
case 'set':
|
|
574
615
|
return runAssetSet(rest);
|
|
575
616
|
case 'rename':
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const FUNDUS_CONFIG_TEMPLATE = "import { defineConfig } from 'fundus/config';\n\nexport default defineConfig({\n\trawDir: './assets/raw',\n\tproxyDir: './static/fundus',\n\tproxyBasePath: '/fundus/',\n\tlibrary: './assets/fundus.library.json',\n\tmanifestsDir: './src/lib/fundus'\n});\n";
|
|
2
|
-
export declare const INIT_HELP = "fundus init \u2014 create a Fundus configuration file\n\nUsage: fundus init [--json]\n\nCreates a template fundus.config.ts in the current directory. The command
|
|
2
|
+
export declare const INIT_HELP = "fundus init \u2014 create a Fundus configuration file\n\nUsage: fundus init [--json]\n\nCreates a template fundus.config.ts and a library with a default \"main\"\nmanifest in the current directory. The command refuses to initialize a project\nthat already has a Fundus configuration file.\n\nFlags:\n --json print {\"ok\": true, \"path\": \"fundus.config.ts\"} on stdout\n --help this help\n\nExample:\n cd my-host-app && npx fundus init\n";
|
|
3
3
|
export declare function runInitCommand(argumentList: string[]): Promise<void>;
|
package/dist/server/cli-init.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { writeFile } from 'node:fs/promises';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
1
|
+
import { mkdir, unlink, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { createEmptyLibrary, serializeLibrary } from '../core/index.js';
|
|
3
4
|
import { parseArguments, printJsonDocument, requirePositionals } from './cli-io.js';
|
|
4
5
|
export const FUNDUS_CONFIG_TEMPLATE = `import { defineConfig } from 'fundus/config';
|
|
5
6
|
|
|
@@ -15,8 +16,9 @@ export const INIT_HELP = `fundus init — create a Fundus configuration file
|
|
|
15
16
|
|
|
16
17
|
Usage: fundus init [--json]
|
|
17
18
|
|
|
18
|
-
Creates a template fundus.config.ts
|
|
19
|
-
refuses to
|
|
19
|
+
Creates a template fundus.config.ts and a library with a default "main"
|
|
20
|
+
manifest in the current directory. The command refuses to initialize a project
|
|
21
|
+
that already has a Fundus configuration file.
|
|
20
22
|
|
|
21
23
|
Flags:
|
|
22
24
|
--json print {"ok": true, "path": "fundus.config.ts"} on stdout
|
|
@@ -45,9 +47,25 @@ export async function runInitCommand(argumentList) {
|
|
|
45
47
|
}
|
|
46
48
|
throw new Error(`Cannot create fundus.config.ts: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
47
49
|
}
|
|
50
|
+
const libraryPath = resolve(process.cwd(), 'assets/fundus.library.json');
|
|
51
|
+
const library = createEmptyLibrary();
|
|
52
|
+
library.manifests.main = {};
|
|
53
|
+
try {
|
|
54
|
+
await mkdir(dirname(libraryPath), { recursive: true });
|
|
55
|
+
await writeFile(libraryPath, serializeLibrary(library), { encoding: 'utf8', flag: 'wx' });
|
|
56
|
+
}
|
|
57
|
+
catch (cause) {
|
|
58
|
+
// The config was created by this invocation. Remove it so a failed
|
|
59
|
+
// library scaffold does not make the project look initialized.
|
|
60
|
+
await unlink(configPath).catch(() => { });
|
|
61
|
+
if (cause.code === 'EEXIST') {
|
|
62
|
+
throw new Error(`assets/fundus.library.json already exists in ${process.cwd()}.`);
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Cannot create assets/fundus.library.json: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
65
|
+
}
|
|
48
66
|
if (parsed.flags.json) {
|
|
49
67
|
printJsonDocument({ ok: true, path: 'fundus.config.ts' });
|
|
50
68
|
return;
|
|
51
69
|
}
|
|
52
|
-
console.log('Created fundus.config.ts.');
|
|
70
|
+
console.log('Created fundus.config.ts and default "main" manifest.');
|
|
53
71
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AudioPreset, ImagePreset, VideoPreset } from '../core/index.js';
|
|
2
|
+
import type { ServerPluginRegistry } from './plugins/contract.js';
|
|
2
3
|
export interface ResolvedPresetPluginConfig {
|
|
3
4
|
presets: Record<string, ImagePreset>;
|
|
4
5
|
}
|
|
@@ -29,6 +30,6 @@ export interface ResolvedFundusConfig {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
/** Pure resolution step (testable without a config file on disk). */
|
|
32
|
-
export declare function resolveConfig(projectRoot: string, value: unknown): ResolvedFundusConfig;
|
|
33
|
+
export declare function resolveConfig(projectRoot: string, value: unknown, registry?: ServerPluginRegistry): ResolvedFundusConfig;
|
|
33
34
|
/** Load `fundus.config.ts` from the host project root. */
|
|
34
35
|
export declare function loadConfig(projectRoot: string): Promise<ResolvedFundusConfig>;
|
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
* absolute, and merges the built-in plugin preset defaults under the host's.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync } from 'node:fs';
|
|
7
|
-
import {
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
8
|
import { createJiti } from 'jiti';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
-
}
|
|
9
|
+
import { serverPluginsByType } from './plugins.js';
|
|
10
|
+
import { isRecord } from './plugins/config.js';
|
|
13
11
|
function requiredString(config, key) {
|
|
14
12
|
const value = config[key];
|
|
15
13
|
if (typeof value !== 'string' || value.length === 0) {
|
|
@@ -18,37 +16,23 @@ function requiredString(config, key) {
|
|
|
18
16
|
return value;
|
|
19
17
|
}
|
|
20
18
|
function resolvePath(projectRoot, value) {
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
function mergePresets(label, builtIn, pluginConfig, validatePreset) {
|
|
24
|
-
const merged = { ...builtIn };
|
|
25
|
-
if (pluginConfig === undefined)
|
|
26
|
-
return merged;
|
|
27
|
-
if (!isRecord(pluginConfig)) {
|
|
28
|
-
throw new Error(`fundus.config: "plugins.${label}" must be an object.`);
|
|
29
|
-
}
|
|
30
|
-
if (pluginConfig.presets === undefined)
|
|
31
|
-
return merged;
|
|
32
|
-
if (!isRecord(pluginConfig.presets)) {
|
|
33
|
-
throw new Error(`fundus.config: "plugins.${label}.presets" must be an object.`);
|
|
34
|
-
}
|
|
35
|
-
for (const [name, preset] of Object.entries(pluginConfig.presets)) {
|
|
36
|
-
merged[name] = validatePreset(name, preset);
|
|
37
|
-
}
|
|
38
|
-
return merged;
|
|
19
|
+
return resolve(projectRoot, value);
|
|
39
20
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
function configDefinitionsFor(registry) {
|
|
22
|
+
const byNamespace = new Map();
|
|
23
|
+
for (const plugin of Object.values(registry)) {
|
|
24
|
+
for (const definition of plugin.configDefinitions) {
|
|
25
|
+
const existing = byNamespace.get(definition.namespace);
|
|
26
|
+
if (existing && existing !== definition) {
|
|
27
|
+
throw new Error(`Plugins provide conflicting config definitions for "plugins.${definition.namespace}".`);
|
|
28
|
+
}
|
|
29
|
+
byNamespace.set(definition.namespace, definition);
|
|
30
|
+
}
|
|
47
31
|
}
|
|
48
|
-
return
|
|
32
|
+
return [...byNamespace.values()];
|
|
49
33
|
}
|
|
50
34
|
/** Pure resolution step (testable without a config file on disk). */
|
|
51
|
-
export function resolveConfig(projectRoot, value) {
|
|
35
|
+
export function resolveConfig(projectRoot, value, registry = serverPluginsByType) {
|
|
52
36
|
if (!isRecord(value)) {
|
|
53
37
|
throw new Error('fundus.config must default-export a configuration object.');
|
|
54
38
|
}
|
|
@@ -60,28 +44,20 @@ export function resolveConfig(projectRoot, value) {
|
|
|
60
44
|
if (!isRecord(plugins)) {
|
|
61
45
|
throw new Error('fundus.config: "plugins" must be an object.');
|
|
62
46
|
}
|
|
47
|
+
const resolvedPlugins = {};
|
|
48
|
+
for (const definition of configDefinitionsFor(registry)) {
|
|
49
|
+
resolvedPlugins[definition.namespace] = definition.resolve({
|
|
50
|
+
projectRoot,
|
|
51
|
+
value: plugins[definition.namespace]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
63
54
|
return {
|
|
64
55
|
rawDir: resolvePath(projectRoot, requiredString(value, 'rawDir')),
|
|
65
56
|
proxyDir: resolvePath(projectRoot, requiredString(value, 'proxyDir')),
|
|
66
57
|
library: resolvePath(projectRoot, requiredString(value, 'library')),
|
|
67
58
|
manifestsDir: resolvePath(projectRoot, requiredString(value, 'manifestsDir')),
|
|
68
59
|
proxyBasePath,
|
|
69
|
-
plugins:
|
|
70
|
-
image: {
|
|
71
|
-
presets: mergePresets('image', IMAGE_BUILT_IN_PRESETS, plugins.image, validateImagePreset)
|
|
72
|
-
},
|
|
73
|
-
slice: {
|
|
74
|
-
presets: mergePresets('slice', SLICE_BUILT_IN_PRESETS, plugins.slice, validateImagePreset)
|
|
75
|
-
},
|
|
76
|
-
video: {
|
|
77
|
-
presets: mergePresets('video', VIDEO_BUILT_IN_PRESETS, plugins.video, validateVideoPreset),
|
|
78
|
-
ffmpegPath: binaryPathOverride(projectRoot, plugins.video, 'ffmpegPath'),
|
|
79
|
-
ffprobePath: binaryPathOverride(projectRoot, plugins.video, 'ffprobePath')
|
|
80
|
-
},
|
|
81
|
-
audio: {
|
|
82
|
-
presets: mergePresets('audio', AUDIO_BUILT_IN_PRESETS, plugins.audio, validateAudioPreset)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
60
|
+
plugins: resolvedPlugins
|
|
85
61
|
};
|
|
86
62
|
}
|
|
87
63
|
/** Load `fundus.config.ts` from the host project root. */
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { ResolvedFundusConfig, ResolvedPresetPluginConfig, ResolvedVideoPluginConfig, ResolvedAudioPluginConfig } from './config-loader.js';
|
|
2
2
|
export { loadConfig, resolveConfig } from './config-loader.js';
|
|
3
3
|
export { LibraryStore } from './library-store.js';
|
|
4
|
-
export type { IngestOptions } from './library-store.js';
|
|
4
|
+
export type { IngestOptions, ReplaceRawFileOptions } from './library-store.js';
|
|
5
5
|
export type { AssetProxyInfo, ProxyOutputInfo, ProcessingReport } from './processing/processor.js';
|
|
6
6
|
export { processAssets, expectedProxyFileName, expectedProxyRelativePath, deliveredAssetIds, isProxyFileName, readProxyOutputInfo } from './processing/processor.js';
|
|
7
7
|
export { ffmpegBinaryPath, runToolchainBinary } from './processing/ffmpeg.js';
|
|
@@ -13,4 +13,5 @@ export type { CheckFinding, CheckReport } from './check.js';
|
|
|
13
13
|
export { formatCheckFinding, runCheck } from './check.js';
|
|
14
14
|
export type { AssetChangeOptions } from './operations.js';
|
|
15
15
|
export { FundusOperations } from './operations.js';
|
|
16
|
+
export type { RunStartOptions } from './start.js';
|
|
16
17
|
export { runStart } from './start.js';
|
|
@@ -23,6 +23,11 @@ export interface IngestOptions {
|
|
|
23
23
|
/** Registered destination folder; empty string means the implicit root. */
|
|
24
24
|
folder?: string;
|
|
25
25
|
}
|
|
26
|
+
export interface ReplaceRawFileOptions {
|
|
27
|
+
/** Original file name of the replacement (sanitized before storing). */
|
|
28
|
+
fileName: string;
|
|
29
|
+
bytes: Uint8Array;
|
|
30
|
+
}
|
|
26
31
|
export declare class LibraryStore {
|
|
27
32
|
#private;
|
|
28
33
|
constructor(config: ResolvedFundusConfig, registry?: ServerPluginRegistry);
|
|
@@ -37,6 +42,12 @@ export declare class LibraryStore {
|
|
|
37
42
|
* the owning plugin's default parameters. Returns the updated library.
|
|
38
43
|
*/
|
|
39
44
|
ingest(options: IngestOptions): Promise<Library>;
|
|
45
|
+
/**
|
|
46
|
+
* Replace one asset's raw file while preserving its identity and authored
|
|
47
|
+
* metadata. The replacement stays in the current folder, receives a safe
|
|
48
|
+
* non-colliding name, and must be accepted by the asset's existing type.
|
|
49
|
+
*/
|
|
50
|
+
replaceRawFile(id: string, options: ReplaceRawFileOptions): Promise<Library>;
|
|
40
51
|
/** Replace an asset's parameters (validated by the owning plugin). */
|
|
41
52
|
updateParameters(id: string, parameters: unknown): Promise<Library>;
|
|
42
53
|
/** Replace an asset's manifest membership (every name must exist). */
|
|
@@ -136,6 +136,63 @@ export class LibraryStore {
|
|
|
136
136
|
await this.write(library);
|
|
137
137
|
return library;
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Replace one asset's raw file while preserving its identity and authored
|
|
141
|
+
* metadata. The replacement stays in the current folder, receives a safe
|
|
142
|
+
* non-colliding name, and must be accepted by the asset's existing type.
|
|
143
|
+
*/
|
|
144
|
+
async replaceRawFile(id, options) {
|
|
145
|
+
return this.#enqueue(() => this.#replaceRawFile(id, options));
|
|
146
|
+
}
|
|
147
|
+
async #replaceRawFile(id, options) {
|
|
148
|
+
const library = await this.read();
|
|
149
|
+
const record = this.#requireAsset(library, id);
|
|
150
|
+
const sanitizedName = sanitizeFileName(options.fileName);
|
|
151
|
+
const extension = extname(sanitizedName).slice(1).toLowerCase();
|
|
152
|
+
const plugin = serverPluginForType(record.type, this.#registry);
|
|
153
|
+
if (!plugin.descriptor.accepts.includes(extension)) {
|
|
154
|
+
throw new Error(`The ${record.type} type does not accept ".${extension}" files (accepts: ${plugin.descriptor.accepts.join(', ')}).`);
|
|
155
|
+
}
|
|
156
|
+
const previousRaw = record.raw;
|
|
157
|
+
const previousHash = record.rawHash;
|
|
158
|
+
const folder = folderOfRawPath(previousRaw);
|
|
159
|
+
const previousName = rawFileNameOfPath(previousRaw);
|
|
160
|
+
const storedName = sanitizedName === previousName ? previousName : this.#uniqueRawName(sanitizedName, folder);
|
|
161
|
+
const nextRaw = folder === '' ? storedName : `${folder}/${storedName}`;
|
|
162
|
+
const previousPath = join(this.#config.rawDir, previousRaw);
|
|
163
|
+
const nextPath = join(this.#config.rawDir, nextRaw);
|
|
164
|
+
const token = crypto.randomUUID();
|
|
165
|
+
const temporaryPath = join(this.#config.rawDir, folder, `.fundus-replace-${token}.tmp`);
|
|
166
|
+
const backupPath = `${previousPath}.fundus-replace-${token}`;
|
|
167
|
+
let previousStaged = false;
|
|
168
|
+
let replacementInstalled = false;
|
|
169
|
+
await mkdir(dirname(temporaryPath), { recursive: true });
|
|
170
|
+
try {
|
|
171
|
+
await writeFile(temporaryPath, options.bytes);
|
|
172
|
+
if (existsSync(previousPath)) {
|
|
173
|
+
await rename(previousPath, backupPath);
|
|
174
|
+
previousStaged = true;
|
|
175
|
+
}
|
|
176
|
+
await rename(temporaryPath, nextPath);
|
|
177
|
+
replacementInstalled = true;
|
|
178
|
+
record.raw = nextRaw;
|
|
179
|
+
record.rawHash = sha256Hex(options.bytes);
|
|
180
|
+
await this.write(library);
|
|
181
|
+
}
|
|
182
|
+
catch (cause) {
|
|
183
|
+
record.raw = previousRaw;
|
|
184
|
+
record.rawHash = previousHash;
|
|
185
|
+
if (replacementInstalled)
|
|
186
|
+
await unlink(nextPath).catch(() => { });
|
|
187
|
+
if (previousStaged)
|
|
188
|
+
await rename(backupPath, previousPath);
|
|
189
|
+
await unlink(temporaryPath).catch(() => { });
|
|
190
|
+
throw cause;
|
|
191
|
+
}
|
|
192
|
+
if (previousStaged)
|
|
193
|
+
await unlink(backupPath).catch(() => { });
|
|
194
|
+
return library;
|
|
195
|
+
}
|
|
139
196
|
/** Replace an asset's parameters (validated by the owning plugin). */
|
|
140
197
|
async updateParameters(id, parameters) {
|
|
141
198
|
return this.#enqueue(() => this.#updateParameters(id, parameters));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type FundusStateDto, type PatchAssetRequestDto, type PatchManifestRequestDto } from '../core/index.js';
|
|
2
2
|
import type { ResolvedFundusConfig } from './config-loader.js';
|
|
3
|
-
import { LibraryStore, type IngestOptions } from './library-store.js';
|
|
3
|
+
import { LibraryStore, type IngestOptions, type ReplaceRawFileOptions } from './library-store.js';
|
|
4
4
|
import type { ServerPluginRegistry } from './plugins/contract.js';
|
|
5
5
|
/**
|
|
6
6
|
* The CLI's `--parameters` merge semantics (M7): a SHALLOW merge at the top
|
|
@@ -37,6 +37,8 @@ export declare class FundusOperations {
|
|
|
37
37
|
/** Apply a PATCH body: parameters and/or manifests and/or a rename — in that order. */
|
|
38
38
|
patchAsset(id: string, body: PatchAssetRequestDto): Promise<FundusStateDto>;
|
|
39
39
|
moveAsset(id: string, folder: string): Promise<FundusStateDto>;
|
|
40
|
+
/** Replace the raw bytes/name and run the normal processing + codegen tail. */
|
|
41
|
+
replaceRawFile(id: string, options: ReplaceRawFileOptions): Promise<FundusStateDto>;
|
|
40
42
|
/** Move a selected asset set as one editor action and roll back on any failure. */
|
|
41
43
|
moveAssets(assetIds: string[], folder: string): Promise<FundusStateDto>;
|
|
42
44
|
createFolder(path: string): Promise<FundusStateDto>;
|
|
@@ -125,6 +125,10 @@ export class FundusOperations {
|
|
|
125
125
|
async moveAsset(id, folder) {
|
|
126
126
|
return this.#afterMutation(await this.#store.moveAsset(id, folder));
|
|
127
127
|
}
|
|
128
|
+
/** Replace the raw bytes/name and run the normal processing + codegen tail. */
|
|
129
|
+
async replaceRawFile(id, options) {
|
|
130
|
+
return this.#afterMutation(await this.#store.replaceRawFile(id, options));
|
|
131
|
+
}
|
|
128
132
|
/** Move a selected asset set as one editor action and roll back on any failure. */
|
|
129
133
|
async moveAssets(assetIds, folder) {
|
|
130
134
|
const uniqueIds = [...new Set(assetIds)];
|
|
@@ -334,7 +338,7 @@ export class FundusOperations {
|
|
|
334
338
|
type: record.type,
|
|
335
339
|
raw: record.raw,
|
|
336
340
|
folder: folderOfRawPath(record.raw),
|
|
337
|
-
rawUrl: `/files/raw/${encodeRelativePathForUrl(record.raw)}`,
|
|
341
|
+
rawUrl: `/files/raw/${encodeRelativePathForUrl(record.raw)}?v=${record.rawHash.slice(0, 12)}`,
|
|
338
342
|
rawHash: record.rawHash,
|
|
339
343
|
parameters: record.parameters,
|
|
340
344
|
manifests: record.manifests,
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
* No dimensions — the proxy's only output metadata is its duration.
|
|
4
4
|
*/
|
|
5
5
|
import { type AudioParameters, type AudioPreset } from '../../core/index.js';
|
|
6
|
-
import { type ServerAssetTypePlugin } from './contract.js';
|
|
6
|
+
import { type ServerAssetTypePlugin, type ServerPluginConfigDefinition } from './contract.js';
|
|
7
|
+
export declare const audioConfigDefinition: ServerPluginConfigDefinition;
|
|
7
8
|
export declare const audioServerPlugin: ServerAssetTypePlugin<AudioParameters, AudioPreset>;
|
|
@@ -2,12 +2,26 @@
|
|
|
2
2
|
* Audio plugin (server part): one ffmpeg transcode per preset.
|
|
3
3
|
* No dimensions — the proxy's only output metadata is its duration.
|
|
4
4
|
*/
|
|
5
|
-
import { audioPlugin } from '../../core/index.js';
|
|
5
|
+
import { AUDIO_BUILT_IN_PRESETS, audioPlugin, validateAudioPreset } from '../../core/index.js';
|
|
6
6
|
import { processAudioFile } from '../processing/audio-processing.js';
|
|
7
7
|
import { probeMedia } from '../processing/media-probe.js';
|
|
8
|
+
import { mergePresets } from './config.js';
|
|
8
9
|
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
10
|
+
import { videoConfigDefinition } from './video.js';
|
|
11
|
+
export const audioConfigDefinition = {
|
|
12
|
+
namespace: 'audio',
|
|
13
|
+
resolve: ({ value }) => ({
|
|
14
|
+
presets: mergePresets({
|
|
15
|
+
label: 'audio',
|
|
16
|
+
builtIn: AUDIO_BUILT_IN_PRESETS,
|
|
17
|
+
pluginConfig: value,
|
|
18
|
+
validatePreset: validateAudioPreset
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
};
|
|
9
22
|
export const audioServerPlugin = {
|
|
10
23
|
descriptor: audioPlugin,
|
|
24
|
+
configDefinitions: [audioConfigDefinition, videoConfigDefinition],
|
|
11
25
|
pluginConfig: (config) => config.plugins.audio,
|
|
12
26
|
presets: (config) => config.plugins.audio.presets,
|
|
13
27
|
resolvePreset: (parameters, config) => resolveNamedPreset('audio', parameters.preset, config.plugins.audio.presets),
|
|
@@ -8,8 +8,10 @@ import { chromaKeyVideoPlugin } from '../../core/index.js';
|
|
|
8
8
|
import { probeMedia } from '../processing/media-probe.js';
|
|
9
9
|
import { processVideoFile } from '../processing/video-processing.js';
|
|
10
10
|
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
11
|
+
import { videoConfigDefinition } from './video.js';
|
|
11
12
|
export const chromaKeyVideoServerPlugin = {
|
|
12
13
|
descriptor: chromaKeyVideoPlugin,
|
|
14
|
+
configDefinitions: [videoConfigDefinition],
|
|
13
15
|
// One preset namespace with the video plugin — the config slice,
|
|
14
16
|
// the preset map, and the resolution label are video's.
|
|
15
17
|
pluginConfig: (config) => config.plugins.video,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function mergePresets<TPreset>(options: {
|
|
3
|
+
label: string;
|
|
4
|
+
builtIn: Record<string, TPreset>;
|
|
5
|
+
pluginConfig: unknown;
|
|
6
|
+
validatePreset: (name: string, value: unknown) => TPreset;
|
|
7
|
+
}): Record<string, TPreset>;
|
|
8
|
+
export declare function binaryPathOverride(options: {
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
pluginConfig: unknown;
|
|
11
|
+
label: string;
|
|
12
|
+
key: string;
|
|
13
|
+
}): string | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
2
|
+
export function isRecord(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
export function mergePresets(options) {
|
|
6
|
+
const { label, builtIn, pluginConfig, validatePreset } = options;
|
|
7
|
+
const merged = { ...builtIn };
|
|
8
|
+
if (pluginConfig === undefined)
|
|
9
|
+
return merged;
|
|
10
|
+
if (!isRecord(pluginConfig)) {
|
|
11
|
+
throw new Error(`fundus.config: "plugins.${label}" must be an object.`);
|
|
12
|
+
}
|
|
13
|
+
if (pluginConfig.presets === undefined)
|
|
14
|
+
return merged;
|
|
15
|
+
if (!isRecord(pluginConfig.presets)) {
|
|
16
|
+
throw new Error(`fundus.config: "plugins.${label}.presets" must be an object.`);
|
|
17
|
+
}
|
|
18
|
+
for (const [name, preset] of Object.entries(pluginConfig.presets)) {
|
|
19
|
+
merged[name] = validatePreset(name, preset);
|
|
20
|
+
}
|
|
21
|
+
return merged;
|
|
22
|
+
}
|
|
23
|
+
export function binaryPathOverride(options) {
|
|
24
|
+
const { projectRoot, pluginConfig, label, key } = options;
|
|
25
|
+
if (!isRecord(pluginConfig) || pluginConfig[key] === undefined)
|
|
26
|
+
return null;
|
|
27
|
+
const value = pluginConfig[key];
|
|
28
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
29
|
+
throw new Error(`fundus.config: "plugins.${label}.${key}" must be a non-empty string.`);
|
|
30
|
+
}
|
|
31
|
+
return isAbsolute(value) ? value : resolve(projectRoot, value);
|
|
32
|
+
}
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* config slice, preset resolution, the processing step (sharp/ffmpeg), proxy
|
|
5
5
|
* probing, and the manifest-entry builder for codegen. The orchestrators
|
|
6
6
|
* (processor, codegen, check, store, operations) are type-agnostic and consult
|
|
7
|
-
* only the registry (plugins.ts)
|
|
8
|
-
* one `ServerAssetTypePlugin` + one registry entry — nothing else server-side.
|
|
7
|
+
* only the projected server registry (plugins.ts).
|
|
9
8
|
*/
|
|
10
9
|
import type { AssetTypePluginDescriptor, ManifestEntryData } from '../../core/index.js';
|
|
11
10
|
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
@@ -47,9 +46,24 @@ export interface ManifestEntryInput<TParameters> {
|
|
|
47
46
|
src: string;
|
|
48
47
|
proxy: AssetProxyInfo;
|
|
49
48
|
}
|
|
49
|
+
/** One host-config namespace required by a server plugin. */
|
|
50
|
+
export interface ServerPluginConfigDefinition {
|
|
51
|
+
/** Key below `plugins` in fundus.config.ts. */
|
|
52
|
+
namespace: string;
|
|
53
|
+
/** Validate and resolve that namespace's host value. */
|
|
54
|
+
resolve(context: {
|
|
55
|
+
projectRoot: string;
|
|
56
|
+
value: unknown;
|
|
57
|
+
}): unknown;
|
|
58
|
+
}
|
|
50
59
|
export interface ServerAssetTypePlugin<TParameters = unknown, TPreset = unknown> {
|
|
51
60
|
/** The environment-free half of the plugin (type key, ingestion, validation). */
|
|
52
61
|
descriptor: AssetTypePluginDescriptor<TParameters, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Config namespaces this plugin needs. Shared definitions are deduplicated
|
|
64
|
+
* by identity (video, chroma-key-video, and audio share the video toolchain).
|
|
65
|
+
*/
|
|
66
|
+
configDefinitions: readonly ServerPluginConfigDefinition[];
|
|
53
67
|
/**
|
|
54
68
|
* The plugin's `plugins.<namespace>` config slice — handed to the
|
|
55
69
|
* descriptor's `defaultParameters` on ingest. Types may share a namespace
|
|
@@ -92,7 +106,7 @@ export interface ServerAssetTypePlugin<TParameters = unknown, TPreset = unknown>
|
|
|
92
106
|
*/
|
|
93
107
|
buildManifestEntry(input: ManifestEntryInput<TParameters>): ManifestEntryData;
|
|
94
108
|
}
|
|
95
|
-
/** The
|
|
109
|
+
/** The server projection consumed by type-agnostic orchestrators and tests. */
|
|
96
110
|
export type ServerPluginRegistry = Record<string, ServerAssetTypePlugin>;
|
|
97
111
|
/** Resolve a named preset out of a plugin's merged preset map, or throw listing the known names. */
|
|
98
112
|
export declare function resolveNamedPreset<TPreset>(label: string, presetName: string, presets: Record<string, TPreset>): TPreset;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Image plugin (server part): sharp scale + convert per preset.
|
|
3
3
|
*/
|
|
4
4
|
import { type ImageFormat, type ImageParameters, type ImagePreset } from '../../core/index.js';
|
|
5
|
-
import { type ServerAssetTypePlugin } from './contract.js';
|
|
5
|
+
import { type ServerAssetTypePlugin, type ServerPluginConfigDefinition } from './contract.js';
|
|
6
|
+
export declare const imageConfigDefinition: ServerPluginConfigDefinition;
|
|
6
7
|
/** The delivered file extension of an image-preset format. */
|
|
7
8
|
export declare function extensionForImageFormat(format: ImageFormat): string;
|
|
8
9
|
export declare const imageServerPlugin: ServerAssetTypePlugin<ImageParameters, ImagePreset>;
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image plugin (server part): sharp scale + convert per preset.
|
|
3
3
|
*/
|
|
4
|
-
import { imagePlugin } from '../../core/index.js';
|
|
4
|
+
import { IMAGE_BUILT_IN_PRESETS, imagePlugin, validateImagePreset } from '../../core/index.js';
|
|
5
5
|
import { processImageFile, readImageDimensions } from '../processing/image-processing.js';
|
|
6
|
+
import { mergePresets } from './config.js';
|
|
6
7
|
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
8
|
+
export const imageConfigDefinition = {
|
|
9
|
+
namespace: 'image',
|
|
10
|
+
resolve: ({ value }) => ({
|
|
11
|
+
presets: mergePresets({
|
|
12
|
+
label: 'image',
|
|
13
|
+
builtIn: IMAGE_BUILT_IN_PRESETS,
|
|
14
|
+
pluginConfig: value,
|
|
15
|
+
validatePreset: validateImagePreset
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
};
|
|
7
19
|
/** The delivered file extension of an image-preset format. */
|
|
8
20
|
export function extensionForImageFormat(format) {
|
|
9
21
|
return format === 'jpeg' ? 'jpg' : format;
|
|
10
22
|
}
|
|
11
23
|
export const imageServerPlugin = {
|
|
12
24
|
descriptor: imagePlugin,
|
|
25
|
+
configDefinitions: [imageConfigDefinition],
|
|
13
26
|
pluginConfig: (config) => config.plugins.image,
|
|
14
27
|
presets: (config) => config.plugins.image.presets,
|
|
15
28
|
resolvePreset: (parameters, config) => resolveNamedPreset('image', parameters.preset, config.plugins.image.presets),
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* keeps the source dimensions. The slicing data is entry-only.
|
|
5
5
|
*/
|
|
6
6
|
import { type ImagePreset, type SliceParameters } from '../../core/index.js';
|
|
7
|
-
import { type ServerAssetTypePlugin } from './contract.js';
|
|
7
|
+
import { type ServerAssetTypePlugin, type ServerPluginConfigDefinition } from './contract.js';
|
|
8
|
+
export declare const sliceConfigDefinition: ServerPluginConfigDefinition;
|
|
8
9
|
/**
|
|
9
10
|
* Best-effort guess of the asset's export scale from a `@2x` / `@3x` suffix in
|
|
10
11
|
* the file name (also tolerates `-`/`_` separators, e.g. `dialog-3x.png`).
|
|
@@ -3,10 +3,22 @@
|
|
|
3
3
|
* never resized — insets are authored in source pixels, so the delivered proxy
|
|
4
4
|
* keeps the source dimensions. The slicing data is entry-only.
|
|
5
5
|
*/
|
|
6
|
-
import { defaultInsetsForSize, slicePlugin } from '../../core/index.js';
|
|
6
|
+
import { SLICE_BUILT_IN_PRESETS, defaultInsetsForSize, slicePlugin, validateImagePreset } from '../../core/index.js';
|
|
7
7
|
import { processImageFile, readImageDimensions } from '../processing/image-processing.js';
|
|
8
8
|
import { extensionForImageFormat } from './image.js';
|
|
9
|
+
import { mergePresets } from './config.js';
|
|
9
10
|
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
11
|
+
export const sliceConfigDefinition = {
|
|
12
|
+
namespace: 'slice',
|
|
13
|
+
resolve: ({ value }) => ({
|
|
14
|
+
presets: mergePresets({
|
|
15
|
+
label: 'slice',
|
|
16
|
+
builtIn: SLICE_BUILT_IN_PRESETS,
|
|
17
|
+
pluginConfig: value,
|
|
18
|
+
validatePreset: validateImagePreset
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
};
|
|
10
22
|
function hasAnySide(insets) {
|
|
11
23
|
if (!insets)
|
|
12
24
|
return false;
|
|
@@ -24,6 +36,7 @@ export function detectPixelRatio(fileName) {
|
|
|
24
36
|
}
|
|
25
37
|
export const sliceServerPlugin = {
|
|
26
38
|
descriptor: slicePlugin,
|
|
39
|
+
configDefinitions: [sliceConfigDefinition],
|
|
27
40
|
pluginConfig: (config) => config.plugins.slice,
|
|
28
41
|
presets: (config) => config.plugins.slice.presets,
|
|
29
42
|
resolvePreset: (parameters, config) => resolveNamedPreset('slice', parameters.preset, config.plugins.slice.presets),
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
* Video plugin (server part): one ffmpeg transcode per preset.
|
|
3
3
|
*/
|
|
4
4
|
import { type VideoParameters, type VideoPreset } from '../../core/index.js';
|
|
5
|
-
import { type ServerAssetTypePlugin } from './contract.js';
|
|
5
|
+
import { type ServerAssetTypePlugin, type ServerPluginConfigDefinition } from './contract.js';
|
|
6
|
+
export declare const videoConfigDefinition: ServerPluginConfigDefinition;
|
|
6
7
|
export declare const videoServerPlugin: ServerAssetTypePlugin<VideoParameters, VideoPreset>;
|