fundus 0.16.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 +37 -1
- package/build/client/_app/immutable/assets/{AssetSourceMenu.B5fzYfym.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.B5fzYfym.css.gz → AssetSourceMenu.Buj2UxUg.css.gz} +0 -0
- package/build/client/_app/immutable/chunks/{D6c2mfMB.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/{B97y-eFA.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.V1MmmICK.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.BqoPRvUU.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 +7 -7
- package/build/index.js +7 -7
- package/build/server/chunks/chunks/{context.js-BXaBgcZT.js → context.js-Dh0ibRIF.js} +196 -7
- package/build/server/chunks/chunks/context.js-Dh0ibRIF.js.map +1 -0
- package/build/server/chunks/chunks/{contract.js-IY1F-HE_.js → contract.js-_LTMZBo2.js} +68 -6
- package/build/server/chunks/chunks/contract.js-_LTMZBo2.js.map +1 -0
- package/build/server/chunks/chunks/{core.js-D1sYC_78.js → core.js-CTyKR4bu.js} +2 -14
- package/build/server/chunks/chunks/{core.js-D1sYC_78.js.map → core.js-CTyKR4bu.js.map} +1 -1
- package/build/server/chunks/chunks/{exports.js-B5DLeNXr.js → exports.js-gz2aNXzz.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-B5DLeNXr.js.map → exports.js-gz2aNXzz.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-CzB9Igb0.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-HE35Pm1C.js → internal.js-DoH0cN5N.js} +3 -3
- package/build/server/chunks/chunks/{internal.js-HE35Pm1C.js.map → internal.js-DoH0cN5N.js.map} +1 -1
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{manifest-membership.js-Cm_2fU-Z.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-1_IwPhKb.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-CcO9li5n.js → raw-reconciliation2.js-z4xl5zw2.js} +2 -2
- package/build/server/chunks/chunks/{raw-reconciliation2.js-CcO9li5n.js.map → raw-reconciliation2.js-z4xl5zw2.js.map} +1 -1
- package/build/server/chunks/chunks/{shared.js-MR8sPPTu.js → shared.js-CI-ZkPQn.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-MR8sPPTu.js.map → shared.js-CI-ZkPQn.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-C2UFBITa.js → utils.js-gXYLxRG9.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-C2UFBITa.js.map → utils.js-gXYLxRG9.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-BnRJ5Qnd.js → _server.ts.js-CdDjuW0r.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-BnRJ5Qnd.js.map → _server.ts.js-CdDjuW0r.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BZPR6n5n.js → _server.ts.js-OWM1eORG.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BZPR6n5n.js.map → _server.ts.js-OWM1eORG.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-DiBS3A0C.js → _server.ts.js-DoG-7ZpZ.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-DiBS3A0C.js.map → _server.ts.js-DoG-7ZpZ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-LOO6qm2-.js → _server.ts.js-CAWjVeGd.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-LOO6qm2-.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-CRtJJoSs.js → _server.ts.js-Bo_EsYXJ.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-CRtJJoSs.js.map → _server.ts.js-Bo_EsYXJ.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-Ds4zCZ9H.js → _server.ts.js-DW4w0XAr.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-Ds4zCZ9H.js.map → _server.ts.js-DW4w0XAr.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DD4N8pcj.js → _server.ts.js-_MiNPnSy.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DD4N8pcj.js.map → _server.ts.js-_MiNPnSy.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-CZev9aYz.js → _server.ts.js-DwbrQnBc.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-CZev9aYz.js.map → _server.ts.js-DwbrQnBc.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-MBDVW0q1.js → _server.ts.js-CEnHQf3b.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-MBDVW0q1.js.map → _server.ts.js-CEnHQf3b.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-C7naXnsR.js → _server.ts.js-BZDuJZtF.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-C7naXnsR.js.map → _server.ts.js-BZDuJZtF.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DuGyRdf3.js → _server.ts.js-DRgdpKKd.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DuGyRdf3.js.map → _server.ts.js-DRgdpKKd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-0-ZCt0qH.js → _server.ts.js-CP5QyIhz.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-0-ZCt0qH.js.map → _server.ts.js-CP5QyIhz.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-DFYU_sZL.js → _server.ts.js-BKEvt065.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-DFYU_sZL.js.map → _server.ts.js-BKEvt065.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-BqJpK0sJ.js → _server.ts.js-C_zihkDS.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-BqJpK0sJ.js.map → _server.ts.js-C_zihkDS.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js--LSqADJp.js → _server.ts.js-watkBmy6.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js--LSqADJp.js.map → _server.ts.js-watkBmy6.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-DcL8nYDM.js → _server.ts.js-YtumLIou.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-DcL8nYDM.js.map → _server.ts.js-YtumLIou.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-72OcyLfP.js → _server.ts.js-Dkouuu-M.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-72OcyLfP.js.map → _server.ts.js-Dkouuu-M.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-D0bOxmG0.js → _server.ts.js-DwsEQeuf.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-D0bOxmG0.js.map → _server.ts.js-DwsEQeuf.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-BjDVCRNV.js → _server.ts.js-CdQxfhV3.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-BjDVCRNV.js.map → _server.ts.js-CdQxfhV3.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-CndfoXXd.js → _server.ts.js-DDdMocWj.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-CndfoXXd.js.map → _server.ts.js-DDdMocWj.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-Ck1mgbk5.js → _server.ts.js-DbzjbH32.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-Ck1mgbk5.js.map → _server.ts.js-DbzjbH32.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-lZ9oP8tz.js → _server.ts.js-K_JbXZsU.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-lZ9oP8tz.js.map → _server.ts.js-K_JbXZsU.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-DQ6l2RSK.js → hooks.server.js-DgZAVFbQ.js} +6 -6
- package/build/server/chunks/entries/{hooks.server.js-DQ6l2RSK.js.map → hooks.server.js-DgZAVFbQ.js.map} +1 -1
- package/build/server/chunks/{handler-Dfk_4a-r.js → handler-CFkKak8t.js} +5 -5
- package/build/server/chunks/{handler-Dfk_4a-r.js.map → handler-CFkKak8t.js.map} +1 -1
- package/build/server/chunks/{index.js-O3Wl1zok.js → index.js-B1IroJH5.js} +5 -5
- package/build/server/chunks/{index.js-O3Wl1zok.js.map → index.js-B1IroJH5.js.map} +1 -1
- package/build/server/chunks/{manifest.js-51nEZnKQ.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-DjqMfDtB.js → 0.js-CZ3mZUZl.js} +2 -2
- package/build/server/chunks/nodes/{0.js-DjqMfDtB.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-BwfSLYJH.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-D7_hF0qp.js.map → 3.js-DOqHmSWt.js.map} +1 -1
- package/build/server/chunks/nodes/4.js-C8pmF8IP.js +7 -0
- package/build/server/chunks/nodes/{4.js-D58M2chZ.js.map → 4.js-C8pmF8IP.js.map} +1 -1
- package/dist/core/api-types.d.ts +2 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/ingest.d.ts +1 -6
- package/dist/core/ingest.js +4 -21
- package/dist/core/propose-asset-id.d.ts +10 -0
- package/dist/core/propose-asset-id.js +25 -0
- package/dist/server/cli-commands.d.ts +2 -2
- package/dist/server/cli-commands.js +213 -29
- package/dist/server/config-loader.d.ts +1 -0
- package/dist/server/config-loader.js +5 -0
- 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 +9 -0
- package/dist/server/library-store.js +79 -0
- package/dist/server/operations.d.ts +12 -0
- package/dist/server/operations.js +121 -1
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/AssetSourceMenu.B5fzYfym.css.br +0 -0
- package/build/client/_app/immutable/chunks/B06eqbax.js +0 -108
- package/build/client/_app/immutable/chunks/B06eqbax.js.br +0 -0
- package/build/client/_app/immutable/chunks/B06eqbax.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B97y-eFA.js.br +0 -0
- package/build/client/_app/immutable/chunks/B97y-eFA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C9MzfQz6.js +0 -2
- package/build/client/_app/immutable/chunks/C9MzfQz6.js.br +0 -0
- package/build/client/_app/immutable/chunks/C9MzfQz6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CcuyGBqj.js +0 -1
- package/build/client/_app/immutable/chunks/CcuyGBqj.js.br +0 -0
- package/build/client/_app/immutable/chunks/CcuyGBqj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D6c2mfMB.js.br +0 -2
- package/build/client/_app/immutable/chunks/D6c2mfMB.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.V1MmmICK.js.br +0 -0
- package/build/client/_app/immutable/entry/app.V1MmmICK.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BbyflZUw.js +0 -1
- package/build/client/_app/immutable/entry/start.BbyflZUw.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BbyflZUw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.BJOUVkow.js +0 -1
- package/build/client/_app/immutable/nodes/0.BJOUVkow.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.BJOUVkow.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BqoPRvUU.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BqoPRvUU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.BSUVFDfW.js +0 -3
- package/build/client/_app/immutable/nodes/2.BSUVFDfW.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.BSUVFDfW.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.B6beFiTQ.js +0 -1
- package/build/client/_app/immutable/nodes/3.B6beFiTQ.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.B6beFiTQ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.Dt96q8V4.js +0 -2
- package/build/client/_app/immutable/nodes/4.Dt96q8V4.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.Dt96q8V4.js.gz +0 -0
- package/build/server/chunks/chunks/context.js-BXaBgcZT.js.map +0 -1
- package/build/server/chunks/chunks/contract.js-IY1F-HE_.js.map +0 -1
- package/build/server/chunks/chunks/http.js-CzB9Igb0.js.map +0 -1
- package/build/server/chunks/chunks/manifest-membership.js-Cm_2fU-Z.js.map +0 -1
- package/build/server/chunks/chunks/raw-reconciliation.js-1_IwPhKb.js.map +0 -1
- package/build/server/chunks/manifest.js-51nEZnKQ.js.map +0 -1
- package/build/server/chunks/nodes/1.js-BwfSLYJH.js +0 -7
- package/build/server/chunks/nodes/2.js-buHbEytf.js +0 -7
- package/build/server/chunks/nodes/2.js-buHbEytf.js.map +0 -1
- package/build/server/chunks/nodes/3.js-D7_hF0qp.js +0 -7
- package/build/server/chunks/nodes/4.js-D58M2chZ.js +0 -7
|
@@ -92,6 +92,15 @@ export declare class LibraryStore {
|
|
|
92
92
|
replaceRawFileFromSource(id: string, options: ReplaceRawFromSourceOptions): Promise<Library>;
|
|
93
93
|
/** CAS-protected replacement used only by a repeatable import source. */
|
|
94
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>;
|
|
95
104
|
/** Replace an asset's parameters (validated by the owning plugin). */
|
|
96
105
|
updateParameters(id: string, parameters: unknown): Promise<Library>;
|
|
97
106
|
/** Replace an asset's manifest membership (every name must exist). */
|
|
@@ -348,6 +348,85 @@ export class LibraryStore {
|
|
|
348
348
|
return library;
|
|
349
349
|
});
|
|
350
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* CAS-protected raw swap for a Figma tag source that may also rename the
|
|
353
|
+
* asset. In tag mode the tag IS the id, so changing the tag moves the library
|
|
354
|
+
* key. The raw swap and the (optional) key move happen in ONE queued critical
|
|
355
|
+
* section and ONE `write`, so no inconsistent intermediate state is ever
|
|
356
|
+
* persisted; on any error the record and raw file are rolled back and the key
|
|
357
|
+
* move (in-memory only until `write`) is discarded.
|
|
358
|
+
*/
|
|
359
|
+
async repointTagAsset(oldId, newId, bytes, expected, nextSource, updateParameters) {
|
|
360
|
+
return this.#enqueue(async () => {
|
|
361
|
+
const library = await this.read();
|
|
362
|
+
const record = this.#requireAsset(library, oldId);
|
|
363
|
+
if (record.raw !== expected.raw ||
|
|
364
|
+
record.rawHash !== expected.rawHash ||
|
|
365
|
+
record.type !== expected.type ||
|
|
366
|
+
!record.importSource ||
|
|
367
|
+
canonicalizeParameters(record.importSource) !== expected.canonicalSource) {
|
|
368
|
+
throw new Error('stale-import');
|
|
369
|
+
}
|
|
370
|
+
const renamed = newId !== oldId;
|
|
371
|
+
if (renamed && Object.hasOwn(library.assets, newId)) {
|
|
372
|
+
throw new Error(`An asset with id "${newId}" already exists.`);
|
|
373
|
+
}
|
|
374
|
+
const absolutePath = join(this.#config.rawDir, record.raw);
|
|
375
|
+
const { verifyStableRawFile } = await import('./raw-reconciliation.js');
|
|
376
|
+
try {
|
|
377
|
+
await verifyStableRawFile(absolutePath, expected.rawHash);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
throw new Error('stale-import');
|
|
381
|
+
}
|
|
382
|
+
const token = crypto.randomUUID();
|
|
383
|
+
const temporaryPath = `${absolutePath}.fundus-retag-${token}.tmp`;
|
|
384
|
+
const backupPath = `${absolutePath}.fundus-retag-${token}`;
|
|
385
|
+
const previousHash = record.rawHash;
|
|
386
|
+
const previousSource = structuredClone(record.importSource);
|
|
387
|
+
const previousParameters = structuredClone(record.parameters);
|
|
388
|
+
let staged = false;
|
|
389
|
+
let installed = false;
|
|
390
|
+
try {
|
|
391
|
+
await writeFile(temporaryPath, bytes);
|
|
392
|
+
await rename(absolutePath, backupPath);
|
|
393
|
+
staged = true;
|
|
394
|
+
await rename(temporaryPath, absolutePath);
|
|
395
|
+
installed = true;
|
|
396
|
+
record.rawHash = sha256Hex(bytes);
|
|
397
|
+
if (nextSource)
|
|
398
|
+
record.importSource = structuredClone(nextSource);
|
|
399
|
+
if (updateParameters)
|
|
400
|
+
record.parameters = updateParameters(record.parameters);
|
|
401
|
+
if (renamed) {
|
|
402
|
+
delete library.assets[oldId];
|
|
403
|
+
library.assets[newId] = record;
|
|
404
|
+
}
|
|
405
|
+
await this.write(library);
|
|
406
|
+
}
|
|
407
|
+
catch (cause) {
|
|
408
|
+
record.rawHash = previousHash;
|
|
409
|
+
record.importSource = previousSource;
|
|
410
|
+
record.parameters = previousParameters;
|
|
411
|
+
if (renamed) {
|
|
412
|
+
delete library.assets[newId];
|
|
413
|
+
library.assets[oldId] = record;
|
|
414
|
+
}
|
|
415
|
+
if (installed)
|
|
416
|
+
await unlink(absolutePath).catch(() => { });
|
|
417
|
+
if (staged)
|
|
418
|
+
await rename(backupPath, absolutePath);
|
|
419
|
+
await unlink(temporaryPath).catch(() => { });
|
|
420
|
+
throw cause;
|
|
421
|
+
}
|
|
422
|
+
if (staged)
|
|
423
|
+
await unlink(backupPath).catch(() => { });
|
|
424
|
+
// The old id's proxies are orphans after a rename (pruning also covers it).
|
|
425
|
+
if (renamed)
|
|
426
|
+
await this.#deleteProxiesOf(oldId);
|
|
427
|
+
return library;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
351
430
|
/** Replace an asset's parameters (validated by the owning plugin). */
|
|
352
431
|
async updateParameters(id, parameters) {
|
|
353
432
|
return this.#enqueue(() => this.#updateParameters(id, parameters));
|
|
@@ -66,6 +66,18 @@ export declare class FundusOperations {
|
|
|
66
66
|
reimportAsset(id: string, input?: unknown): Promise<FundusStateDto>;
|
|
67
67
|
/** Materialize a new source, then atomically replace an existing asset's raw file. */
|
|
68
68
|
replaceRawFromSource(id: string, options: ReplaceRawFromSourceOptions): Promise<FundusStateDto>;
|
|
69
|
+
/**
|
|
70
|
+
* Edit a Figma tag asset's tag, fileKey, and/or scale in place. Because in tag
|
|
71
|
+
* mode the tag IS the asset id, changing the tag renames the asset (moves the
|
|
72
|
+
* library key); changing only fileKey/scale is an in-place re-point. Mirrors
|
|
73
|
+
* `reimportAsset`'s three phases: preflight in the queue, materialize outside
|
|
74
|
+
* it, then commit atomically through `store.repointTagAsset`.
|
|
75
|
+
*/
|
|
76
|
+
retagFigmaAsset(id: string, input: {
|
|
77
|
+
tag?: string;
|
|
78
|
+
fileKey?: string;
|
|
79
|
+
scale?: 1 | 2 | 3 | 4;
|
|
80
|
+
}): Promise<FundusStateDto>;
|
|
69
81
|
/** Apply a PATCH body: parameters and/or manifests and/or a rename — in that order. */
|
|
70
82
|
patchAsset(id: string, body: PatchAssetRequestDto): Promise<FundusStateDto>;
|
|
71
83
|
moveAsset(id: string, folder: string): Promise<FundusStateDto>;
|
|
@@ -53,6 +53,18 @@ function figmaSlicePixelRatio(raw, type) {
|
|
|
53
53
|
return undefined;
|
|
54
54
|
return validateFigmaSourceParameters(raw.importSource.parameters).scale;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The Fundus tag of a Figma tag-mode import source, else null. In tag mode the
|
|
58
|
+
* tag IS the asset id, so this is the single seam that ties provenance to
|
|
59
|
+
* identity — used to assert id equality on import/reimport and to forbid
|
|
60
|
+
* renaming a tag-referenced asset. Only Figma sources are inspected.
|
|
61
|
+
*/
|
|
62
|
+
function figmaTagOf(importSource) {
|
|
63
|
+
if (importSource?.importer !== 'figma')
|
|
64
|
+
return null;
|
|
65
|
+
const params = validateFigmaSourceParameters(importSource.parameters);
|
|
66
|
+
return params.mode === 'tag' ? params.fundusTag : null;
|
|
67
|
+
}
|
|
56
68
|
export class FundusOperations {
|
|
57
69
|
#config;
|
|
58
70
|
#registry;
|
|
@@ -359,6 +371,10 @@ export class FundusOperations {
|
|
|
359
371
|
});
|
|
360
372
|
return this.#enqueueOperation(async () => {
|
|
361
373
|
this.#validateImportedRaw(raw, options.importer);
|
|
374
|
+
const tag = figmaTagOf(raw.importSource);
|
|
375
|
+
if (tag !== null && tag !== options.id) {
|
|
376
|
+
throw new SourceImportError('invalid-input', `A Figma tag import must use its tag as the asset id: the tag is "${tag}", not "${options.id}".`);
|
|
377
|
+
}
|
|
362
378
|
return this.#createImportedAsset(raw, options);
|
|
363
379
|
});
|
|
364
380
|
}
|
|
@@ -399,6 +415,10 @@ export class FundusOperations {
|
|
|
399
415
|
}
|
|
400
416
|
return this.#enqueueOperation(async () => {
|
|
401
417
|
this.#validateImportedRaw(raw, snapshot.importer.kind);
|
|
418
|
+
const tag = figmaTagOf(raw.importSource);
|
|
419
|
+
if (tag !== null && tag !== id) {
|
|
420
|
+
throw new SourceImportError('invalid-input', `A Figma tag reimport may not retarget asset "${id}" to a different tag "${tag}".`);
|
|
421
|
+
}
|
|
402
422
|
const plugin = serverPluginForType(snapshot.expected.type, this.#registry);
|
|
403
423
|
if (!plugin.descriptor.accepts.includes(raw.extension)) {
|
|
404
424
|
throw new SourceImportError('invalid-response', `The ${snapshot.expected.type} type does not accept ".${raw.extension}" files.`);
|
|
@@ -458,6 +478,10 @@ export class FundusOperations {
|
|
|
458
478
|
const importSource = raw.importSource;
|
|
459
479
|
return this.#enqueueOperation(async () => {
|
|
460
480
|
this.#validateImportedRaw(raw, preflight.importer.kind);
|
|
481
|
+
const tag = figmaTagOf(importSource);
|
|
482
|
+
if (tag !== null && tag !== id) {
|
|
483
|
+
throw new SourceImportError('invalid-input', `A Figma tag replacement must use its own tag as the asset id: the tag is "${tag}", not "${id}".`);
|
|
484
|
+
}
|
|
461
485
|
const plugin = serverPluginForType(preflight.expected.type, this.#registry);
|
|
462
486
|
if (!plugin.descriptor.accepts.includes(raw.extension)) {
|
|
463
487
|
throw new SourceImportError('invalid-response', `The ${preflight.expected.type} type does not accept ".${raw.extension}" files.`);
|
|
@@ -486,6 +510,92 @@ export class FundusOperations {
|
|
|
486
510
|
}
|
|
487
511
|
});
|
|
488
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Edit a Figma tag asset's tag, fileKey, and/or scale in place. Because in tag
|
|
515
|
+
* mode the tag IS the asset id, changing the tag renames the asset (moves the
|
|
516
|
+
* library key); changing only fileKey/scale is an in-place re-point. Mirrors
|
|
517
|
+
* `reimportAsset`'s three phases: preflight in the queue, materialize outside
|
|
518
|
+
* it, then commit atomically through `store.repointTagAsset`.
|
|
519
|
+
*/
|
|
520
|
+
async retagFigmaAsset(id, input) {
|
|
521
|
+
const snapshot = await this.#enqueueOperation(async () => {
|
|
522
|
+
const library = await this.#store.read();
|
|
523
|
+
this.#requireAsset(library, id);
|
|
524
|
+
const record = library.assets[id];
|
|
525
|
+
const currentTag = figmaTagOf(record.importSource);
|
|
526
|
+
if (currentTag === null || !record.importSource) {
|
|
527
|
+
throw new SourceImportError('invalid-input', `Asset "${id}" is not a Figma tag import.`);
|
|
528
|
+
}
|
|
529
|
+
const current = validateFigmaSourceParameters(record.importSource.parameters);
|
|
530
|
+
const newTag = input.tag ?? currentTag;
|
|
531
|
+
const fileKey = input.fileKey ?? current.fileKey;
|
|
532
|
+
const scale = input.scale ?? current.scale;
|
|
533
|
+
if (newTag !== id) {
|
|
534
|
+
const problem = assetIdError(newTag);
|
|
535
|
+
if (problem)
|
|
536
|
+
throw new SourceImportError('invalid-input', problem);
|
|
537
|
+
if (Object.hasOwn(library.assets, newTag)) {
|
|
538
|
+
throw new SourceImportError('invalid-input', `An asset with id "${newTag}" already exists.`);
|
|
539
|
+
}
|
|
540
|
+
// A tag change renames the asset; reject one that would dangle references.
|
|
541
|
+
const candidate = structuredClone(library);
|
|
542
|
+
candidate.assets[newTag] = candidate.assets[id];
|
|
543
|
+
delete candidate.assets[id];
|
|
544
|
+
await this.#assertNoIntroducedIssues(library, candidate);
|
|
545
|
+
}
|
|
546
|
+
try {
|
|
547
|
+
await verifyStableRawFile(join(this.#config.rawDir, record.raw), record.rawHash);
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
throw new SourceImportError('stale-import', 'The local raw file changed. Apply or discard that edit before retagging.');
|
|
551
|
+
}
|
|
552
|
+
const importer = this.#importers.require('figma');
|
|
553
|
+
return {
|
|
554
|
+
importer,
|
|
555
|
+
input: importer.validateInput({ mode: 'tag', tag: newTag, fileKey, scale, format: 'png' }),
|
|
556
|
+
newTag,
|
|
557
|
+
expected: {
|
|
558
|
+
raw: record.raw,
|
|
559
|
+
rawHash: record.rawHash,
|
|
560
|
+
type: record.type,
|
|
561
|
+
canonicalSource: canonicalizeParameters(record.importSource)
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
});
|
|
565
|
+
const raw = await snapshot.importer.importRaw(snapshot.input, {
|
|
566
|
+
config: this.#config,
|
|
567
|
+
fetch: globalThis.fetch
|
|
568
|
+
});
|
|
569
|
+
if (!raw.importSource) {
|
|
570
|
+
throw new SourceImportError('invalid-response', 'The Figma importer did not return provenance for the retagged source.');
|
|
571
|
+
}
|
|
572
|
+
const importSource = raw.importSource;
|
|
573
|
+
return this.#enqueueOperation(async () => {
|
|
574
|
+
this.#validateImportedRaw(raw, snapshot.importer.kind);
|
|
575
|
+
if (figmaTagOf(importSource) !== snapshot.newTag) {
|
|
576
|
+
throw new SourceImportError('invalid-response', `The Figma importer resolved a different tag than "${snapshot.newTag}".`);
|
|
577
|
+
}
|
|
578
|
+
const plugin = serverPluginForType(snapshot.expected.type, this.#registry);
|
|
579
|
+
if (!plugin.descriptor.accepts.includes(raw.extension)) {
|
|
580
|
+
throw new SourceImportError('invalid-response', `The ${snapshot.expected.type} type does not accept ".${raw.extension}" files.`);
|
|
581
|
+
}
|
|
582
|
+
// A retag may change the export scale, so always sync a slice's pixel ratio.
|
|
583
|
+
const pixelRatio = figmaSlicePixelRatio(raw, snapshot.expected.type);
|
|
584
|
+
let library;
|
|
585
|
+
try {
|
|
586
|
+
library = await this.#store.repointTagAsset(id, snapshot.newTag, raw.bytes, snapshot.expected, importSource, pixelRatio === undefined
|
|
587
|
+
? undefined
|
|
588
|
+
: (parameters) => plugin.descriptor.validateParameters(mergeParameterPatch(parameters, { pixelRatio })));
|
|
589
|
+
}
|
|
590
|
+
catch (cause) {
|
|
591
|
+
if (cause instanceof Error && cause.message === 'stale-import') {
|
|
592
|
+
throw new SourceImportError('stale-import', 'The asset or local raw file changed while Figma was exporting. Nothing was changed.');
|
|
593
|
+
}
|
|
594
|
+
throw cause;
|
|
595
|
+
}
|
|
596
|
+
return this.#afterMutation(library);
|
|
597
|
+
});
|
|
598
|
+
}
|
|
489
599
|
async #createImportedAsset(raw, options) {
|
|
490
600
|
this.#validateImportedRaw(raw);
|
|
491
601
|
const fileName = raw.suggestedFileName ?? `${options.id}.${raw.extension}`;
|
|
@@ -723,6 +833,9 @@ export class FundusOperations {
|
|
|
723
833
|
async #renameAsset(id, newId) {
|
|
724
834
|
const library = await this.#store.read();
|
|
725
835
|
this.#requireAsset(library, id);
|
|
836
|
+
if (figmaTagOf(library.assets[id].importSource) !== null) {
|
|
837
|
+
throw new SourceImportError('invalid-input', 'This asset is referenced by a Figma tag and cannot be renamed; re-import with a new tag instead.');
|
|
838
|
+
}
|
|
726
839
|
// A collision or invalid new id is the store's own (clearer) error; the
|
|
727
840
|
// candidate check covers what the store cannot see: broken references.
|
|
728
841
|
if (newId !== id && !Object.hasOwn(library.assets, newId)) {
|
|
@@ -986,7 +1099,14 @@ export class FundusOperations {
|
|
|
986
1099
|
gitBranch,
|
|
987
1100
|
library,
|
|
988
1101
|
assets,
|
|
989
|
-
importDefaults: {
|
|
1102
|
+
importDefaults: {
|
|
1103
|
+
figma: {
|
|
1104
|
+
scale: this.#config.import.figma.defaults.scale,
|
|
1105
|
+
...(this.#config.import.figma.defaultFileKey !== undefined
|
|
1106
|
+
? { defaultFileKey: this.#config.import.figma.defaultFileKey }
|
|
1107
|
+
: {})
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
990
1110
|
issues,
|
|
991
1111
|
presetNames,
|
|
992
1112
|
presetCapabilities,
|
package/package.json
CHANGED
|
Binary file
|