fundus 0.15.0 → 0.17.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 +158 -1
- package/build/client/_app/immutable/assets/2.X44Wyac-.css +1 -0
- package/build/client/_app/immutable/assets/2.X44Wyac-.css.br +0 -0
- package/build/client/_app/immutable/assets/2.X44Wyac-.css.gz +0 -0
- package/build/client/_app/immutable/assets/{3.C6xo25uI.css → 3.66PJT3JC.css} +1 -1
- package/build/client/_app/immutable/assets/3.66PJT3JC.css.br +0 -0
- package/build/client/_app/immutable/assets/3.66PJT3JC.css.gz +0 -0
- package/build/client/_app/immutable/assets/{AssetSourceMenu.IjDdjx3o.css → AssetSourceMenu.Buj2UxUg.css} +1 -1
- package/build/client/_app/immutable/assets/AssetSourceMenu.Buj2UxUg.css.br +0 -0
- package/build/client/_app/immutable/assets/AssetSourceMenu.Buj2UxUg.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{VX-Uq_b9.js → 5fq8ySvR.js} +1 -1
- package/build/client/_app/immutable/chunks/5fq8ySvR.js.br +0 -0
- package/build/client/_app/immutable/chunks/5fq8ySvR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BcVZHIBD.js +1 -0
- package/build/client/_app/immutable/chunks/BcVZHIBD.js.br +0 -0
- package/build/client/_app/immutable/chunks/BcVZHIBD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DXiUbZ9S.js +2 -0
- package/build/client/_app/immutable/chunks/DXiUbZ9S.js.br +0 -0
- package/build/client/_app/immutable/chunks/DXiUbZ9S.js.gz +0 -0
- package/build/client/_app/immutable/chunks/H5KnxQLQ.js +108 -0
- package/build/client/_app/immutable/chunks/H5KnxQLQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/H5KnxQLQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{-8MKoIIe.js → jEVVu5G9.js} +1 -1
- package/build/client/_app/immutable/chunks/jEVVu5G9.js.br +0 -0
- package/build/client/_app/immutable/chunks/jEVVu5G9.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CJpgt8H5.js → app.Z9n2k33I.js} +2 -2
- package/build/client/_app/immutable/entry/app.Z9n2k33I.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Z9n2k33I.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DGo1uhI2.js +1 -0
- package/build/client/_app/immutable/entry/start.DGo1uhI2.js.br +0 -0
- package/build/client/_app/immutable/entry/start.DGo1uhI2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.DiZpkhxP.js +1 -0
- package/build/client/_app/immutable/nodes/0.DiZpkhxP.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.DiZpkhxP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.D6OhgXcC.js → 1.rgoSlmPu.js} +1 -1
- package/build/client/_app/immutable/nodes/1.rgoSlmPu.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.rgoSlmPu.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.Dzp3a3be.js +3 -0
- package/build/client/_app/immutable/nodes/2.Dzp3a3be.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.Dzp3a3be.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.mSsa2TDI.js +1 -0
- package/build/client/_app/immutable/nodes/3.mSsa2TDI.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.mSsa2TDI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.4oRGPYr4.js +2 -0
- package/build/client/_app/immutable/nodes/4.4oRGPYr4.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.4oRGPYr4.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 +8 -7
- package/build/handler.js.map +1 -1
- package/build/index.js +8 -7
- package/build/index.js.map +1 -1
- package/build/server/chunks/chunks/{context.js-CVuTSPjH.js → context.js-Dh0ibRIF.js} +818 -136
- package/build/server/chunks/chunks/context.js-Dh0ibRIF.js.map +1 -0
- package/build/server/chunks/chunks/{contract.js-itFN0Gon.js → contract.js-_LTMZBo2.js} +156 -58
- package/build/server/chunks/chunks/contract.js-_LTMZBo2.js.map +1 -0
- package/build/server/chunks/chunks/{core.js-CABVXpby.js → core.js-CTyKR4bu.js} +145 -156
- package/build/server/chunks/chunks/core.js-CTyKR4bu.js.map +1 -0
- package/build/server/chunks/chunks/{exports.js-VRV1_yEb.js → exports.js-gz2aNXzz.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-VRV1_yEb.js.map → exports.js-gz2aNXzz.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-ChQRRKfZ.js → http.js-BiqUcAuu.js} +3 -2
- package/build/server/chunks/chunks/http.js-BiqUcAuu.js.map +1 -0
- package/build/server/chunks/chunks/{internal.js-qXxDe7VN.js → internal.js-DoH0cN5N.js} +5 -3
- package/build/server/chunks/chunks/internal.js-DoH0cN5N.js.map +1 -0
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{manifest-membership.js-BnSECWpJ.js → manifest-membership.js-at1ixQdW.js} +10 -3
- package/build/server/chunks/chunks/manifest-membership.js-at1ixQdW.js.map +1 -0
- package/build/server/chunks/chunks/{raw-reconciliation.js-ZEOCk_jA.js → raw-reconciliation.js-CUH8IIJx.js} +3 -3
- package/build/server/chunks/chunks/raw-reconciliation.js-CUH8IIJx.js.map +1 -0
- package/build/server/chunks/chunks/{raw-reconciliation2.js-CscuoBC2.js → raw-reconciliation2.js-z4xl5zw2.js} +2 -2
- package/build/server/chunks/chunks/{raw-reconciliation2.js-CscuoBC2.js.map → raw-reconciliation2.js-z4xl5zw2.js.map} +1 -1
- package/build/server/chunks/chunks/{shared.js-DnEOHbgT.js → shared.js-CI-ZkPQn.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-DnEOHbgT.js.map → shared.js-CI-ZkPQn.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-lNH0hKSx.js → utils.js-gXYLxRG9.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-lNH0hKSx.js.map → utils.js-gXYLxRG9.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-z988EDp2.js → _server.ts.js-CdDjuW0r.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-z988EDp2.js.map → _server.ts.js-CdDjuW0r.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-C68ru6gS.js → _server.ts.js-OWM1eORG.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-C68ru6gS.js.map → _server.ts.js-OWM1eORG.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-C0tk6RFk.js → _server.ts.js-DoG-7ZpZ.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-C0tk6RFk.js.map → _server.ts.js-DoG-7ZpZ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-I03oZuo8.js → _server.ts.js-CAWjVeGd.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-I03oZuo8.js.map → _server.ts.js-CAWjVeGd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/_server.ts.js-CqS4aALY.js +38 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/_server.ts.js-CqS4aALY.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-BsQB8YDq.js → _server.ts.js-Bo_EsYXJ.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-BsQB8YDq.js.map → _server.ts.js-Bo_EsYXJ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-CJuFE6Fb.js → _server.ts.js-DW4w0XAr.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-CJuFE6Fb.js.map → _server.ts.js-DW4w0XAr.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DuOZRRiA.js → _server.ts.js-_MiNPnSy.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DuOZRRiA.js.map → _server.ts.js-_MiNPnSy.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DGzYpQbv.js → _server.ts.js-DwbrQnBc.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DGzYpQbv.js.map → _server.ts.js-DwbrQnBc.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-B045QoCA.js → _server.ts.js-CEnHQf3b.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-B045QoCA.js.map → _server.ts.js-CEnHQf3b.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-HUkVdz19.js → _server.ts.js-BZDuJZtF.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-HUkVdz19.js.map → _server.ts.js-BZDuJZtF.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-ry509mom.js → _server.ts.js-DRgdpKKd.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-ry509mom.js.map → _server.ts.js-DRgdpKKd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DU0J7U25.js → _server.ts.js-CP5QyIhz.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DU0J7U25.js.map → _server.ts.js-CP5QyIhz.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AZIV2PIn.js → _server.ts.js-BKEvt065.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AZIV2PIn.js.map → _server.ts.js-BKEvt065.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CDL0hCfM.js → _server.ts.js-C_zihkDS.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CDL0hCfM.js.map → _server.ts.js-C_zihkDS.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-B13c17fq.js → _server.ts.js-watkBmy6.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-B13c17fq.js.map → _server.ts.js-watkBmy6.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-BCxjyAGC.js → _server.ts.js-YtumLIou.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-BCxjyAGC.js.map → _server.ts.js-YtumLIou.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-BveeMZc2.js → _server.ts.js-Dkouuu-M.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-BveeMZc2.js.map → _server.ts.js-Dkouuu-M.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-BZjTd9a0.js → _server.ts.js-DwsEQeuf.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-BZjTd9a0.js.map → _server.ts.js-DwsEQeuf.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-BbTfPvC9.js → _server.ts.js-CdQxfhV3.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-BbTfPvC9.js.map → _server.ts.js-CdQxfhV3.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-8VPJMLBk.js → _server.ts.js-DDdMocWj.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-8VPJMLBk.js.map → _server.ts.js-DDdMocWj.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-BqR2J2sB.js → _server.ts.js-DbzjbH32.js} +9 -7
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-BqR2J2sB.js.map → _server.ts.js-DbzjbH32.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-DbA28dCY.js → _server.ts.js-K_JbXZsU.js} +9 -7
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-DbA28dCY.js.map → _server.ts.js-K_JbXZsU.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-9PBc_3Jh.js → hooks.server.js-DgZAVFbQ.js} +8 -6
- package/build/server/chunks/entries/{hooks.server.js-9PBc_3Jh.js.map → hooks.server.js-DgZAVFbQ.js.map} +1 -1
- package/build/server/chunks/{handler-BHt7eOOX.js → handler-CFkKak8t.js} +5 -5
- package/build/server/chunks/{handler-BHt7eOOX.js.map → handler-CFkKak8t.js.map} +1 -1
- package/build/server/chunks/{index.js-DcVaL-Ja.js → index.js-B1IroJH5.js} +5 -5
- package/build/server/chunks/{index.js-DcVaL-Ja.js.map → index.js-B1IroJH5.js.map} +1 -1
- package/build/server/chunks/{manifest.js-DgjSU4wg.js → manifest.js-DNDBsrEU.js} +36 -29
- package/build/server/chunks/manifest.js-DNDBsrEU.js.map +1 -0
- package/build/server/chunks/nodes/{0.js-BgTB-_Zt.js → 0.js-CZ3mZUZl.js} +2 -2
- package/build/server/chunks/nodes/{0.js-BgTB-_Zt.js.map → 0.js-CZ3mZUZl.js.map} +1 -1
- package/build/server/chunks/nodes/1.js-BmDzLKxV.js +7 -0
- package/build/server/chunks/nodes/{1.js-DNabcIPP.js.map → 1.js-BmDzLKxV.js.map} +1 -1
- package/build/server/chunks/nodes/2.js-Dirm-bY-.js +7 -0
- package/build/server/chunks/nodes/2.js-Dirm-bY-.js.map +1 -0
- package/build/server/chunks/nodes/3.js-DOqHmSWt.js +7 -0
- package/build/server/chunks/nodes/3.js-DOqHmSWt.js.map +1 -0
- package/build/server/chunks/nodes/4.js-C8pmF8IP.js +7 -0
- package/build/server/chunks/nodes/{4.js-BE7ZG9vA.js.map → 4.js-C8pmF8IP.js.map} +1 -1
- package/dist/config.d.ts +3 -3
- package/dist/config.js +2 -2
- package/dist/core/api-types.d.ts +24 -3
- package/dist/core/config.d.ts +84 -0
- package/dist/core/config.js +9 -0
- package/dist/core/index.d.ts +10 -5
- package/dist/core/index.js +5 -2
- package/dist/core/ingest.d.ts +1 -6
- package/dist/core/ingest.js +4 -21
- package/dist/core/library-validation.d.ts +13 -0
- package/dist/core/library-validation.js +48 -0
- package/dist/core/library.d.ts +19 -0
- package/dist/core/library.js +4 -4
- package/dist/core/manifest-membership.d.ts +2 -2
- package/dist/core/package-declaration.d.ts +43 -0
- package/dist/core/package-declaration.js +80 -0
- package/dist/core/package-merge.d.ts +65 -0
- package/dist/core/package-merge.js +173 -0
- package/dist/core/propose-asset-id.d.ts +10 -0
- package/dist/core/propose-asset-id.js +25 -0
- package/dist/core/reference-graph.d.ts +3 -3
- package/dist/server/build.js +26 -11
- package/dist/server/check.d.ts +13 -3
- package/dist/server/check.js +112 -24
- package/dist/server/cli-commands.d.ts +2 -2
- package/dist/server/cli-commands.js +213 -29
- package/dist/server/codegen.d.ts +3 -1
- package/dist/server/codegen.js +18 -0
- package/dist/server/config-loader.d.ts +36 -1
- package/dist/server/config-loader.js +190 -4
- package/dist/server/http.js +1 -0
- package/dist/server/importers/figma.d.ts +26 -3
- package/dist/server/importers/figma.js +114 -1
- package/dist/server/library-store.d.ts +20 -1
- package/dist/server/library-store.js +121 -60
- package/dist/server/operations.d.ts +12 -0
- package/dist/server/operations.js +283 -48
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/2.C-1MfvLQ.css +0 -1
- package/build/client/_app/immutable/assets/2.C-1MfvLQ.css.br +0 -0
- package/build/client/_app/immutable/assets/2.C-1MfvLQ.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.C6xo25uI.css.br +0 -0
- package/build/client/_app/immutable/assets/3.C6xo25uI.css.gz +0 -0
- package/build/client/_app/immutable/assets/AssetSourceMenu.IjDdjx3o.css.br +0 -0
- package/build/client/_app/immutable/assets/AssetSourceMenu.IjDdjx3o.css.gz +0 -0
- package/build/client/_app/immutable/chunks/-8MKoIIe.js.br +0 -0
- package/build/client/_app/immutable/chunks/-8MKoIIe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D1wFzWdi.js +0 -108
- package/build/client/_app/immutable/chunks/D1wFzWdi.js.br +0 -0
- package/build/client/_app/immutable/chunks/D1wFzWdi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DmQb0SN8.js +0 -2
- package/build/client/_app/immutable/chunks/DmQb0SN8.js.br +0 -0
- package/build/client/_app/immutable/chunks/DmQb0SN8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/N1X6Qzcl.js +0 -1
- package/build/client/_app/immutable/chunks/N1X6Qzcl.js.br +0 -0
- package/build/client/_app/immutable/chunks/N1X6Qzcl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/VX-Uq_b9.js.br +0 -1
- package/build/client/_app/immutable/chunks/VX-Uq_b9.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CJpgt8H5.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CJpgt8H5.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DToPJitz.js +0 -1
- package/build/client/_app/immutable/entry/start.DToPJitz.js.br +0 -1
- package/build/client/_app/immutable/entry/start.DToPJitz.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Co4Hh8k6.js +0 -1
- package/build/client/_app/immutable/nodes/0.Co4Hh8k6.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Co4Hh8k6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.D6OhgXcC.js.br +0 -3
- package/build/client/_app/immutable/nodes/1.D6OhgXcC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.sBDjyIQu.js +0 -3
- package/build/client/_app/immutable/nodes/2.sBDjyIQu.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.sBDjyIQu.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.DDt73kMC.js +0 -1
- package/build/client/_app/immutable/nodes/3.DDt73kMC.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.DDt73kMC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.FsXXhVSP.js +0 -2
- package/build/client/_app/immutable/nodes/4.FsXXhVSP.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.FsXXhVSP.js.gz +0 -0
- package/build/server/chunks/chunks/context.js-CVuTSPjH.js.map +0 -1
- package/build/server/chunks/chunks/contract.js-itFN0Gon.js.map +0 -1
- package/build/server/chunks/chunks/core.js-CABVXpby.js.map +0 -1
- package/build/server/chunks/chunks/http.js-ChQRRKfZ.js.map +0 -1
- package/build/server/chunks/chunks/internal.js-qXxDe7VN.js.map +0 -1
- package/build/server/chunks/chunks/manifest-membership.js-BnSECWpJ.js.map +0 -1
- package/build/server/chunks/chunks/raw-reconciliation.js-ZEOCk_jA.js.map +0 -1
- package/build/server/chunks/manifest.js-DgjSU4wg.js.map +0 -1
- package/build/server/chunks/nodes/1.js-DNabcIPP.js +0 -7
- package/build/server/chunks/nodes/2.js-CcVBvLSR.js +0 -7
- package/build/server/chunks/nodes/2.js-CcVBvLSR.js.map +0 -1
- package/build/server/chunks/nodes/3.js-DreG5rig.js +0 -7
- package/build/server/chunks/nodes/3.js-DreG5rig.js.map +0 -1
- package/build/server/chunks/nodes/4.js-BE7ZG9vA.js +0 -7
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
* the shape, resolves all paths to
|
|
4
4
|
* absolute, and merges the built-in plugin preset defaults under the host's.
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
|
-
import {
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { readFile } from 'node:fs/promises';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
10
|
+
import { pathToFileURL } from 'node:url';
|
|
8
11
|
import { createJiti } from 'jiti';
|
|
12
|
+
import { parsePackageDeclaration, validatePackageDeclaration } from '../core/index.js';
|
|
9
13
|
import { serverPluginsByType } from './plugins.js';
|
|
10
14
|
import { isRecord } from './plugins/config.js';
|
|
11
15
|
export const USAGE_NOT_CONFIGURED_MESSAGE = 'Usage analysis is not configured; add usage.referenceProvider and usage.openLocation to fundus.config.ts.';
|
|
@@ -32,6 +36,88 @@ function configDefinitionsFor(registry) {
|
|
|
32
36
|
}
|
|
33
37
|
return [...byNamespace.values()];
|
|
34
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* A string `packages` entry is a *path* when it looks like one (starts with
|
|
41
|
+
* `.`/`/`, is absolute, or ends `.json`), or a bare relative path with a
|
|
42
|
+
* separator; otherwise it is a package *name*. A scoped name (`@scope/pkg`)
|
|
43
|
+
* contains a `/` but is a name.
|
|
44
|
+
*/
|
|
45
|
+
function classifyPackageSpec(spec) {
|
|
46
|
+
if (spec.startsWith('.') || spec.startsWith('/') || isAbsolute(spec))
|
|
47
|
+
return 'path';
|
|
48
|
+
if (DECLARATION_FILE_RE.test(spec))
|
|
49
|
+
return 'path';
|
|
50
|
+
if (spec.startsWith('@'))
|
|
51
|
+
return 'name';
|
|
52
|
+
if (spec.includes('/') || spec.includes('\\'))
|
|
53
|
+
return 'path';
|
|
54
|
+
return 'name';
|
|
55
|
+
}
|
|
56
|
+
/** A declaration file is JSON or a JS/TS module (the latter loaded via jiti). */
|
|
57
|
+
const DECLARATION_FILE_RE = /\.(json|[mc]?[jt]s)$/;
|
|
58
|
+
/**
|
|
59
|
+
* Read a package declaration from its resolved file: a JSON `fundus.package.json`
|
|
60
|
+
* (parsed as text) or a JS/TS module (`fundus.package.ts`) whose default export is
|
|
61
|
+
* the declaration — loaded through the same jiti path as `fundus.config.ts`, which
|
|
62
|
+
* keeps the resource `type`s literal for the package's `PackageAssets<…>` derivation.
|
|
63
|
+
*/
|
|
64
|
+
async function loadPackageDeclaration(file, origin) {
|
|
65
|
+
if (/\.[mc]?[jt]s$/.test(file)) {
|
|
66
|
+
let module;
|
|
67
|
+
try {
|
|
68
|
+
const jiti = createJiti(import.meta.url, { interopDefault: false });
|
|
69
|
+
module = await jiti.import(file);
|
|
70
|
+
}
|
|
71
|
+
catch (cause) {
|
|
72
|
+
throw new Error(`Cannot load fundus package "${origin}" (${file}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
73
|
+
}
|
|
74
|
+
const value = isRecord(module) && 'default' in module ? module.default : module;
|
|
75
|
+
return validatePackageDeclaration(value, origin);
|
|
76
|
+
}
|
|
77
|
+
let text;
|
|
78
|
+
try {
|
|
79
|
+
text = await readFile(file, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
catch (cause) {
|
|
82
|
+
throw new Error(`Cannot read fundus package "${origin}" (${file}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
83
|
+
}
|
|
84
|
+
return parsePackageDeclaration(text, origin);
|
|
85
|
+
}
|
|
86
|
+
function normalizePackageSource(projectRoot, entry) {
|
|
87
|
+
let spec;
|
|
88
|
+
let rawDirOverride;
|
|
89
|
+
if (typeof entry === 'string') {
|
|
90
|
+
spec = entry;
|
|
91
|
+
}
|
|
92
|
+
else if (isRecord(entry) && typeof entry.package === 'string' && entry.package.length > 0) {
|
|
93
|
+
spec = entry.package;
|
|
94
|
+
if (entry.rawDir !== undefined) {
|
|
95
|
+
if (typeof entry.rawDir !== 'string' || entry.rawDir.length === 0) {
|
|
96
|
+
throw new Error('fundus.config: a "packages" entry "rawDir" must be a non-empty string.');
|
|
97
|
+
}
|
|
98
|
+
rawDirOverride = entry.rawDir;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw new Error('fundus.config: each "packages" entry must be a package name, a path string, or { package: string; rawDir?: string }.');
|
|
103
|
+
}
|
|
104
|
+
const kind = classifyPackageSpec(spec);
|
|
105
|
+
const reference = kind === 'path' ? resolve(projectRoot, spec) : spec;
|
|
106
|
+
return {
|
|
107
|
+
origin: spec,
|
|
108
|
+
kind,
|
|
109
|
+
reference,
|
|
110
|
+
...(rawDirOverride !== undefined ? { rawDirOverride } : {})
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function resolvePackageSources(projectRoot, value) {
|
|
114
|
+
if (value === undefined)
|
|
115
|
+
return [];
|
|
116
|
+
if (!Array.isArray(value)) {
|
|
117
|
+
throw new Error('fundus.config: "packages" must be an array.');
|
|
118
|
+
}
|
|
119
|
+
return value.map((entry) => normalizePackageSource(projectRoot, entry));
|
|
120
|
+
}
|
|
35
121
|
/** Pure resolution step (testable without a config file on disk). */
|
|
36
122
|
export function resolveConfig(projectRoot, value, registry = serverPluginsByType) {
|
|
37
123
|
if (!isRecord(value)) {
|
|
@@ -63,6 +149,10 @@ export function resolveConfig(projectRoot, value, registry = serverPluginsByType
|
|
|
63
149
|
if (figma.token !== undefined && (typeof figma.token !== 'string' || figma.token.length === 0)) {
|
|
64
150
|
throw new Error('fundus.config: "import.figma.token" must be a non-empty string.');
|
|
65
151
|
}
|
|
152
|
+
if (figma.defaultFileKey !== undefined &&
|
|
153
|
+
(typeof figma.defaultFileKey !== 'string' || figma.defaultFileKey.length === 0)) {
|
|
154
|
+
throw new Error('fundus.config: "import.figma.defaultFileKey" must be a non-empty string.');
|
|
155
|
+
}
|
|
66
156
|
const defaults = figma.defaults === undefined ? {} : figma.defaults;
|
|
67
157
|
if (!isRecord(defaults)) {
|
|
68
158
|
throw new Error('fundus.config: "import.figma.defaults" must be an object.');
|
|
@@ -92,12 +182,107 @@ export function resolveConfig(projectRoot, value, registry = serverPluginsByType
|
|
|
92
182
|
import: {
|
|
93
183
|
figma: {
|
|
94
184
|
...(figma.token !== undefined ? { token: figma.token } : {}),
|
|
185
|
+
...(figma.defaultFileKey !== undefined ? { defaultFileKey: figma.defaultFileKey } : {}),
|
|
95
186
|
defaults: { scale }
|
|
96
187
|
}
|
|
97
188
|
},
|
|
98
|
-
usage: usage === undefined ? null : usage
|
|
189
|
+
usage: usage === undefined ? null : usage,
|
|
190
|
+
packageSources: resolvePackageSources(projectRoot, value.packages),
|
|
191
|
+
packages: []
|
|
99
192
|
};
|
|
100
193
|
}
|
|
194
|
+
function nearestNamedPackageJson(entryPath, name) {
|
|
195
|
+
let dir = dirname(entryPath);
|
|
196
|
+
for (;;) {
|
|
197
|
+
const candidate = join(dir, 'package.json');
|
|
198
|
+
if (existsSync(candidate)) {
|
|
199
|
+
try {
|
|
200
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
201
|
+
if (isRecord(pkg) && pkg.name === name)
|
|
202
|
+
return { dir, pkg };
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Malformed package.json — keep walking up.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const parent = dirname(dir);
|
|
209
|
+
if (parent === dir)
|
|
210
|
+
break;
|
|
211
|
+
dir = parent;
|
|
212
|
+
}
|
|
213
|
+
throw new Error(`Cannot find the package.json for fundus package "${name}" (resolved to ${entryPath}).`);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Resolve a name-form Package to its root directory + parsed `package.json`. A
|
|
217
|
+
* declaration-only Package ships no JS entry module, so resolving the bare name
|
|
218
|
+
* would throw `MODULE_NOT_FOUND`; resolve its `package.json` subpath first and
|
|
219
|
+
* only fall back to entry resolution when an `exports` map hides it.
|
|
220
|
+
*/
|
|
221
|
+
function resolvePackageRoot(require, reference, origin) {
|
|
222
|
+
try {
|
|
223
|
+
const manifestPath = require.resolve(`${reference}/package.json`);
|
|
224
|
+
const pkg = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
225
|
+
if (isRecord(pkg))
|
|
226
|
+
return { dir: dirname(manifestPath), pkg };
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// No resolvable ./package.json subpath — fall back to the entry module.
|
|
230
|
+
}
|
|
231
|
+
let entry;
|
|
232
|
+
try {
|
|
233
|
+
entry = require.resolve(reference);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
throw new Error(`Cannot resolve fundus package "${origin}": no installed package declares it — is it a dependency?`);
|
|
237
|
+
}
|
|
238
|
+
return nearestNamedPackageJson(entry, reference);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Read and parse every declared Package from disk. Name entries resolve through
|
|
242
|
+
* the host's module resolver (respecting `exports`), then walk up to the nearest
|
|
243
|
+
* `package.json` that declares the name and read its `"fundus"` field. Path
|
|
244
|
+
* entries point directly at a `fundus.package.json`. Throws a clear,
|
|
245
|
+
* origin-tagged error on any resolution/read/parse failure — Package assembly
|
|
246
|
+
* failures are symmetric with a missing `fundus.config.ts`.
|
|
247
|
+
*/
|
|
248
|
+
export async function loadPackages(projectRoot, sources) {
|
|
249
|
+
const require = createRequire(pathToFileURL(join(projectRoot, 'fundus.config.ts')).href);
|
|
250
|
+
const packages = [];
|
|
251
|
+
for (const source of sources) {
|
|
252
|
+
let file;
|
|
253
|
+
let defaultRawDir;
|
|
254
|
+
if (source.kind === 'path') {
|
|
255
|
+
file = source.reference;
|
|
256
|
+
if (!existsSync(file)) {
|
|
257
|
+
throw new Error(`Fundus package file not found: ${file} (from "${source.origin}").`);
|
|
258
|
+
}
|
|
259
|
+
defaultRawDir = dirname(file);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const { dir, pkg } = resolvePackageRoot(require, source.reference, source.origin);
|
|
263
|
+
const fundusField = pkg.fundus;
|
|
264
|
+
if (!isRecord(fundusField) ||
|
|
265
|
+
typeof fundusField.package !== 'string' ||
|
|
266
|
+
fundusField.package.length === 0) {
|
|
267
|
+
throw new Error(`Fundus package "${source.origin}" is missing a "fundus.package" field in its package.json.`);
|
|
268
|
+
}
|
|
269
|
+
file = resolve(dir, fundusField.package);
|
|
270
|
+
if (!existsSync(file)) {
|
|
271
|
+
throw new Error(`Fundus package "${source.origin}" declaration not found: ${file}.`);
|
|
272
|
+
}
|
|
273
|
+
defaultRawDir =
|
|
274
|
+
typeof fundusField.rawDir === 'string' && fundusField.rawDir.length > 0
|
|
275
|
+
? resolve(dir, fundusField.rawDir)
|
|
276
|
+
: dir;
|
|
277
|
+
}
|
|
278
|
+
const declaration = await loadPackageDeclaration(file, source.origin);
|
|
279
|
+
const rawDir = source.rawDirOverride !== undefined
|
|
280
|
+
? resolve(projectRoot, source.rawDirOverride)
|
|
281
|
+
: defaultRawDir;
|
|
282
|
+
packages.push({ origin: source.origin, file, rawDir, declaration });
|
|
283
|
+
}
|
|
284
|
+
return packages;
|
|
285
|
+
}
|
|
101
286
|
/** Load `fundus.config.ts` from the host project root. */
|
|
102
287
|
export async function loadConfig(projectRoot) {
|
|
103
288
|
const root = resolve(projectRoot);
|
|
@@ -113,5 +298,6 @@ export async function loadConfig(projectRoot) {
|
|
|
113
298
|
catch (cause) {
|
|
114
299
|
throw new Error(`Cannot load fundus.config.ts: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
115
300
|
}
|
|
116
|
-
|
|
301
|
+
const config = resolveConfig(root, isRecord(module) && 'default' in module ? module.default : module);
|
|
302
|
+
return { ...config, packages: await loadPackages(root, config.packageSources) };
|
|
117
303
|
}
|
package/dist/server/http.js
CHANGED
|
@@ -1,21 +1,44 @@
|
|
|
1
|
-
import { type ServerSourceImporter } from './contract.js';
|
|
1
|
+
import { type ImporterContext, type ServerSourceImporter } from './contract.js';
|
|
2
2
|
import { type PinnedHttpsTarget } from './pinned-https.js';
|
|
3
|
-
export interface
|
|
3
|
+
export interface FigmaNodeSourceParameters {
|
|
4
|
+
mode?: 'node';
|
|
4
5
|
fileKey: string;
|
|
5
6
|
nodeId: string;
|
|
6
7
|
format: 'png';
|
|
7
8
|
scale: 1 | 2 | 3 | 4;
|
|
8
9
|
}
|
|
9
|
-
export interface
|
|
10
|
+
export interface FigmaTagSourceParameters {
|
|
11
|
+
mode: 'tag';
|
|
12
|
+
fileKey: string;
|
|
13
|
+
/** The Fundus asset id carried in Figma as `sharedPluginData.fundus.assetId`. */
|
|
14
|
+
fundusTag: string;
|
|
15
|
+
/** Resolved node id (colon-form), cached from the last tag resolution. */
|
|
16
|
+
nodeId: string;
|
|
17
|
+
format: 'png';
|
|
18
|
+
scale: 1 | 2 | 3 | 4;
|
|
19
|
+
}
|
|
20
|
+
export type FigmaSourceParameters = FigmaNodeSourceParameters | FigmaTagSourceParameters;
|
|
21
|
+
export interface FigmaNodeImportInput {
|
|
22
|
+
mode?: 'node';
|
|
10
23
|
url: string;
|
|
11
24
|
format?: 'png';
|
|
12
25
|
scale?: 1 | 2 | 3 | 4;
|
|
13
26
|
}
|
|
27
|
+
export interface FigmaTagImportInput {
|
|
28
|
+
mode: 'tag';
|
|
29
|
+
tag: string;
|
|
30
|
+
fileKey?: string;
|
|
31
|
+
format?: 'png';
|
|
32
|
+
scale?: 1 | 2 | 3 | 4;
|
|
33
|
+
}
|
|
34
|
+
export type FigmaImportInput = FigmaNodeImportInput | FigmaTagImportInput;
|
|
14
35
|
export declare function parseFigmaSelectionUrl(value: string): {
|
|
15
36
|
fileKey: string;
|
|
16
37
|
nodeId: string;
|
|
17
38
|
};
|
|
39
|
+
export declare function parseFigmaFileKey(value: string): string;
|
|
18
40
|
export declare function validateFigmaSourceParameters(value: unknown): FigmaSourceParameters;
|
|
19
41
|
export declare function resolveSafeDownloadTarget(value: string | URL): Promise<PinnedHttpsTarget>;
|
|
20
42
|
export declare function assertSafeDownloadUrl(value: string | URL): Promise<URL>;
|
|
43
|
+
export declare function resolveTagToNode(fileKey: string, tag: string, context: ImporterContext): Promise<string>;
|
|
21
44
|
export declare const figmaSourceImporter: ServerSourceImporter<FigmaImportInput, FigmaSourceParameters>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lookup } from 'node:dns/promises';
|
|
2
2
|
import { isIP } from 'node:net';
|
|
3
3
|
import sharp from 'sharp';
|
|
4
|
+
import { isValidAssetId } from '../../core/index.js';
|
|
4
5
|
import { SourceImportError, invalidInput } from './contract.js';
|
|
5
6
|
import { requestPinnedHttps } from './pinned-https.js';
|
|
6
7
|
const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
@@ -36,6 +37,24 @@ export function parseFigmaSelectionUrl(value) {
|
|
|
36
37
|
const [left, right] = nodeIds[0].split(/[-:]/);
|
|
37
38
|
return { fileKey: segments[1], nodeId: `${left}:${right}` };
|
|
38
39
|
}
|
|
40
|
+
export function parseFigmaFileKey(value) {
|
|
41
|
+
let url;
|
|
42
|
+
try {
|
|
43
|
+
url = new URL(value);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return invalidInput('Enter a valid Figma file link.');
|
|
47
|
+
}
|
|
48
|
+
if (url.protocol !== 'https:' ||
|
|
49
|
+
(url.hostname !== 'figma.com' && url.hostname !== 'www.figma.com')) {
|
|
50
|
+
return invalidInput('Figma links must use HTTPS on figma.com.');
|
|
51
|
+
}
|
|
52
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
53
|
+
if (!['design', 'file', 'proto'].includes(segments[0] ?? '') || !segments[1]) {
|
|
54
|
+
return invalidInput('The Figma link must point to a design, file, or prototype.');
|
|
55
|
+
}
|
|
56
|
+
return segments[1];
|
|
57
|
+
}
|
|
39
58
|
function validateScale(value) {
|
|
40
59
|
if (value !== 1 && value !== 2 && value !== 3 && value !== 4) {
|
|
41
60
|
return invalidInput('Figma export scale must be 1, 2, 3, or 4.');
|
|
@@ -44,12 +63,31 @@ function validateScale(value) {
|
|
|
44
63
|
}
|
|
45
64
|
function validateInput(value) {
|
|
46
65
|
const input = record(value);
|
|
47
|
-
if (!input
|
|
66
|
+
if (!input)
|
|
48
67
|
return invalidInput('Figma import requires a URL.');
|
|
49
68
|
if (input.format !== undefined && input.format !== 'png') {
|
|
50
69
|
return invalidInput('Figma import currently supports PNG only.');
|
|
51
70
|
}
|
|
71
|
+
if (input.mode === 'tag') {
|
|
72
|
+
if (typeof input.tag !== 'string' || !isValidAssetId(input.tag)) {
|
|
73
|
+
return invalidInput('Figma tag import requires a valid Fundus asset id as the tag.');
|
|
74
|
+
}
|
|
75
|
+
if (input.fileKey !== undefined &&
|
|
76
|
+
(typeof input.fileKey !== 'string' || input.fileKey.length === 0)) {
|
|
77
|
+
return invalidInput('Figma tag import fileKey must be a non-empty string.');
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
mode: 'tag',
|
|
81
|
+
tag: input.tag,
|
|
82
|
+
format: 'png',
|
|
83
|
+
...(input.fileKey !== undefined ? { fileKey: input.fileKey } : {}),
|
|
84
|
+
...(input.scale !== undefined ? { scale: validateScale(input.scale) } : {})
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (typeof input.url !== 'string')
|
|
88
|
+
return invalidInput('Figma import requires a URL.');
|
|
52
89
|
return {
|
|
90
|
+
mode: 'node',
|
|
53
91
|
url: input.url,
|
|
54
92
|
format: 'png',
|
|
55
93
|
...(input.scale !== undefined ? { scale: validateScale(input.scale) } : {})
|
|
@@ -65,6 +103,19 @@ export function validateFigmaSourceParameters(value) {
|
|
|
65
103
|
source.format !== 'png') {
|
|
66
104
|
return invalidInput('Stored Figma source parameters are invalid.');
|
|
67
105
|
}
|
|
106
|
+
if (source.mode === 'tag') {
|
|
107
|
+
if (typeof source.fundusTag !== 'string' || !isValidAssetId(source.fundusTag)) {
|
|
108
|
+
return invalidInput('Stored Figma source parameters are invalid.');
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
mode: 'tag',
|
|
112
|
+
fileKey: source.fileKey,
|
|
113
|
+
fundusTag: source.fundusTag,
|
|
114
|
+
nodeId: source.nodeId,
|
|
115
|
+
format: 'png',
|
|
116
|
+
scale: validateScale(source.scale)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
68
119
|
return {
|
|
69
120
|
fileKey: source.fileKey,
|
|
70
121
|
nodeId: source.nodeId,
|
|
@@ -332,6 +383,49 @@ async function validatePng(bytes) {
|
|
|
332
383
|
throw new SourceImportError('image-too-large', 'Figma export exceeds the 32 megapixel limit.');
|
|
333
384
|
}
|
|
334
385
|
}
|
|
386
|
+
function collectTaggedNodeIds(node, tag, matches) {
|
|
387
|
+
const current = record(node);
|
|
388
|
+
if (!current)
|
|
389
|
+
return;
|
|
390
|
+
const shared = record(current.sharedPluginData);
|
|
391
|
+
const fundus = shared ? record(shared.fundus) : null;
|
|
392
|
+
if (fundus && fundus.assetId === tag && typeof current.id === 'string') {
|
|
393
|
+
matches.push(current.id);
|
|
394
|
+
}
|
|
395
|
+
if (Array.isArray(current.children)) {
|
|
396
|
+
for (const child of current.children)
|
|
397
|
+
collectTaggedNodeIds(child, tag, matches);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
export async function resolveTagToNode(fileKey, tag, context) {
|
|
401
|
+
const token = context.config.import.figma.token;
|
|
402
|
+
if (!token) {
|
|
403
|
+
throw new SourceImportError('not-configured', 'Figma access is not configured.');
|
|
404
|
+
}
|
|
405
|
+
const endpoint = new URL(`https://api.figma.com/v1/files/${encodeURIComponent(fileKey)}`);
|
|
406
|
+
endpoint.searchParams.set('plugin_data', 'shared');
|
|
407
|
+
const jsonBytes = await fetchWithDeadline(context.fetch, endpoint, { redirect: 'error', headers: { 'X-Figma-Token': token } }, async (response) => new Uint8Array(await response.arrayBuffer()));
|
|
408
|
+
let payload;
|
|
409
|
+
try {
|
|
410
|
+
payload = JSON.parse(new TextDecoder().decode(jsonBytes));
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
throw new SourceImportError('invalid-response', 'Figma returned an invalid response.');
|
|
414
|
+
}
|
|
415
|
+
const body = record(payload);
|
|
416
|
+
if (!body || body.err || (typeof body.status === 'number' && body.status >= 400)) {
|
|
417
|
+
throw new SourceImportError('invalid-response', 'Figma returned an invalid response.');
|
|
418
|
+
}
|
|
419
|
+
const matches = [];
|
|
420
|
+
collectTaggedNodeIds(body.document, tag, matches);
|
|
421
|
+
if (matches.length === 0) {
|
|
422
|
+
throw new SourceImportError('not-found', `No Figma node is tagged "${tag}" in this file.`);
|
|
423
|
+
}
|
|
424
|
+
if (matches.length > 1) {
|
|
425
|
+
throw new SourceImportError('ambiguous-tag', `More than one Figma node is tagged "${tag}" in this file.`);
|
|
426
|
+
}
|
|
427
|
+
return matches[0];
|
|
428
|
+
}
|
|
335
429
|
async function materialize(source, context) {
|
|
336
430
|
const token = context.config.import.figma.token;
|
|
337
431
|
if (!token) {
|
|
@@ -371,6 +465,21 @@ export const figmaSourceImporter = {
|
|
|
371
465
|
label: 'Figma',
|
|
372
466
|
validateInput,
|
|
373
467
|
async importRaw(input, context) {
|
|
468
|
+
if (input.mode === 'tag') {
|
|
469
|
+
const fileKey = input.fileKey ?? context.config.import.figma.defaultFileKey;
|
|
470
|
+
if (!fileKey) {
|
|
471
|
+
throw new SourceImportError('not-configured', 'Figma tag import needs a fileKey or import.figma.defaultFileKey in fundus.config.');
|
|
472
|
+
}
|
|
473
|
+
const nodeId = await resolveTagToNode(fileKey, input.tag, context);
|
|
474
|
+
return materialize({
|
|
475
|
+
mode: 'tag',
|
|
476
|
+
fileKey,
|
|
477
|
+
fundusTag: input.tag,
|
|
478
|
+
nodeId,
|
|
479
|
+
format: 'png',
|
|
480
|
+
scale: input.scale ?? context.config.import.figma.defaults.scale
|
|
481
|
+
}, context);
|
|
482
|
+
}
|
|
374
483
|
const parsed = parseFigmaSelectionUrl(input.url);
|
|
375
484
|
return materialize({
|
|
376
485
|
fileKey: parsed.fileKey,
|
|
@@ -381,6 +490,10 @@ export const figmaSourceImporter = {
|
|
|
381
490
|
},
|
|
382
491
|
validateSourceParameters: validateFigmaSourceParameters,
|
|
383
492
|
async reimportRaw(source, context) {
|
|
493
|
+
if (source.mode === 'tag') {
|
|
494
|
+
const nodeId = await resolveTagToNode(source.fileKey, source.fundusTag, context);
|
|
495
|
+
return materialize({ ...source, nodeId }, context);
|
|
496
|
+
}
|
|
384
497
|
return materialize(source, context);
|
|
385
498
|
}
|
|
386
499
|
};
|
|
@@ -26,6 +26,14 @@ export interface IngestOptions {
|
|
|
26
26
|
export interface CreateAssetOptions extends IngestOptions {
|
|
27
27
|
manifests?: string[];
|
|
28
28
|
importSource?: ImportSourceRecord;
|
|
29
|
+
/**
|
|
30
|
+
* Fill/override a Package slot: store these parameters verbatim as a host
|
|
31
|
+
* override record instead of expanding plugin defaults. Partial by design
|
|
32
|
+
* (only the overridden keys) — the merge deep-merges them over the Package
|
|
33
|
+
* base, and they are schema-validated over the effective library, not here.
|
|
34
|
+
* When set, `prepareParameters` is ignored.
|
|
35
|
+
*/
|
|
36
|
+
parametersOverride?: unknown;
|
|
29
37
|
}
|
|
30
38
|
export interface CreateAssetStaging {
|
|
31
39
|
rawPath: string;
|
|
@@ -84,6 +92,15 @@ export declare class LibraryStore {
|
|
|
84
92
|
replaceRawFileFromSource(id: string, options: ReplaceRawFromSourceOptions): Promise<Library>;
|
|
85
93
|
/** CAS-protected replacement used only by a repeatable import source. */
|
|
86
94
|
replaceRawFromImportSource(id: string, bytes: Uint8Array, expected: ImportSourceExpectation, nextSource?: ImportSourceRecord, updateParameters?: (parameters: unknown) => unknown): Promise<Library>;
|
|
95
|
+
/**
|
|
96
|
+
* CAS-protected raw swap for a Figma tag source that may also rename the
|
|
97
|
+
* asset. In tag mode the tag IS the id, so changing the tag moves the library
|
|
98
|
+
* key. The raw swap and the (optional) key move happen in ONE queued critical
|
|
99
|
+
* section and ONE `write`, so no inconsistent intermediate state is ever
|
|
100
|
+
* persisted; on any error the record and raw file are rolled back and the key
|
|
101
|
+
* move (in-memory only until `write`) is discarded.
|
|
102
|
+
*/
|
|
103
|
+
repointTagAsset(oldId: string, newId: string, bytes: Uint8Array, expected: ImportSourceExpectation, nextSource?: ImportSourceRecord, updateParameters?: (parameters: unknown) => unknown): Promise<Library>;
|
|
87
104
|
/** Replace an asset's parameters (validated by the owning plugin). */
|
|
88
105
|
updateParameters(id: string, parameters: unknown): Promise<Library>;
|
|
89
106
|
/** Replace an asset's manifest membership (every name must exist). */
|
|
@@ -124,6 +141,8 @@ export declare class LibraryStore {
|
|
|
124
141
|
* type, dangling raw pointer, invalid parameters, unknown manifest names,
|
|
125
142
|
* and reference errors (dangling target, type mismatch, cycles).
|
|
126
143
|
*/
|
|
127
|
-
validate(library: Library
|
|
144
|
+
validate(library: Library, options?: {
|
|
145
|
+
flatSpace?: boolean;
|
|
146
|
+
}): Promise<ValidationIssueDto[]>;
|
|
128
147
|
syncFolders(library: Library): Promise<void>;
|
|
129
148
|
}
|