fundus 0.28.1 → 0.29.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 +41 -33
- package/build/client/_app/immutable/chunks/{OL1Dh1oP.js → BCmR1_e0.js} +1 -1
- package/build/client/_app/immutable/chunks/BCmR1_e0.js.br +0 -0
- package/build/client/_app/immutable/chunks/BCmR1_e0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{apKHI4vq.js → C0uU_Rhe.js} +2 -2
- package/build/client/_app/immutable/chunks/C0uU_Rhe.js.br +0 -0
- package/build/client/_app/immutable/chunks/C0uU_Rhe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{g2rma9Ym.js → CMJ10zbJ.js} +1 -1
- package/build/client/_app/immutable/chunks/CMJ10zbJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CMJ10zbJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{QlQ-jeIO.js → kkVEh3_1.js} +1 -1
- package/build/client/_app/immutable/chunks/kkVEh3_1.js.br +2 -0
- package/build/client/_app/immutable/chunks/kkVEh3_1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/wBLhaPea.js +1 -0
- package/build/client/_app/immutable/chunks/wBLhaPea.js.br +0 -0
- package/build/client/_app/immutable/chunks/wBLhaPea.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.DpZM49_s.js → app.DCXSqiQ1.js} +2 -2
- package/build/client/_app/immutable/entry/app.DCXSqiQ1.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DCXSqiQ1.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BOUpxuMU.js +1 -0
- package/build/client/_app/immutable/entry/start.BOUpxuMU.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BOUpxuMU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.Wqvn8XD-.js → 0.CsqkK05g.js} +1 -1
- package/build/client/_app/immutable/nodes/0.CsqkK05g.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CsqkK05g.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.Z-UvvLkP.js → 1.BVSXvtU9.js} +1 -1
- package/build/client/_app/immutable/nodes/1.BVSXvtU9.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.BVSXvtU9.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.lu89W_qJ.js → 2.BJEXWmrT.js} +1 -1
- package/build/client/_app/immutable/nodes/2.BJEXWmrT.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.BJEXWmrT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.Dt32EAoq.js → 3.BDMmDyL1.js} +1 -1
- package/build/client/_app/immutable/nodes/3.BDMmDyL1.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BDMmDyL1.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.WipzcX8_.js → 4.dd49_qQE.js} +1 -1
- package/build/client/_app/immutable/nodes/4.dd49_qQE.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.dd49_qQE.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-BZSZNuAd.js → context.js-DFZgDxCD.js} +157 -95
- package/build/server/chunks/chunks/context.js-DFZgDxCD.js.map +1 -0
- package/build/server/chunks/chunks/{contract.js-B4JdXFgm.js → contract.js-DGZXpcNX.js} +7 -7
- package/build/server/chunks/chunks/{contract.js-B4JdXFgm.js.map → contract.js-DGZXpcNX.js.map} +1 -1
- package/build/server/chunks/chunks/{core.js-BHxa3VBo.js → core.js-AGKlECJ0.js} +32 -32
- package/build/server/chunks/chunks/core.js-AGKlECJ0.js.map +1 -0
- package/build/server/chunks/chunks/{exports.js-D0dALD2Q.js → exports.js-BsVKSGJk.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-D0dALD2Q.js.map → exports.js-BsVKSGJk.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-DYYKvppy.js → http.js-BQhjMaW9.js} +2 -2
- package/build/server/chunks/chunks/{http.js-DYYKvppy.js.map → http.js-BQhjMaW9.js.map} +1 -1
- package/build/server/chunks/chunks/{internal.js-CJkB_gzV.js → internal.js-DJGcNryj.js} +3 -3
- package/build/server/chunks/chunks/{internal.js-CJkB_gzV.js.map → internal.js-DJGcNryj.js.map} +1 -1
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{manifest-membership.js-BkrPsM_r.js → manifest-membership.js-DkHaPk2F.js} +2 -2
- package/build/server/chunks/chunks/{manifest-membership.js-BkrPsM_r.js.map → manifest-membership.js-DkHaPk2F.js.map} +1 -1
- package/build/server/chunks/chunks/{raw-reconciliation.js-DPnNXe0b.js → raw-reconciliation.js-D2sI2C-t.js} +3 -3
- package/build/server/chunks/chunks/raw-reconciliation.js-D2sI2C-t.js.map +1 -0
- package/build/server/chunks/chunks/{raw-reconciliation2.js-DKOpJ8if.js → raw-reconciliation2.js-CMpUvuMd.js} +2 -2
- package/build/server/chunks/chunks/{raw-reconciliation2.js-DKOpJ8if.js.map → raw-reconciliation2.js-CMpUvuMd.js.map} +1 -1
- package/build/server/chunks/chunks/{shared.js-D3ZDVUlh.js → shared.js-BtmYg0tL.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-D3ZDVUlh.js.map → shared.js-BtmYg0tL.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-CqWW_v-5.js → utils.js-CqXk0Ug6.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-CqWW_v-5.js.map → utils.js-CqXk0Ug6.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-Dw2MW5Tm.js → _server.ts.js-DKGD9Jk2.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-Dw2MW5Tm.js.map → _server.ts.js-DKGD9Jk2.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-GQDuJGug.js → _server.ts.js-C1UfF0EO.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-GQDuJGug.js.map → _server.ts.js-C1UfF0EO.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-JbLSv7Xx.js → _server.ts.js-DpASkk00.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-JbLSv7Xx.js.map → _server.ts.js-DpASkk00.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-BPae-BYF.js → _server.ts.js-C3jU2vC8.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-BPae-BYF.js.map → _server.ts.js-C3jU2vC8.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/{_server.ts.js-BafrrUxu.js → _server.ts.js-B7OwufF3.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/{_server.ts.js-BafrrUxu.js.map → _server.ts.js-B7OwufF3.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-UOlSNEi9.js → _server.ts.js-COhg1nYk.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-UOlSNEi9.js.map → _server.ts.js-COhg1nYk.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-QE1vUuAx.js → _server.ts.js-DAEE17eM.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-QE1vUuAx.js.map → _server.ts.js-DAEE17eM.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DIVfppGt.js → _server.ts.js-BrZSS4gm.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-DIVfppGt.js.map → _server.ts.js-BrZSS4gm.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/events/{_server.ts.js-B6R_kqP8.js → _server.ts.js-Cfst-Brd.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/events/{_server.ts.js-B6R_kqP8.js.map → _server.ts.js-Cfst-Brd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-CertbDsH.js → _server.ts.js-aN_h2Ifo.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-CertbDsH.js.map → _server.ts.js-aN_h2Ifo.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-DEIF3Hu2.js → _server.ts.js-CX2A66ij.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-DEIF3Hu2.js.map → _server.ts.js-CX2A66ij.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-DNFsNGr4.js → _server.ts.js-CwTiaheP.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-DNFsNGr4.js.map → _server.ts.js-CwTiaheP.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DDW8XRPe.js → _server.ts.js-fIbsr_2e.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DDW8XRPe.js.map → _server.ts.js-fIbsr_2e.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-C222-xli.js → _server.ts.js-Dvo1bsLi.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-C222-xli.js.map → _server.ts.js-Dvo1bsLi.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-CI1P1uyd.js → _server.ts.js-VLg1G3mN.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-CI1P1uyd.js.map → _server.ts.js-VLg1G3mN.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-DpiUwaSh.js → _server.ts.js-DqctsOop.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-DpiUwaSh.js.map → _server.ts.js-DqctsOop.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DT0VPqPT.js → _server.ts.js-CoxSU5iO.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DT0VPqPT.js.map → _server.ts.js-CoxSU5iO.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-DBpHM42g.js → _server.ts.js-wUxsLZdr.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-DBpHM42g.js.map → _server.ts.js-wUxsLZdr.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-Cxu5b6Au.js → _server.ts.js-BKsF0Oit.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-Cxu5b6Au.js.map → _server.ts.js-BKsF0Oit.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-W4dhMo8Y.js → _server.ts.js-Dk9oOZwM.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-W4dhMo8Y.js.map → _server.ts.js-Dk9oOZwM.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-DJ_adxbT.js → _server.ts.js-DxjNmmtL.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-DJ_adxbT.js.map → _server.ts.js-DxjNmmtL.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-BisoHihD.js → _server.ts.js-DD0b38VV.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-BisoHihD.js.map → _server.ts.js-DD0b38VV.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-D4jLLuko.js → _server.ts.js-DId6F2i_.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-D4jLLuko.js.map → _server.ts.js-DId6F2i_.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-Dfv2ItjT.js → _server.ts.js-DKKkGoK2.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-Dfv2ItjT.js.map → _server.ts.js-DKKkGoK2.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-oHJX5INm.js → hooks.server.js-B5Ctvorj.js} +6 -6
- package/build/server/chunks/entries/{hooks.server.js-oHJX5INm.js.map → hooks.server.js-B5Ctvorj.js.map} +1 -1
- package/build/server/chunks/{handler-BAe0c9yw.js → handler-DwVfBx6r.js} +5 -5
- package/build/server/chunks/{handler-BAe0c9yw.js.map → handler-DwVfBx6r.js.map} +1 -1
- package/build/server/chunks/{index.js-DUYD4JB0.js → index.js-DgPuPQJn.js} +5 -5
- package/build/server/chunks/{index.js-DUYD4JB0.js.map → index.js-DgPuPQJn.js.map} +1 -1
- package/build/server/chunks/{manifest.js-zwun-T7O.js → manifest.js-B054EcAf.js} +31 -31
- package/build/server/chunks/{manifest.js-zwun-T7O.js.map → manifest.js-B054EcAf.js.map} +1 -1
- package/build/server/chunks/nodes/{0.js-BmkXwDRY.js → 0.js-DoHZTmnT.js} +2 -2
- package/build/server/chunks/nodes/{0.js-BmkXwDRY.js.map → 0.js-DoHZTmnT.js.map} +1 -1
- package/build/server/chunks/nodes/1.js-BcPlsloO.js +7 -0
- package/build/server/chunks/nodes/{1.js-xbOwEHCW.js.map → 1.js-BcPlsloO.js.map} +1 -1
- package/build/server/chunks/nodes/{2.js-BuNw1vTZ.js → 2.js-5qCXkvzl.js} +2 -2
- package/build/server/chunks/nodes/{2.js-BuNw1vTZ.js.map → 2.js-5qCXkvzl.js.map} +1 -1
- package/build/server/chunks/nodes/3.js-D6fAMX1w.js +7 -0
- package/build/server/chunks/nodes/{3.js-Dpr_DvNU.js.map → 3.js-D6fAMX1w.js.map} +1 -1
- package/build/server/chunks/nodes/4.js-CGkeT0SQ.js +7 -0
- package/build/server/chunks/nodes/{4.js-14Lypd4o.js.map → 4.js-CGkeT0SQ.js.map} +1 -1
- package/dist/cli.js +3 -4
- package/dist/core/figma-tag-links.d.ts +66 -0
- package/dist/core/figma-tag-links.js +97 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/server/cli-commands.d.ts +1 -1
- package/dist/server/cli-commands.js +1 -1
- package/dist/server/cli-figma-tags.d.ts +1 -1
- package/dist/server/cli-figma-tags.js +149 -88
- package/dist/server/figma-tags.d.ts +72 -24
- package/dist/server/figma-tags.js +130 -79
- package/dist/server/importers/figma.d.ts +20 -15
- package/dist/server/importers/figma.js +172 -92
- package/package.json +1 -1
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js +0 -1
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/OL1Dh1oP.js.br +0 -0
- package/build/client/_app/immutable/chunks/OL1Dh1oP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/QlQ-jeIO.js.br +0 -2
- package/build/client/_app/immutable/chunks/QlQ-jeIO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/apKHI4vq.js.br +0 -0
- package/build/client/_app/immutable/chunks/apKHI4vq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/g2rma9Ym.js.br +0 -0
- package/build/client/_app/immutable/chunks/g2rma9Ym.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DpZM49_s.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DpZM49_s.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js +0 -1
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js.br +0 -0
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Wqvn8XD-.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Wqvn8XD-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.Z-UvvLkP.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Z-UvvLkP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.lu89W_qJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.lu89W_qJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.Dt32EAoq.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.Dt32EAoq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.WipzcX8_.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.WipzcX8_.js.gz +0 -0
- package/build/server/chunks/chunks/context.js-BZSZNuAd.js.map +0 -1
- package/build/server/chunks/chunks/core.js-BHxa3VBo.js.map +0 -1
- package/build/server/chunks/chunks/raw-reconciliation.js-DPnNXe0b.js.map +0 -1
- package/build/server/chunks/nodes/1.js-xbOwEHCW.js +0 -7
- package/build/server/chunks/nodes/3.js-Dpr_DvNU.js +0 -7
- package/build/server/chunks/nodes/4.js-14Lypd4o.js +0 -7
- package/dist/core/figma-tag-script.d.ts +0 -154
- package/dist/core/figma-tag-script.js +0 -248
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Planning for
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Planning and REST writes for `fundus figma set-tag` / `delete-tag`. Fundus
|
|
3
|
+
* confirms the file's tag links against the nodes that still exist (one link
|
|
4
|
+
* list plus one `/nodes` call), plans purely on that snapshot and the library,
|
|
5
|
+
* then writes the links in an order where an interruption can at worst leave
|
|
6
|
+
* a tag on two nodes (`ambiguous-tag`), never lose it.
|
|
7
7
|
*/
|
|
8
|
-
import { assetIdError
|
|
9
|
-
import {
|
|
10
|
-
import { LibraryStore } from './library-store.js';
|
|
8
|
+
import { assetIdError } from '../core/index.js';
|
|
9
|
+
import { createFigmaTagLink, deleteFigmaDevResource, fetchFigmaNodeSummaries, listFigmaTagEntries, validateFigmaSourceParameters } from './importers/figma.js';
|
|
11
10
|
/** An expected refusal that carries the file's current tags so the caller can pick another. */
|
|
12
11
|
export class FigmaTagConflictError extends Error {
|
|
13
12
|
existingTags;
|
|
@@ -16,26 +15,24 @@ export class FigmaTagConflictError extends Error {
|
|
|
16
15
|
this.existingTags = existingTags;
|
|
17
16
|
}
|
|
18
17
|
}
|
|
19
|
-
/** Normalize a `12:34` / `12-34` node id; Fundus
|
|
18
|
+
/** Normalize a `12:34` / `12-34` node id; Fundus tags only this id form. */
|
|
20
19
|
export function parseFigmaNodeId(value) {
|
|
21
20
|
if (!/^\d+[-:]\d+$/.test(value)) {
|
|
22
|
-
throw new Error(`"${value}" is not a taggable Figma node id — use the form 12:34 (or 12-34 as in a node-id link parameter).
|
|
21
|
+
throw new Error(`"${value}" is not a taggable Figma node id — use the form 12:34 (or 12-34 as in a node-id link parameter). Layers inside instances (ids starting with "I") cannot be tagged.`);
|
|
23
22
|
}
|
|
24
23
|
return value.replace('-', ':');
|
|
25
24
|
}
|
|
26
|
-
function tagListing(
|
|
27
|
-
return
|
|
28
|
-
.filter((node) => node.tag !== null)
|
|
29
|
-
.map((node) => ({
|
|
30
|
-
tag: node.tag,
|
|
31
|
-
nodeId: node.nodeId,
|
|
32
|
-
nodeName: node.nodeName,
|
|
33
|
-
page: node.page
|
|
34
|
-
}))
|
|
35
|
-
.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
25
|
+
function tagListing(tags) {
|
|
26
|
+
return tags.map(({ tag, nodeId, nodeName }) => ({ tag, nodeId, nodeName }));
|
|
36
27
|
}
|
|
37
28
|
function describeNode(node) {
|
|
38
|
-
return `"${node.nodeName}" (${node.nodeId})
|
|
29
|
+
return `"${node.nodeName}" (${node.nodeId})`;
|
|
30
|
+
}
|
|
31
|
+
function quotedTags(tags) {
|
|
32
|
+
return `${tags.length === 1 ? 'tag' : 'tags'} ${tags.map((entry) => `"${entry.tag}"`).join(', ')}`;
|
|
33
|
+
}
|
|
34
|
+
function deleteWrite({ nodeId, nodeName, tag, linkId }) {
|
|
35
|
+
return { action: 'delete', nodeId, nodeName, tag, linkId };
|
|
39
36
|
}
|
|
40
37
|
/** Whether `record` is a node-mode Figma import of `nodeId` in `fileKey`. */
|
|
41
38
|
function isNodeImportOf(record, fileKey, nodeId) {
|
|
@@ -54,21 +51,32 @@ function tagImportedAsset(library, fileKey, tag) {
|
|
|
54
51
|
? tag
|
|
55
52
|
: null;
|
|
56
53
|
}
|
|
57
|
-
export function planSetFigmaTag(request, { library,
|
|
54
|
+
export function planSetFigmaTag(request, { library, snapshot }) {
|
|
58
55
|
const { fileKey, nodeId, tag, force } = request;
|
|
59
56
|
const problem = assetIdError(tag);
|
|
60
57
|
if (problem)
|
|
61
58
|
throw new Error(problem);
|
|
62
|
-
const
|
|
63
|
-
|
|
59
|
+
const { tags, target } = snapshot;
|
|
60
|
+
if (!target)
|
|
61
|
+
throw new Error(`Node ${nodeId} does not exist in Figma file "${fileKey}".`);
|
|
62
|
+
if (target.nodeType === 'DOCUMENT' || target.nodeType === 'CANVAS') {
|
|
63
|
+
throw new Error(`Node ${describeNode(target)} is a ${target.nodeType} node and cannot be tagged — tag a layer on the page.`);
|
|
64
|
+
}
|
|
65
|
+
const existingTags = tagListing(tags);
|
|
64
66
|
const warnings = [];
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
const others = tags.filter((entry) => entry.tag === tag && entry.nodeId !== nodeId);
|
|
68
|
+
const ownOther = tags.filter((entry) => entry.nodeId === nodeId && entry.tag !== tag);
|
|
69
|
+
const carries = tags.some((entry) => entry.nodeId === nodeId && entry.tag === tag);
|
|
70
|
+
if (others.length > 0) {
|
|
71
|
+
const described = others.map(describeNode).join(', ');
|
|
68
72
|
if (!force) {
|
|
69
|
-
throw new FigmaTagConflictError(
|
|
73
|
+
throw new FigmaTagConflictError(carries
|
|
74
|
+
? `Tag "${tag}" is also on ${described} — pass --force to remove it there and keep it only on node ${describeNode(target)} (existing tags are listed).`
|
|
75
|
+
: `Tag "${tag}" is already used by ${described} — choose another tag, or pass --force to move it to node ${describeNode(target)} (existing tags are listed).`, existingTags);
|
|
70
76
|
}
|
|
71
|
-
warnings.push(
|
|
77
|
+
warnings.push(carries
|
|
78
|
+
? `Removes tag "${tag}" from ${described}; it stays on node ${describeNode(target)}.`
|
|
79
|
+
: `Moves tag "${tag}" from ${described} to node ${describeNode(target)}.`);
|
|
72
80
|
}
|
|
73
81
|
const existing = Object.hasOwn(library.assets, tag) ? library.assets[tag] : null;
|
|
74
82
|
if (existing && tagImportedAsset(library, fileKey, tag) === null) {
|
|
@@ -88,75 +96,77 @@ export function planSetFigmaTag(request, { library, document }) {
|
|
|
88
96
|
warnings.push(`Fundus asset "${tag}" already exists from another source — afterwards run ${replace} to switch it to this tag; that replaces its current source.`);
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!force) {
|
|
98
|
-
throw new Error(`Node ${describeNode(node)} already carries tag "${tag}" — nothing to do. Pass --force to regenerate the script anyway (e.g. to repair its annotation).`);
|
|
99
|
-
}
|
|
100
|
-
warnings.push(`Node ${nodeId} already carries tag "${tag}"; the script rewrites it.`);
|
|
101
|
-
}
|
|
102
|
-
else if (previousTag !== null) {
|
|
103
|
-
const orphaned = tagImportedAsset(library, fileKey, previousTag);
|
|
104
|
-
const followUp = orphaned
|
|
105
|
-
? ` Fundus asset "${orphaned}" is imported from that tag — afterwards run \`fundus asset reimport ${orphaned} --figma-tag ${tag}\` (renames it to "${tag}").`
|
|
106
|
-
: '';
|
|
99
|
+
if (ownOther.length > 0) {
|
|
100
|
+
const followUp = ownOther
|
|
101
|
+
.map((entry) => tagImportedAsset(library, fileKey, entry.tag))
|
|
102
|
+
.filter((orphaned) => orphaned !== null)
|
|
103
|
+
.map((orphaned) => ` Fundus asset "${orphaned}" is imported from that tag — afterwards run \`fundus asset reimport ${orphaned} --figma-tag ${tag}\` (renames it to "${tag}").`)
|
|
104
|
+
.join('');
|
|
107
105
|
if (!force) {
|
|
108
|
-
throw new
|
|
106
|
+
throw new FigmaTagConflictError(`Node ${describeNode(target)} already carries ${quotedTags(ownOther)}. Pass --force to replace ${ownOther.length === 1 ? 'it' : 'them'}.${followUp}`, existingTags);
|
|
109
107
|
}
|
|
110
|
-
warnings.push(`Replaces
|
|
108
|
+
warnings.push(`Replaces ${quotedTags(ownOther)} on node ${describeNode(target)}.${followUp}`);
|
|
111
109
|
}
|
|
112
|
-
|
|
110
|
+
const writes = [
|
|
111
|
+
...(carries ? [] : [{ action: 'create', nodeId, nodeName: target.nodeName, tag }]),
|
|
112
|
+
...ownOther.map(deleteWrite),
|
|
113
|
+
...others.map(deleteWrite)
|
|
114
|
+
];
|
|
115
|
+
if (tagImportedAsset(library, fileKey, tag) !== null && writes.length > 0) {
|
|
113
116
|
warnings.push(`Fundus asset "${tag}" is imported from this tag; its next \`fundus asset reimport ${tag}\` resolves to this node.`);
|
|
114
117
|
}
|
|
115
118
|
return {
|
|
116
119
|
fileKey,
|
|
117
120
|
nodeId,
|
|
121
|
+
nodeName: target.nodeName,
|
|
118
122
|
tag,
|
|
119
|
-
previousTag,
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
previousTag: ownOther[0]?.tag ?? (carries ? tag : null),
|
|
124
|
+
movedFrom: others.map(({ nodeId, nodeName }) => ({ nodeId, nodeName })),
|
|
125
|
+
writes,
|
|
126
|
+
warnings
|
|
122
127
|
};
|
|
123
128
|
}
|
|
124
|
-
export function planDeleteFigmaTag(request, { library,
|
|
129
|
+
export function planDeleteFigmaTag(request, { library, snapshot }) {
|
|
125
130
|
const { fileKey, force } = request;
|
|
126
131
|
if (request.tag !== undefined) {
|
|
127
132
|
const problem = assetIdError(request.tag);
|
|
128
133
|
if (problem)
|
|
129
134
|
throw new Error(problem);
|
|
130
135
|
}
|
|
131
|
-
const
|
|
132
|
-
const existingTags = tagListing(
|
|
133
|
-
let
|
|
136
|
+
const { tags, target } = snapshot;
|
|
137
|
+
const existingTags = tagListing(tags);
|
|
138
|
+
let holders;
|
|
134
139
|
if (request.nodeId !== undefined) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
throw new Error(`Node ${request.nodeId} is not in Figma file "${fileKey}".`);
|
|
140
|
+
if (!target) {
|
|
141
|
+
throw new Error(`Node ${request.nodeId} does not exist in Figma file "${fileKey}".`);
|
|
138
142
|
}
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
const own = tags.filter((entry) => entry.nodeId === request.nodeId);
|
|
144
|
+
if (own.length === 0) {
|
|
145
|
+
throw new FigmaTagConflictError(`Node ${describeNode(target)} carries no Fundus tag (existing tags are listed).`, existingTags);
|
|
141
146
|
}
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
holders = request.tag === undefined ? own : own.filter((entry) => entry.tag === request.tag);
|
|
148
|
+
if (holders.length === 0) {
|
|
149
|
+
throw new FigmaTagConflictError(`Node ${describeNode(target)} carries ${quotedTags(own)}, not "${request.tag}".`, existingTags);
|
|
150
|
+
}
|
|
151
|
+
if (new Set(holders.map((entry) => entry.tag)).size > 1) {
|
|
152
|
+
throw new FigmaTagConflictError(`Node ${describeNode(target)} carries ${quotedTags(own)} — pass --tag to pick one.`, existingTags);
|
|
144
153
|
}
|
|
145
|
-
node = found;
|
|
146
154
|
}
|
|
147
155
|
else {
|
|
148
|
-
|
|
149
|
-
if (
|
|
156
|
+
holders = tags.filter((entry) => entry.tag === request.tag);
|
|
157
|
+
if (holders.length === 0) {
|
|
150
158
|
throw new FigmaTagConflictError(`No node carries tag "${request.tag}" in Figma file "${fileKey}" (existing tags are listed).`, existingTags);
|
|
151
159
|
}
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
const nodeIds = [...new Set(holders.map((entry) => entry.nodeId))];
|
|
161
|
+
if (nodeIds.length > 1) {
|
|
162
|
+
throw new FigmaTagConflictError(`Tag "${request.tag}" is on ${nodeIds.length} nodes (${nodeIds.join(', ')}) — pass --node-id to pick one.`, existingTags);
|
|
154
163
|
}
|
|
155
|
-
node = matches[0];
|
|
156
164
|
}
|
|
157
|
-
const
|
|
165
|
+
const [holder] = holders;
|
|
158
166
|
const warnings = [];
|
|
159
|
-
|
|
167
|
+
// Deleting one of several holders fixes an ambiguous tag rather than losing it.
|
|
168
|
+
const keptElsewhere = tags.some((entry) => entry.tag === holder.tag && entry.nodeId !== holder.nodeId);
|
|
169
|
+
const imported = keptElsewhere ? null : tagImportedAsset(library, fileKey, holder.tag);
|
|
160
170
|
if (imported) {
|
|
161
171
|
const consequence = `Fundus asset "${imported}" is imported from this tag; without it, \`fundus asset reimport ${imported}\` fails until the tag is set again.`;
|
|
162
172
|
if (!force)
|
|
@@ -165,15 +175,56 @@ export function planDeleteFigmaTag(request, { library, document }) {
|
|
|
165
175
|
}
|
|
166
176
|
return {
|
|
167
177
|
fileKey,
|
|
168
|
-
nodeId:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
178
|
+
nodeId: holder.nodeId,
|
|
179
|
+
nodeName: holder.nodeName,
|
|
180
|
+
tag: holder.tag,
|
|
181
|
+
writes: holders.map(deleteWrite),
|
|
182
|
+
warnings
|
|
172
183
|
};
|
|
173
184
|
}
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
/**
|
|
186
|
+
* The file's tag links confirmed against the nodes that exist: one link list,
|
|
187
|
+
* then one `/nodes` call over every tagged node plus the target. Links on
|
|
188
|
+
* missing nodes are orphaned and dropped, so nothing ever touches them.
|
|
189
|
+
*/
|
|
190
|
+
export async function loadFigmaTagSnapshot(context, fileKey, targetNodeId) {
|
|
191
|
+
const entries = await listFigmaTagEntries(fileKey, context);
|
|
192
|
+
const nodeIds = new Set(entries.map((entry) => entry.nodeId));
|
|
193
|
+
if (targetNodeId !== undefined)
|
|
194
|
+
nodeIds.add(targetNodeId);
|
|
195
|
+
const nodes = new Map((await fetchFigmaNodeSummaries(fileKey, [...nodeIds], context)).map((node) => [
|
|
196
|
+
node.nodeId,
|
|
197
|
+
node
|
|
198
|
+
]));
|
|
199
|
+
const tags = [];
|
|
200
|
+
for (const { tag, nodeId, linkId } of entries) {
|
|
201
|
+
const node = nodes.get(nodeId);
|
|
202
|
+
if (node)
|
|
203
|
+
tags.push({ tag, nodeId, nodeName: node.nodeName, nodeType: node.nodeType, linkId });
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
tags,
|
|
207
|
+
target: targetNodeId === undefined ? null : (nodes.get(targetNodeId) ?? null)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** Runs the writes in order and stops at the first failure; retries live in the REST client. */
|
|
211
|
+
export async function applyFigmaTagWrites(fileKey, writes, context) {
|
|
212
|
+
for (const [index, write] of writes.entries()) {
|
|
213
|
+
try {
|
|
214
|
+
if (write.action === 'create') {
|
|
215
|
+
await createFigmaTagLink(fileKey, write.nodeId, write.tag, context);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
await deleteFigmaDevResource(fileKey, write.linkId, context);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (cause) {
|
|
222
|
+
return {
|
|
223
|
+
written: writes.slice(0, index),
|
|
224
|
+
failure: { write, message: cause instanceof Error ? cause.message : String(cause) },
|
|
225
|
+
notWritten: writes.slice(index)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { written: [...writes], failure: null, notWritten: [] };
|
|
179
230
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FigmaTagEntry } from '../../core/index.js';
|
|
1
2
|
import { type ImporterContext, type ServerSourceImporter } from './contract.js';
|
|
2
3
|
import { type PinnedHttpsTarget } from './pinned-https.js';
|
|
3
4
|
export interface FigmaNodeSourceParameters {
|
|
@@ -10,9 +11,9 @@ export interface FigmaNodeSourceParameters {
|
|
|
10
11
|
export interface FigmaTagSourceParameters {
|
|
11
12
|
mode: 'tag';
|
|
12
13
|
fileKey: string;
|
|
13
|
-
/** The Fundus asset id carried in Figma as
|
|
14
|
+
/** The Fundus asset id, carried in Figma as a Fundus tag link (Dev Resource) on the node. */
|
|
14
15
|
fundusTag: string;
|
|
15
|
-
/**
|
|
16
|
+
/** Node id (colon-form) of the last tag resolution; every reimport resolves the tag again. */
|
|
16
17
|
nodeId: string;
|
|
17
18
|
format: 'png';
|
|
18
19
|
scale: 1 | 2 | 3 | 4;
|
|
@@ -40,22 +41,26 @@ export declare function parseFigmaFileKey(value: string): string;
|
|
|
40
41
|
export declare function validateFigmaSourceParameters(value: unknown): FigmaSourceParameters;
|
|
41
42
|
export declare function resolveSafeDownloadTarget(value: string | URL): Promise<PinnedHttpsTarget>;
|
|
42
43
|
export declare function assertSafeDownloadUrl(value: string | URL): Promise<URL>;
|
|
43
|
-
/**
|
|
44
|
-
export
|
|
44
|
+
/** Every Fundus tag link in the file (orphaned ones included); each entry has its `linkId`. */
|
|
45
|
+
export declare function listFigmaTagEntries(fileKey: string, context: ImporterContext): Promise<Array<FigmaTagEntry & {
|
|
46
|
+
linkId: string;
|
|
47
|
+
}>>;
|
|
48
|
+
export interface FigmaNodeSummary {
|
|
45
49
|
nodeId: string;
|
|
46
50
|
nodeName: string;
|
|
47
|
-
|
|
48
|
-
page: string;
|
|
49
|
-
/** The node's Fundus tag, or null when untagged. */
|
|
50
|
-
tag: string | null;
|
|
51
|
+
nodeType: string;
|
|
51
52
|
}
|
|
53
|
+
/** The requested nodes that exist; Figma answers deleted or unknown ids with `null` (dropped). */
|
|
54
|
+
export declare function fetchFigmaNodeSummaries(fileKey: string, nodeIds: readonly string[], context: ImporterContext): Promise<FigmaNodeSummary[]>;
|
|
55
|
+
export declare function createFigmaTagLink(fileKey: string, nodeId: string, tag: string, context: ImporterContext): Promise<void>;
|
|
56
|
+
export declare function deleteFigmaDevResource(fileKey: string, linkId: string, context: ImporterContext): Promise<void>;
|
|
52
57
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
58
|
+
* The one renderable node carrying `tag`. All candidates go into a single
|
|
59
|
+
* `/images` call, which answers deleted nodes with `null`; a live but
|
|
60
|
+
* unrenderable second candidate therefore goes unnoticed.
|
|
56
61
|
*/
|
|
57
|
-
export declare function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
export declare function resolveTaggedNode(fileKey: string, tag: string, scale: 1 | 2 | 3 | 4, context: ImporterContext): Promise<{
|
|
63
|
+
nodeId: string;
|
|
64
|
+
imageUrl: string;
|
|
65
|
+
}>;
|
|
61
66
|
export declare const figmaSourceImporter: ServerSourceImporter<FigmaImportInput, FigmaSourceParameters>;
|