fundus 0.2.0 → 0.4.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.
Files changed (217) hide show
  1. package/README.md +67 -2
  2. package/build/client/_app/immutable/assets/2.B5tQm8hR.css +1 -0
  3. package/build/client/_app/immutable/assets/2.B5tQm8hR.css.br +0 -0
  4. package/build/client/_app/immutable/assets/2.B5tQm8hR.css.gz +0 -0
  5. package/build/client/_app/immutable/assets/{3.ChSpCG-T.css → 3.CmXD8k9d.css} +1 -1
  6. package/build/client/_app/immutable/assets/3.CmXD8k9d.css.br +0 -0
  7. package/build/client/_app/immutable/assets/3.CmXD8k9d.css.gz +0 -0
  8. package/build/client/_app/immutable/assets/{LibraryContextMenu.BYvU4SY7.css → LibraryContextMenu.CVb-Lqdf.css} +1 -1
  9. package/build/client/_app/immutable/assets/LibraryContextMenu.CVb-Lqdf.css.br +0 -0
  10. package/build/client/_app/immutable/assets/{LibraryContextMenu.BYvU4SY7.css.gz → LibraryContextMenu.CVb-Lqdf.css.gz} +0 -0
  11. package/build/client/_app/immutable/chunks/{C8W3KwDq.js → BPiGyn-T.js} +1 -1
  12. package/build/client/_app/immutable/chunks/BPiGyn-T.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/BPiGyn-T.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/{CP-bnOxU.js → BoEHtxM8.js} +1 -1
  15. package/build/client/_app/immutable/chunks/BoEHtxM8.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/BoEHtxM8.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/C-T1_s5P.js +1 -0
  18. package/build/client/_app/immutable/chunks/C-T1_s5P.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/C-T1_s5P.js.gz +0 -0
  20. package/build/client/_app/immutable/chunks/{ByVSyIj7.js → CeJsL6hj.js} +1 -1
  21. package/build/client/_app/immutable/chunks/CeJsL6hj.js.br +0 -0
  22. package/build/client/_app/immutable/chunks/CeJsL6hj.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/{Db8LRH6X.js → Cq4Yaq35.js} +3 -3
  24. package/build/client/_app/immutable/chunks/Cq4Yaq35.js.br +0 -0
  25. package/build/client/_app/immutable/chunks/Cq4Yaq35.js.gz +0 -0
  26. package/build/client/_app/immutable/chunks/D5sQSsUv.js +1 -0
  27. package/build/client/_app/immutable/chunks/D5sQSsUv.js.br +0 -0
  28. package/build/client/_app/immutable/chunks/D5sQSsUv.js.gz +0 -0
  29. package/build/client/_app/immutable/entry/{app.nM6SgHKJ.js → app.BQ4TfPHG.js} +2 -2
  30. package/build/client/_app/immutable/entry/app.BQ4TfPHG.js.br +0 -0
  31. package/build/client/_app/immutable/entry/app.BQ4TfPHG.js.gz +0 -0
  32. package/build/client/_app/immutable/entry/start.COP9tIRH.js +1 -0
  33. package/build/client/_app/immutable/entry/start.COP9tIRH.js.br +0 -0
  34. package/build/client/_app/immutable/entry/start.COP9tIRH.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/0.CftchwYV.js +2 -0
  36. package/build/client/_app/immutable/nodes/0.CftchwYV.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/0.CftchwYV.js.gz +0 -0
  38. package/build/client/_app/immutable/nodes/{1.dMklHTc2.js → 1.C8DOqctV.js} +1 -1
  39. package/build/client/_app/immutable/nodes/1.C8DOqctV.js.br +1 -0
  40. package/build/client/_app/immutable/nodes/1.C8DOqctV.js.gz +0 -0
  41. package/build/client/_app/immutable/nodes/2.BR8OVsS9.js +3 -0
  42. package/build/client/_app/immutable/nodes/2.BR8OVsS9.js.br +0 -0
  43. package/build/client/_app/immutable/nodes/2.BR8OVsS9.js.gz +0 -0
  44. package/build/client/_app/immutable/nodes/3.BO5hcxIH.js +1 -0
  45. package/build/client/_app/immutable/nodes/3.BO5hcxIH.js.br +0 -0
  46. package/build/client/_app/immutable/nodes/3.BO5hcxIH.js.gz +0 -0
  47. package/build/client/_app/immutable/nodes/{4.Dgxx52FJ.js → 4.DTaH-k-c.js} +1 -1
  48. package/build/client/_app/immutable/nodes/4.DTaH-k-c.js.br +0 -0
  49. package/build/client/_app/immutable/nodes/4.DTaH-k-c.js.gz +0 -0
  50. package/build/client/_app/version.json +1 -1
  51. package/build/client/_app/version.json.br +0 -0
  52. package/build/client/_app/version.json.gz +0 -0
  53. package/build/handler.js +7 -7
  54. package/build/index.js +7 -7
  55. package/build/server/chunks/chunks/asset-id.js-CFsW8jeL.js +48 -0
  56. package/build/server/chunks/chunks/asset-id.js-CFsW8jeL.js.map +1 -0
  57. package/build/server/chunks/chunks/{context.js-B77ZAVLi.js → context.js-CG2t-ufq.js} +568 -25
  58. package/build/server/chunks/chunks/context.js-CG2t-ufq.js.map +1 -0
  59. package/build/server/chunks/chunks/{library-store.js-DJ-JRE-g.js → contract.js-sHny3b-s.js} +133 -359
  60. package/build/server/chunks/chunks/contract.js-sHny3b-s.js.map +1 -0
  61. package/build/server/chunks/chunks/{core.js-EA_ysb1J.js → core.js-Clg1CHb6.js} +2 -48
  62. package/build/server/chunks/chunks/core.js-Clg1CHb6.js.map +1 -0
  63. package/build/server/chunks/chunks/{exports.js-DvhE7Ffd.js → exports.js-rOfFZumP.js} +2 -2
  64. package/build/server/chunks/chunks/{exports.js-DvhE7Ffd.js.map → exports.js-rOfFZumP.js.map} +1 -1
  65. package/build/server/chunks/chunks/{http.js-DyoR9XRI.js → http.js--6sDZFfm.js} +35 -3
  66. package/build/server/chunks/chunks/http.js--6sDZFfm.js.map +1 -0
  67. package/build/server/chunks/chunks/{internal.js-Dn37LrAW.js → internal.js-DnveDsYn.js} +3 -3
  68. package/build/server/chunks/chunks/{internal.js-Dn37LrAW.js.map → internal.js-DnveDsYn.js.map} +1 -1
  69. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
  70. package/build/server/chunks/chunks/raw-reconciliation.js-DOFtjkgu.js +343 -0
  71. package/build/server/chunks/chunks/raw-reconciliation.js-DOFtjkgu.js.map +1 -0
  72. package/build/server/chunks/chunks/raw-reconciliation2.js-DFwky2ou.js +11 -0
  73. package/build/server/chunks/chunks/raw-reconciliation2.js-DFwky2ou.js.map +1 -0
  74. package/build/server/chunks/chunks/{shared.js-Dob0TlTu.js → shared.js-DtvIbMLj.js} +2 -2
  75. package/build/server/chunks/chunks/{shared.js-Dob0TlTu.js.map → shared.js-DtvIbMLj.js.map} +1 -1
  76. package/build/server/chunks/chunks/{utils.js-B4YimMxv.js → utils.js-bP1j9RdM.js} +3 -3
  77. package/build/server/chunks/chunks/{utils.js-B4YimMxv.js.map → utils.js-bP1j9RdM.js.map} +1 -1
  78. package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-H4Pq1KAE.js → _server.ts.js-CkSErIdC.js} +7 -5
  79. package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-H4Pq1KAE.js.map → _server.ts.js-CkSErIdC.js.map} +1 -1
  80. package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BI3KHSfE.js → _server.ts.js-BfdsBPWo.js} +11 -6
  81. package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BI3KHSfE.js.map → _server.ts.js-BfdsBPWo.js.map} +1 -1
  82. package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BlNYUbXX.js → _server.ts.js-D0aKDq87.js} +11 -6
  83. package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BlNYUbXX.js.map → _server.ts.js-D0aKDq87.js.map} +1 -1
  84. package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/_server.ts.js-DsX_m2gc.js +31 -0
  85. package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/_server.ts.js-DsX_m2gc.js.map +1 -0
  86. package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-j-_N540j.js → _server.ts.js-nfThjRxs.js} +11 -6
  87. package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-j-_N540j.js.map → _server.ts.js-nfThjRxs.js.map} +1 -1
  88. package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-BHKobzAg.js → _server.ts.js-CseHmsYB.js} +11 -6
  89. package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-BHKobzAg.js.map → _server.ts.js-CseHmsYB.js.map} +1 -1
  90. package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-BFKjIOCv.js → _server.ts.js-CHiICsyC.js} +11 -6
  91. package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-BFKjIOCv.js.map → _server.ts.js-CHiICsyC.js.map} +1 -1
  92. package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BxZeVw8y.js → _server.ts.js-Dgf46K5u.js} +11 -6
  93. package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BxZeVw8y.js.map → _server.ts.js-Dgf46K5u.js.map} +1 -1
  94. package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-CO36U_CT.js → _server.ts.js-DwQJUZFa.js} +11 -6
  95. package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-CO36U_CT.js.map → _server.ts.js-DwQJUZFa.js.map} +1 -1
  96. package/build/server/chunks/entries/endpoints/api/imports/_server.ts.js-BnKS8nVF.js +37 -0
  97. package/build/server/chunks/entries/endpoints/api/imports/_server.ts.js-BnKS8nVF.js.map +1 -0
  98. package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-CBlg1Bkl.js → _server.ts.js-BrDbLcyO.js} +11 -6
  99. package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-CBlg1Bkl.js.map → _server.ts.js-BrDbLcyO.js.map} +1 -1
  100. package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DcGLCjIs.js → _server.ts.js-DIUiQuyd.js} +11 -6
  101. package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DcGLCjIs.js.map → _server.ts.js-DIUiQuyd.js.map} +1 -1
  102. package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AqhtjOiL.js → _server.ts.js-BrReStaf.js} +11 -6
  103. package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AqhtjOiL.js.map → _server.ts.js-BrReStaf.js.map} +1 -1
  104. package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CSa6-DcU.js → _server.ts.js-B31iGwJ-.js} +11 -6
  105. package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CSa6-DcU.js.map → _server.ts.js-B31iGwJ-.js.map} +1 -1
  106. package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DnzZUoAo.js → _server.ts.js-DeZQFNdj.js} +11 -6
  107. package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DnzZUoAo.js.map → _server.ts.js-DeZQFNdj.js.map} +1 -1
  108. package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-B0KMprKq.js → _server.ts.js-DhDhUU7V.js} +11 -6
  109. package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-B0KMprKq.js.map → _server.ts.js-DhDhUU7V.js.map} +1 -1
  110. package/build/server/chunks/entries/endpoints/api/reveal/_server.ts.js-DGkPIMcq.js +80 -0
  111. package/build/server/chunks/entries/endpoints/api/reveal/_server.ts.js-DGkPIMcq.js.map +1 -0
  112. package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-B1N9o1Ne.js → _server.ts.js-BTQgWDH1.js} +11 -6
  113. package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-B1N9o1Ne.js.map → _server.ts.js-BTQgWDH1.js.map} +1 -1
  114. package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-B6HbnRqq.js → _server.ts.js-DB3i4juR.js} +7 -5
  115. package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-B6HbnRqq.js.map → _server.ts.js-DB3i4juR.js.map} +1 -1
  116. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-7wzPyG9v.js → _server.ts.js-kmc-gejQ.js} +11 -6
  117. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-7wzPyG9v.js.map → _server.ts.js-kmc-gejQ.js.map} +1 -1
  118. package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-BWztxYge.js → _server.ts.js-B8EE1DD9.js} +11 -6
  119. package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-BWztxYge.js.map → _server.ts.js-B8EE1DD9.js.map} +1 -1
  120. package/build/server/chunks/entries/{hooks.server.js-CVKLksJ1.js → hooks.server.js-DbBA5yYQ.js} +10 -5
  121. package/build/server/chunks/entries/{hooks.server.js-CVKLksJ1.js.map → hooks.server.js-DbBA5yYQ.js.map} +1 -1
  122. package/build/server/chunks/{handler-DZ2Sps4a.js → handler-J6aKdov3.js} +5 -5
  123. package/build/server/chunks/{handler-DZ2Sps4a.js.map → handler-J6aKdov3.js.map} +1 -1
  124. package/build/server/chunks/{index.js-CdmTWQz8.js → index.js-CsvWJ7Qu.js} +5 -5
  125. package/build/server/chunks/{index.js-CdmTWQz8.js.map → index.js-CsvWJ7Qu.js.map} +1 -1
  126. package/build/server/chunks/{manifest.js-GhfNr35x.js → manifest.js-wHtZ9vlA.js} +46 -25
  127. package/build/server/chunks/manifest.js-wHtZ9vlA.js.map +1 -0
  128. package/build/server/chunks/nodes/0.js-By6-kbk6.js +11 -0
  129. package/build/server/chunks/nodes/0.js-By6-kbk6.js.map +1 -0
  130. package/build/server/chunks/nodes/1.js-CrPfmKpN.js +7 -0
  131. package/build/server/chunks/nodes/{1.js-DQgYreU0.js.map → 1.js-CrPfmKpN.js.map} +1 -1
  132. package/build/server/chunks/nodes/2.js-C-i3Y5t2.js +7 -0
  133. package/build/server/chunks/nodes/2.js-C-i3Y5t2.js.map +1 -0
  134. package/build/server/chunks/nodes/3.js-C1gcIGeV.js +7 -0
  135. package/build/server/chunks/nodes/3.js-C1gcIGeV.js.map +1 -0
  136. package/build/server/chunks/nodes/4.js-tgwl3VD3.js +7 -0
  137. package/build/server/chunks/nodes/{4.js-BebbMVXy.js.map → 4.js-tgwl3VD3.js.map} +1 -1
  138. package/dist/cli.js +2 -0
  139. package/dist/config.d.ts +1 -1
  140. package/dist/core/api-types.d.ts +12 -1
  141. package/dist/core/config.d.ts +12 -0
  142. package/dist/core/index.d.ts +3 -2
  143. package/dist/core/library.d.ts +8 -1
  144. package/dist/core/library.js +30 -6
  145. package/dist/server/cli-commands.d.ts +2 -1
  146. package/dist/server/cli-commands.js +155 -34
  147. package/dist/server/config-loader.d.ts +9 -1
  148. package/dist/server/config-loader.js +26 -1
  149. package/dist/server/file-manager.d.ts +2 -0
  150. package/dist/server/file-manager.js +17 -0
  151. package/dist/server/http.d.ts +1 -0
  152. package/dist/server/http.js +37 -0
  153. package/dist/server/importers/contract.d.ts +29 -0
  154. package/dist/server/importers/contract.js +13 -0
  155. package/dist/server/importers/figma.d.ts +21 -0
  156. package/dist/server/importers/figma.js +386 -0
  157. package/dist/server/importers/file.d.ts +6 -0
  158. package/dist/server/importers/file.js +33 -0
  159. package/dist/server/importers/pinned-https.d.ts +23 -0
  160. package/dist/server/importers/pinned-https.js +60 -0
  161. package/dist/server/importers/registry.d.ts +12 -0
  162. package/dist/server/importers/registry.js +31 -0
  163. package/dist/server/importers.d.ts +2 -0
  164. package/dist/server/importers.js +7 -0
  165. package/dist/server/library-store.d.ts +19 -1
  166. package/dist/server/library-store.js +110 -14
  167. package/dist/server/operations.d.ts +13 -1
  168. package/dist/server/operations.js +159 -23
  169. package/package.json +1 -1
  170. package/build/client/_app/immutable/assets/2.ohi-TClG.css +0 -1
  171. package/build/client/_app/immutable/assets/2.ohi-TClG.css.br +0 -0
  172. package/build/client/_app/immutable/assets/2.ohi-TClG.css.gz +0 -0
  173. package/build/client/_app/immutable/assets/3.ChSpCG-T.css.br +0 -0
  174. package/build/client/_app/immutable/assets/3.ChSpCG-T.css.gz +0 -0
  175. package/build/client/_app/immutable/assets/LibraryContextMenu.BYvU4SY7.css.br +0 -0
  176. package/build/client/_app/immutable/chunks/BAEZPv6c.js +0 -1
  177. package/build/client/_app/immutable/chunks/BAEZPv6c.js.br +0 -0
  178. package/build/client/_app/immutable/chunks/BAEZPv6c.js.gz +0 -0
  179. package/build/client/_app/immutable/chunks/ByVSyIj7.js.br +0 -1
  180. package/build/client/_app/immutable/chunks/ByVSyIj7.js.gz +0 -0
  181. package/build/client/_app/immutable/chunks/C8W3KwDq.js.br +0 -0
  182. package/build/client/_app/immutable/chunks/C8W3KwDq.js.gz +0 -0
  183. package/build/client/_app/immutable/chunks/CP-bnOxU.js.br +0 -0
  184. package/build/client/_app/immutable/chunks/CP-bnOxU.js.gz +0 -0
  185. package/build/client/_app/immutable/chunks/Db8LRH6X.js.br +0 -0
  186. package/build/client/_app/immutable/chunks/Db8LRH6X.js.gz +0 -0
  187. package/build/client/_app/immutable/entry/app.nM6SgHKJ.js.br +0 -0
  188. package/build/client/_app/immutable/entry/app.nM6SgHKJ.js.gz +0 -0
  189. package/build/client/_app/immutable/entry/start.DNXvkhtP.js +0 -1
  190. package/build/client/_app/immutable/entry/start.DNXvkhtP.js.br +0 -0
  191. package/build/client/_app/immutable/entry/start.DNXvkhtP.js.gz +0 -0
  192. package/build/client/_app/immutable/nodes/0.QeXQNCp2.js +0 -2
  193. package/build/client/_app/immutable/nodes/0.QeXQNCp2.js.br +0 -0
  194. package/build/client/_app/immutable/nodes/0.QeXQNCp2.js.gz +0 -0
  195. package/build/client/_app/immutable/nodes/1.dMklHTc2.js.br +0 -0
  196. package/build/client/_app/immutable/nodes/1.dMklHTc2.js.gz +0 -0
  197. package/build/client/_app/immutable/nodes/2.CW4VMcIP.js +0 -3
  198. package/build/client/_app/immutable/nodes/2.CW4VMcIP.js.br +0 -0
  199. package/build/client/_app/immutable/nodes/2.CW4VMcIP.js.gz +0 -0
  200. package/build/client/_app/immutable/nodes/3.CTiY_pbK.js +0 -1
  201. package/build/client/_app/immutable/nodes/3.CTiY_pbK.js.br +0 -0
  202. package/build/client/_app/immutable/nodes/3.CTiY_pbK.js.gz +0 -0
  203. package/build/client/_app/immutable/nodes/4.Dgxx52FJ.js.br +0 -0
  204. package/build/client/_app/immutable/nodes/4.Dgxx52FJ.js.gz +0 -0
  205. package/build/server/chunks/chunks/context.js-B77ZAVLi.js.map +0 -1
  206. package/build/server/chunks/chunks/core.js-EA_ysb1J.js.map +0 -1
  207. package/build/server/chunks/chunks/http.js-DyoR9XRI.js.map +0 -1
  208. package/build/server/chunks/chunks/library-store.js-DJ-JRE-g.js.map +0 -1
  209. package/build/server/chunks/manifest.js-GhfNr35x.js.map +0 -1
  210. package/build/server/chunks/nodes/0.js-DGT86_i-.js +0 -11
  211. package/build/server/chunks/nodes/0.js-DGT86_i-.js.map +0 -1
  212. package/build/server/chunks/nodes/1.js-DQgYreU0.js +0 -7
  213. package/build/server/chunks/nodes/2.js-DItreSrX.js +0 -7
  214. package/build/server/chunks/nodes/2.js-DItreSrX.js.map +0 -1
  215. package/build/server/chunks/nodes/3.js-bE1dJ_6v.js +0 -7
  216. package/build/server/chunks/nodes/3.js-bE1dJ_6v.js.map +0 -1
  217. package/build/server/chunks/nodes/4.js-BebbMVXy.js +0 -7
@@ -14,6 +14,7 @@ import { runBuild } from './build.js';
14
14
  import { formatCheckFinding, runCheck } from './check.js';
15
15
  import { loadConfig } from './config-loader.js';
16
16
  import { FundusOperations } from './operations.js';
17
+ import { validateFigmaSourceParameters } from './importers/figma.js';
17
18
  import { pluginsByType, serverPluginForType } from './plugins.js';
18
19
  import { CliUsageError, formatBytes, parseArguments, parseJsonFlag, parseManifestList, printJsonDocument, requirePositionals } from './cli-io.js';
19
20
  const COMMON_FLAGS = {
@@ -111,6 +112,10 @@ function printHumanAssetDetail(asset) {
111
112
  console.log(` raw: ${asset.raw}`);
112
113
  console.log(` folder: ${asset.folder || 'root'}`);
113
114
  console.log(` rawHash: ${asset.rawHash}`);
115
+ if (asset.importSource?.importer === 'figma') {
116
+ const source = validateFigmaSourceParameters(asset.importSource.parameters);
117
+ console.log(` source: Figma node ${source.nodeId} at ${source.scale}x`);
118
+ }
114
119
  console.log(` manifests: ${asset.manifests.length > 0 ? asset.manifests.join(', ') : '(none)'}`);
115
120
  for (const membership of asset.passiveManifests) {
116
121
  console.log(` passive: ${membership.manifest} (via ${membership.via.join(', ')})`);
@@ -201,7 +206,8 @@ document on stdout) and --help.
201
206
  Subcommands:
202
207
  list list assets (--manifest, --type, --warnings-only)
203
208
  show one asset in detail
204
- ingest copy a file into the raw dir as a typed asset
209
+ ingest acquire a file or Figma selection as a typed asset
210
+ reimport refresh an asset from its saved source
205
211
  replace replace an asset's raw file while preserving its metadata
206
212
  set merge parameters and/or replace manifest membership
207
213
  rename rename an asset id
@@ -248,43 +254,65 @@ Read-only: nothing is processed or written.
248
254
  Example:
249
255
  fundus asset show navigationPanel --json
250
256
  `;
251
- export const ASSET_INGEST_HELP = `fundus asset ingest — copy a file into the raw dir as a typed asset
257
+ export const ASSET_INGEST_HELP = `fundus asset ingest — acquire a file or Figma selection as a typed asset
252
258
 
253
259
  Usage: fundus asset ingest <file> [--type <type>] [--id <id>] [--folder <path|root>]
260
+ [--manifests <a,b>] [--parameters <json>] [--from file] [--json]
261
+ fundus asset ingest <figma-selection-link> --from figma --type <image|slice> --id <id>
262
+ [--scale <1|2|3|4>] [--folder <path|root>]
254
263
  [--manifests <a,b>] [--parameters <json>] [--json]
255
264
 
256
- Behaves like an editor upload: the file is copied into the raw
257
- directory (sanitized, non-colliding name) and content-hashed, and the
258
- record starts with the owning plugin's default parameters — a slice
259
- additionally seeds insets from the image dimensions and a pixel ratio
260
- from a @2x-style file-name suffix. Runs the full autosave chain
261
- (write library → process → regenerate manifest modules).
265
+ File ingest remains the default and behaves like an editor upload. Figma
266
+ ingest materializes one selection as PNG through the same importer-neutral
267
+ operations pipeline. Both run the full autosave chain.
262
268
 
263
269
  Flags:
264
- --type <type> required when the file extension is ambiguous
265
- (.png could be image or slice, .mp4 video or
266
- chroma-key-video); optional when exactly one
267
- type accepts it (.wav → audio); known types:
270
+ --from <file|figma> acquisition source; default: file. URLs are never
271
+ detected heuristically
272
+ --type <type> required for Figma (image or slice) and when a file
273
+ extension is ambiguous; known types:
268
274
  ${Object.keys(pluginsByType).sort().join(', ')}
269
- --id <id> asset id (camelCase identifier); default:
270
- proposed from the file name
271
- (panel_button@2x.png → sidebarPanel)
275
+ --id <id> required for Figma; for files defaults to a proposal
276
+ from the file name
272
277
  --folder <path|root> destination folder; default: root
273
- --manifests <a,b> explicit manifest membership, applied in the
274
- same operation (comma-separated; every name
275
- must exist)
276
- --parameters <json> parameter patch merged onto the plugin defaults
277
- — same shallow-merge semantics as \`asset set\`
278
+ --manifests <a,b> explicit manifest membership (every name must exist)
279
+ --parameters <json> shallow parameter patch merged after ingest seeding
278
280
  (see \`fundus asset set --help\`)
281
+ --scale <1|2|3|4> Figma export scale; default:
282
+ import.figma.defaults.scale from fundus.config.ts
283
+ (invalid for file ingest)
279
284
  --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
280
285
  --help this help
281
286
 
282
- A failed ingest (invalid patch, unknown manifest, …) leaves nothing
283
- behind: the raw file and record are rolled back.
287
+ A failed ingest leaves neither a raw file nor a record behind.
284
288
 
285
289
  Example:
286
- fundus asset ingest ./exports/navigation_panel@2x.png --type slice \\
287
- --manifests mainCatalog --json
290
+ fundus asset ingest ./exports/navigation_panel@2x.png --type slice --json
291
+ fundus asset ingest 'https://www.figma.com/design/abc/UI?node-id=12-34' \\
292
+ --from figma --type slice --id navigationPanel --scale 3 --json
293
+ `;
294
+ export const ASSET_REIMPORT_HELP = `fundus asset reimport — refresh an asset from its saved source
295
+
296
+ Usage: fundus asset reimport <id> [--json]
297
+ fundus asset reimport <id> --figma-link <selection-link> [--scale <1|2|3|4>] [--json]
298
+ fundus asset reimport <id> --scale <1|2|3|4> [--json]
299
+
300
+ Without overrides, re-materializes the persisted importer source. Figma
301
+ overrides may update the link, scale, or both; omitted values are preserved.
302
+ Asset type, parameters, folder, manifests, and id remain unchanged. Raw-file
303
+ drift or concurrent changes abort without overwriting local edits.
304
+
305
+ Flags:
306
+ --figma-link <link> replace the persisted Figma file/node selection
307
+ --scale <1|2|3|4> replace the persisted Figma export scale
308
+ --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
309
+ --help this help
310
+
311
+ Example:
312
+ fundus asset reimport navigationPanel
313
+ fundus asset reimport navigationPanel --figma-link '<new-selection-link>'
314
+ fundus asset reimport navigationPanel --scale 4
315
+ fundus asset reimport navigationPanel --figma-link '<new-selection-link>' --scale 4 --json
288
316
  `;
289
317
  export const ASSET_REPLACE_HELP = `fundus asset replace — replace an asset's raw file
290
318
 
@@ -385,6 +413,7 @@ const ASSET_SUBCOMMAND_HELP = {
385
413
  list: ASSET_LIST_HELP,
386
414
  show: ASSET_SHOW_HELP,
387
415
  ingest: ASSET_INGEST_HELP,
416
+ reimport: ASSET_REIMPORT_HELP,
388
417
  replace: ASSET_REPLACE_HELP,
389
418
  set: ASSET_SET_HELP,
390
419
  rename: ASSET_RENAME_HELP,
@@ -470,9 +499,57 @@ async function runAssetIngest(argumentList) {
470
499
  id: { takesValue: true },
471
500
  manifests: { takesValue: true },
472
501
  parameters: { takesValue: true },
473
- folder: { takesValue: true }
502
+ folder: { takesValue: true },
503
+ from: { takesValue: true },
504
+ scale: { takesValue: true }
474
505
  }, 'fundus asset ingest');
475
- const [filePath] = requirePositionals(parsed, ['<file>'], 'fundus asset ingest');
506
+ const [source] = requirePositionals(parsed, ['<file|figma-selection-link>'], 'fundus asset ingest');
507
+ const from = parsed.flags.from === undefined ? 'file' : parsed.flags.from;
508
+ if (from !== 'file' && from !== 'figma') {
509
+ throw new CliUsageError('Flag "--from" must be "file" or "figma" — run `fundus asset ingest --help`.');
510
+ }
511
+ const scale = typeof parsed.flags.scale === 'string'
512
+ ? parseFigmaScale(parsed.flags.scale, 'fundus asset ingest')
513
+ : undefined;
514
+ if (from === 'file' && scale !== undefined) {
515
+ throw new CliUsageError('Flag "--scale" is only valid with `--from figma` — run `fundus asset ingest --help`.');
516
+ }
517
+ const parametersPatch = typeof parsed.flags.parameters === 'string'
518
+ ? parseJsonFlag('parameters', parsed.flags.parameters)
519
+ : undefined;
520
+ const manifests = typeof parsed.flags.manifests === 'string'
521
+ ? parseManifestList(parsed.flags.manifests)
522
+ : undefined;
523
+ const folder = typeof parsed.flags.folder === 'string' ? parseFolderToken(parsed.flags.folder) : '';
524
+ if (from === 'figma') {
525
+ if (typeof parsed.flags.id !== 'string') {
526
+ throw new CliUsageError('Figma ingest requires --id <id> — run `fundus asset ingest --help`.');
527
+ }
528
+ if (typeof parsed.flags.type !== 'string' ||
529
+ (parsed.flags.type !== 'image' && parsed.flags.type !== 'slice')) {
530
+ throw new CliUsageError('Figma ingest requires --type <image|slice> — run `fundus asset ingest --help`.');
531
+ }
532
+ const id = parsed.flags.id;
533
+ const type = parsed.flags.type;
534
+ const operations = await createOperations();
535
+ const state = await operations.importAsset({
536
+ importer: 'figma',
537
+ input: { url: source, format: 'png', ...(scale !== undefined ? { scale } : {}) },
538
+ type,
539
+ id,
540
+ folder,
541
+ parametersPatch,
542
+ manifests
543
+ });
544
+ const asset = assetById(state, id);
545
+ if (parsed.flags.json) {
546
+ printJsonDocument({ ok: true, asset, issues: state.issues });
547
+ return;
548
+ }
549
+ printMutationResult(`Imported asset "${id}" from Figma as ${type}.`, asset, state);
550
+ return;
551
+ }
552
+ const filePath = source;
476
553
  const absolutePath = resolve(process.cwd(), filePath);
477
554
  let bytes;
478
555
  try {
@@ -484,13 +561,6 @@ async function runAssetIngest(argumentList) {
484
561
  const fileName = basename(absolutePath);
485
562
  const type = typeof parsed.flags.type === 'string' ? parsed.flags.type : inferTypeForFileName(fileName);
486
563
  const id = typeof parsed.flags.id === 'string' ? parsed.flags.id : proposeAssetId(fileName);
487
- const parametersPatch = typeof parsed.flags.parameters === 'string'
488
- ? parseJsonFlag('parameters', parsed.flags.parameters)
489
- : undefined;
490
- const manifests = typeof parsed.flags.manifests === 'string'
491
- ? parseManifestList(parsed.flags.manifests)
492
- : undefined;
493
- const folder = typeof parsed.flags.folder === 'string' ? parseFolderToken(parsed.flags.folder) : '';
494
564
  const operations = await createOperations();
495
565
  const state = await operations.ingest({
496
566
  fileName,
@@ -508,6 +578,55 @@ async function runAssetIngest(argumentList) {
508
578
  }
509
579
  printMutationResult(`Ingested "${fileName}" as ${type} asset "${id}".`, asset, state);
510
580
  }
581
+ function parseFigmaScale(value, commandPath) {
582
+ if (value === '1' || value === '2' || value === '3' || value === '4') {
583
+ return Number(value);
584
+ }
585
+ throw new CliUsageError(`Flag "--scale" must be 1, 2, 3, or 4 — run \`${commandPath} --help\`.`);
586
+ }
587
+ function canonicalFigmaSelectionLink(source) {
588
+ const url = new URL(`https://www.figma.com/design/${encodeURIComponent(source.fileKey)}/fundus`);
589
+ url.searchParams.set('node-id', source.nodeId.replace(':', '-'));
590
+ return url.toString();
591
+ }
592
+ async function runAssetReimport(argumentList) {
593
+ const parsed = parseArguments(argumentList, {
594
+ ...COMMON_FLAGS,
595
+ 'figma-link': { takesValue: true },
596
+ scale: { takesValue: true }
597
+ }, 'fundus asset reimport');
598
+ const [id] = requirePositionals(parsed, ['<id>'], 'fundus asset reimport');
599
+ const scale = typeof parsed.flags.scale === 'string'
600
+ ? parseFigmaScale(parsed.flags.scale, 'fundus asset reimport')
601
+ : undefined;
602
+ const figmaLink = typeof parsed.flags['figma-link'] === 'string' ? parsed.flags['figma-link'] : undefined;
603
+ const operations = await createOperations();
604
+ let state;
605
+ if (figmaLink === undefined && scale === undefined) {
606
+ state = await operations.reimportAsset(id);
607
+ }
608
+ else {
609
+ const current = await operations.state();
610
+ const currentAsset = assetById(current, id);
611
+ if (currentAsset.importSource?.importer !== 'figma') {
612
+ throw new Error(`Asset "${id}" does not have a Figma source.`);
613
+ }
614
+ const source = validateFigmaSourceParameters(currentAsset.importSource.parameters);
615
+ state = await operations.reimportAsset(id, {
616
+ url: figmaLink ?? canonicalFigmaSelectionLink(source),
617
+ format: 'png',
618
+ scale: scale ?? source.scale
619
+ });
620
+ }
621
+ const asset = assetById(state, id);
622
+ if (parsed.flags.json) {
623
+ printJsonDocument({ ok: true, asset, issues: state.issues });
624
+ return;
625
+ }
626
+ printMutationResult(figmaLink === undefined && scale === undefined
627
+ ? `Refreshed asset "${id}" from its saved source.`
628
+ : `Reimported asset "${id}" with updated Figma source.`, asset, state);
629
+ }
511
630
  async function runAssetReplace(argumentList) {
512
631
  const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset replace');
513
632
  const [id, filePath] = requirePositionals(parsed, ['<id>', '<file>'], 'fundus asset replace');
@@ -593,7 +712,7 @@ export async function runAssetCommand(argumentList) {
593
712
  if (subcommand === undefined || subcommand === '--help') {
594
713
  console.log(ASSET_HELP);
595
714
  if (subcommand === undefined) {
596
- throw new CliUsageError('`fundus asset` needs a subcommand: list, show, ingest, replace, set, rename, move, delete.');
715
+ throw new CliUsageError('`fundus asset` needs a subcommand: list, show, ingest, reimport, replace, set, rename, move, delete.');
597
716
  }
598
717
  return;
599
718
  }
@@ -609,6 +728,8 @@ export async function runAssetCommand(argumentList) {
609
728
  return runAssetShow(rest);
610
729
  case 'ingest':
611
730
  return runAssetIngest(rest);
731
+ case 'reimport':
732
+ return runAssetReimport(rest);
612
733
  case 'replace':
613
734
  return runAssetReplace(rest);
614
735
  case 'set':
@@ -1,4 +1,4 @@
1
- import type { AudioPreset, ImagePreset, VideoPreset } from '../core/index.js';
1
+ import type { AudioPreset, FigmaImportScale, ImagePreset, VideoPreset } from '../core/index.js';
2
2
  import type { ServerPluginRegistry } from './plugins/contract.js';
3
3
  export interface ResolvedPresetPluginConfig {
4
4
  presets: Record<string, ImagePreset>;
@@ -28,6 +28,14 @@ export interface ResolvedFundusConfig {
28
28
  video: ResolvedVideoPluginConfig;
29
29
  audio: ResolvedAudioPluginConfig;
30
30
  };
31
+ import: {
32
+ figma: {
33
+ token?: string;
34
+ defaults: {
35
+ scale: FigmaImportScale;
36
+ };
37
+ };
38
+ };
31
39
  }
32
40
  /** Pure resolution step (testable without a config file on disk). */
33
41
  export declare function resolveConfig(projectRoot: string, value: unknown, registry?: ServerPluginRegistry): ResolvedFundusConfig;
@@ -51,13 +51,38 @@ export function resolveConfig(projectRoot, value, registry = serverPluginsByType
51
51
  value: plugins[definition.namespace]
52
52
  });
53
53
  }
54
+ const importConfig = value.import === undefined ? {} : value.import;
55
+ if (!isRecord(importConfig)) {
56
+ throw new Error('fundus.config: "import" must be an object.');
57
+ }
58
+ const figma = importConfig.figma === undefined ? {} : importConfig.figma;
59
+ if (!isRecord(figma)) {
60
+ throw new Error('fundus.config: "import.figma" must be an object.');
61
+ }
62
+ if (figma.token !== undefined && (typeof figma.token !== 'string' || figma.token.length === 0)) {
63
+ throw new Error('fundus.config: "import.figma.token" must be a non-empty string.');
64
+ }
65
+ const defaults = figma.defaults === undefined ? {} : figma.defaults;
66
+ if (!isRecord(defaults)) {
67
+ throw new Error('fundus.config: "import.figma.defaults" must be an object.');
68
+ }
69
+ const scale = defaults.scale === undefined ? 2 : defaults.scale;
70
+ if (scale !== 1 && scale !== 2 && scale !== 3 && scale !== 4) {
71
+ throw new Error('fundus.config: "import.figma.defaults.scale" must be 1, 2, 3, or 4.');
72
+ }
54
73
  return {
55
74
  rawDir: resolvePath(projectRoot, requiredString(value, 'rawDir')),
56
75
  proxyDir: resolvePath(projectRoot, requiredString(value, 'proxyDir')),
57
76
  library: resolvePath(projectRoot, requiredString(value, 'library')),
58
77
  manifestsDir: resolvePath(projectRoot, requiredString(value, 'manifestsDir')),
59
78
  proxyBasePath,
60
- plugins: resolvedPlugins
79
+ plugins: resolvedPlugins,
80
+ import: {
81
+ figma: {
82
+ ...(figma.token !== undefined ? { token: figma.token } : {}),
83
+ defaults: { scale }
84
+ }
85
+ }
61
86
  };
62
87
  }
63
88
  /** Load `fundus.config.ts` from the host project root. */
@@ -0,0 +1,2 @@
1
+ /** Reveal a file, or open a directory, in the host platform's file manager. */
2
+ export declare function openInFileManager(target: string, kind: 'file' | 'directory'): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { dirname } from 'node:path';
4
+ import open from 'open';
5
+ const execute = promisify(execFile);
6
+ /** Reveal a file, or open a directory, in the host platform's file manager. */
7
+ export async function openInFileManager(target, kind) {
8
+ if (kind === 'file' && process.platform === 'darwin') {
9
+ await execute('open', ['-R', target]);
10
+ return;
11
+ }
12
+ if (kind === 'file' && process.platform === 'win32') {
13
+ await execute('explorer.exe', [`/select,${target}`], { windowsHide: true });
14
+ return;
15
+ }
16
+ await open(kind === 'file' ? dirname(target) : target);
17
+ }
@@ -5,6 +5,7 @@ export declare function routeNotFound(event: {
5
5
  url: URL;
6
6
  }): Response;
7
7
  export declare function apiResponse(run: () => Promise<unknown>): Promise<Response>;
8
+ export declare function sourceImportResponse(run: () => Promise<unknown>): Promise<Response>;
8
9
  export declare function parseByteRange(rangeHeader: string | null, size: number): {
9
10
  start: number;
10
11
  end: number;
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { lstat, readFile } from 'node:fs/promises';
3
3
  import { extname, join } from 'node:path';
4
4
  import { assertSafeRelativeFilePath } from './library-store.js';
5
+ import { SourceImportError } from './importers/contract.js';
5
6
  const CONTENT_TYPES = {
6
7
  '.png': 'image/png',
7
8
  '.jpg': 'image/jpeg',
@@ -37,6 +38,42 @@ export async function apiResponse(run) {
37
38
  return errorResponse(cause, 400);
38
39
  }
39
40
  }
41
+ const SOURCE_ERROR_STATUS = {
42
+ 'invalid-input': 400,
43
+ 'not-configured': 401,
44
+ unauthorized: 401,
45
+ forbidden: 403,
46
+ 'not-found': 404,
47
+ 'rate-limited': 429,
48
+ 'upstream-failed': 502,
49
+ 'invalid-response': 502,
50
+ 'unsafe-download-url': 502,
51
+ 'download-timeout': 504,
52
+ 'download-too-large': 502,
53
+ 'image-too-large': 502,
54
+ 'stale-import': 409
55
+ };
56
+ export async function sourceImportResponse(run) {
57
+ try {
58
+ return Response.json(await run());
59
+ }
60
+ catch (cause) {
61
+ const error = cause instanceof SourceImportError
62
+ ? cause
63
+ : new SourceImportError('invalid-input', cause instanceof Error ? cause.message : String(cause));
64
+ const body = {
65
+ error: {
66
+ message: error.message,
67
+ code: error.code,
68
+ retryAfterSeconds: error.retryAfterSeconds
69
+ }
70
+ };
71
+ const headers = error.code === 'rate-limited' && error.retryAfterSeconds !== undefined
72
+ ? { 'Retry-After': String(error.retryAfterSeconds) }
73
+ : undefined;
74
+ return Response.json(body, { status: SOURCE_ERROR_STATUS[error.code], headers });
75
+ }
76
+ }
40
77
  export function parseByteRange(rangeHeader, size) {
41
78
  if (!rangeHeader)
42
79
  return null;
@@ -0,0 +1,29 @@
1
+ import type { ImportSourceRecord, SourceImportErrorCode } from '../../core/index.js';
2
+ import type { ResolvedFundusConfig } from '../config-loader.js';
3
+ import type { PinnedHttpsTransport } from './pinned-https.js';
4
+ export interface ImportedRaw {
5
+ bytes: Uint8Array;
6
+ /** Lowercase extension without a leading dot. */
7
+ extension: string;
8
+ suggestedFileName?: string;
9
+ importSource?: ImportSourceRecord;
10
+ }
11
+ export interface ImporterContext {
12
+ config: ResolvedFundusConfig;
13
+ fetch: typeof globalThis.fetch;
14
+ httpsTransport?: PinnedHttpsTransport;
15
+ }
16
+ export interface ServerSourceImporter<TInput = unknown, TSource = unknown> {
17
+ kind: string;
18
+ label: string;
19
+ validateInput(value: unknown): TInput;
20
+ importRaw(input: TInput, context: ImporterContext): Promise<ImportedRaw>;
21
+ validateSourceParameters(value: unknown): TSource;
22
+ reimportRaw?(source: TSource, context: ImporterContext): Promise<ImportedRaw>;
23
+ }
24
+ export declare class SourceImportError extends Error {
25
+ readonly code: SourceImportErrorCode;
26
+ readonly retryAfterSeconds?: number;
27
+ constructor(code: SourceImportErrorCode, message: string, retryAfterSeconds?: number);
28
+ }
29
+ export declare function invalidInput(message: string): never;
@@ -0,0 +1,13 @@
1
+ export class SourceImportError extends Error {
2
+ code;
3
+ retryAfterSeconds;
4
+ constructor(code, message, retryAfterSeconds) {
5
+ super(message);
6
+ this.name = 'SourceImportError';
7
+ this.code = code;
8
+ this.retryAfterSeconds = retryAfterSeconds;
9
+ }
10
+ }
11
+ export function invalidInput(message) {
12
+ throw new SourceImportError('invalid-input', message);
13
+ }
@@ -0,0 +1,21 @@
1
+ import { type ServerSourceImporter } from './contract.js';
2
+ import { type PinnedHttpsTarget } from './pinned-https.js';
3
+ export interface FigmaSourceParameters {
4
+ fileKey: string;
5
+ nodeId: string;
6
+ format: 'png';
7
+ scale: 1 | 2 | 3 | 4;
8
+ }
9
+ export interface FigmaImportInput {
10
+ url: string;
11
+ format?: 'png';
12
+ scale?: 1 | 2 | 3 | 4;
13
+ }
14
+ export declare function parseFigmaSelectionUrl(value: string): {
15
+ fileKey: string;
16
+ nodeId: string;
17
+ };
18
+ export declare function validateFigmaSourceParameters(value: unknown): FigmaSourceParameters;
19
+ export declare function resolveSafeDownloadTarget(value: string | URL): Promise<PinnedHttpsTarget>;
20
+ export declare function assertSafeDownloadUrl(value: string | URL): Promise<URL>;
21
+ export declare const figmaSourceImporter: ServerSourceImporter<FigmaImportInput, FigmaSourceParameters>;