fritzkit 0.0.0-stage → 0.1.1

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 (319) hide show
  1. package/.claude-plugin/marketplace.json +12 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/CHANGELOG.md +8 -0
  4. package/LICENSE +21 -0
  5. package/README.md +429 -2
  6. package/dist/cli/commands/doctor.d.ts +4 -0
  7. package/dist/cli/commands/doctor.js +7 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/export.d.ts +17 -0
  10. package/dist/cli/commands/export.js +81 -0
  11. package/dist/cli/commands/export.js.map +1 -0
  12. package/dist/cli/commands/kicad.d.ts +10 -0
  13. package/dist/cli/commands/kicad.js +22 -0
  14. package/dist/cli/commands/kicad.js.map +1 -0
  15. package/dist/cli/commands/parts.d.ts +22 -0
  16. package/dist/cli/commands/parts.js +46 -0
  17. package/dist/cli/commands/parts.js.map +1 -0
  18. package/dist/cli/commands/prune.d.ts +14 -0
  19. package/dist/cli/commands/prune.js +67 -0
  20. package/dist/cli/commands/prune.js.map +1 -0
  21. package/dist/cli/commands/render.d.ts +24 -0
  22. package/dist/cli/commands/render.js +62 -0
  23. package/dist/cli/commands/render.js.map +1 -0
  24. package/dist/cli/commands/schema.d.ts +10 -0
  25. package/dist/cli/commands/schema.js +35 -0
  26. package/dist/cli/commands/schema.js.map +1 -0
  27. package/dist/cli/commands/ui.d.ts +14 -0
  28. package/dist/cli/commands/ui.js +67 -0
  29. package/dist/cli/commands/ui.js.map +1 -0
  30. package/dist/cli/commands/validate.d.ts +24 -0
  31. package/dist/cli/commands/validate.js +91 -0
  32. package/dist/cli/commands/validate.js.map +1 -0
  33. package/dist/cli/context.d.ts +81 -0
  34. package/dist/cli/context.js +139 -0
  35. package/dist/cli/context.js.map +1 -0
  36. package/dist/cli/main.d.ts +2 -0
  37. package/dist/cli/main.js +8 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/program.d.ts +21 -0
  40. package/dist/cli/program.js +158 -0
  41. package/dist/cli/program.js.map +1 -0
  42. package/dist/cli/raster.d.ts +4 -0
  43. package/dist/cli/raster.js +9 -0
  44. package/dist/cli/raster.js.map +1 -0
  45. package/dist/core/compile/breadboard-util.d.ts +12 -0
  46. package/dist/core/compile/breadboard-util.js +107 -0
  47. package/dist/core/compile/breadboard-util.js.map +1 -0
  48. package/dist/core/compile/breadboard.d.ts +60 -0
  49. package/dist/core/compile/breadboard.js +513 -0
  50. package/dist/core/compile/breadboard.js.map +1 -0
  51. package/dist/core/compile/fzp.d.ts +12 -0
  52. package/dist/core/compile/fzp.js +183 -0
  53. package/dist/core/compile/fzp.js.map +1 -0
  54. package/dist/core/compile/icon.d.ts +17 -0
  55. package/dist/core/compile/icon.js +27 -0
  56. package/dist/core/compile/icon.js.map +1 -0
  57. package/dist/core/compile/index.d.ts +4 -0
  58. package/dist/core/compile/index.js +62 -0
  59. package/dist/core/compile/index.js.map +1 -0
  60. package/dist/core/compile/pcb.d.ts +39 -0
  61. package/dist/core/compile/pcb.js +164 -0
  62. package/dist/core/compile/pcb.js.map +1 -0
  63. package/dist/core/compile/schematic.d.ts +69 -0
  64. package/dist/core/compile/schematic.js +240 -0
  65. package/dist/core/compile/schematic.js.map +1 -0
  66. package/dist/core/compile/style.d.ts +52 -0
  67. package/dist/core/compile/style.js +72 -0
  68. package/dist/core/compile/style.js.map +1 -0
  69. package/dist/core/compile/types.d.ts +50 -0
  70. package/dist/core/compile/types.js +15 -0
  71. package/dist/core/compile/types.js.map +1 -0
  72. package/dist/core/export/bin.d.ts +61 -0
  73. package/dist/core/export/bin.js +221 -0
  74. package/dist/core/export/bin.js.map +1 -0
  75. package/dist/core/export/fzpz.d.ts +37 -0
  76. package/dist/core/export/fzpz.js +121 -0
  77. package/dist/core/export/fzpz.js.map +1 -0
  78. package/dist/core/export/index.d.ts +5 -0
  79. package/dist/core/export/index.js +6 -0
  80. package/dist/core/export/index.js.map +1 -0
  81. package/dist/core/export/install.d.ts +77 -0
  82. package/dist/core/export/install.js +183 -0
  83. package/dist/core/export/install.js.map +1 -0
  84. package/dist/core/export/prune.d.ts +72 -0
  85. package/dist/core/export/prune.js +165 -0
  86. package/dist/core/export/prune.js.map +1 -0
  87. package/dist/core/fritzing.d.ts +22 -0
  88. package/dist/core/fritzing.js +77 -0
  89. package/dist/core/fritzing.js.map +1 -0
  90. package/dist/core/identity.d.ts +29 -0
  91. package/dist/core/identity.js +70 -0
  92. package/dist/core/identity.js.map +1 -0
  93. package/dist/core/image/bbox.d.ts +27 -0
  94. package/dist/core/image/bbox.js +24 -0
  95. package/dist/core/image/bbox.js.map +1 -0
  96. package/dist/core/image/index.d.ts +8 -0
  97. package/dist/core/image/index.js +9 -0
  98. package/dist/core/image/index.js.map +1 -0
  99. package/dist/core/image/key.d.ts +72 -0
  100. package/dist/core/image/key.js +317 -0
  101. package/dist/core/image/key.js.map +1 -0
  102. package/dist/core/image/prepare.d.ts +31 -0
  103. package/dist/core/image/prepare.js +98 -0
  104. package/dist/core/image/prepare.js.map +1 -0
  105. package/dist/core/image/remover.d.ts +17 -0
  106. package/dist/core/image/remover.js +51 -0
  107. package/dist/core/image/remover.js.map +1 -0
  108. package/dist/core/index.d.ts +16 -0
  109. package/dist/core/index.js +17 -0
  110. package/dist/core/index.js.map +1 -0
  111. package/dist/core/kicad/footprint.d.ts +104 -0
  112. package/dist/core/kicad/footprint.js +317 -0
  113. package/dist/core/kicad/footprint.js.map +1 -0
  114. package/dist/core/kicad/index.d.ts +25 -0
  115. package/dist/core/kicad/index.js +197 -0
  116. package/dist/core/kicad/index.js.map +1 -0
  117. package/dist/core/kicad/sexpr.d.ts +35 -0
  118. package/dist/core/kicad/sexpr.js +163 -0
  119. package/dist/core/kicad/sexpr.js.map +1 -0
  120. package/dist/core/resolve.d.ts +113 -0
  121. package/dist/core/resolve.js +301 -0
  122. package/dist/core/resolve.js.map +1 -0
  123. package/dist/core/schema/part.d.ts +995 -0
  124. package/dist/core/schema/part.js +278 -0
  125. package/dist/core/schema/part.js.map +1 -0
  126. package/dist/core/schema/part.schema.json +1122 -0
  127. package/dist/core/svg.d.ts +20 -0
  128. package/dist/core/svg.js +56 -0
  129. package/dist/core/svg.js.map +1 -0
  130. package/dist/core/units.d.ts +28 -0
  131. package/dist/core/units.js +42 -0
  132. package/dist/core/units.js.map +1 -0
  133. package/dist/core/validate/checker.d.ts +46 -0
  134. package/dist/core/validate/checker.js +142 -0
  135. package/dist/core/validate/checker.js.map +1 -0
  136. package/dist/core/validate/doctor.d.ts +25 -0
  137. package/dist/core/validate/doctor.js +156 -0
  138. package/dist/core/validate/doctor.js.map +1 -0
  139. package/dist/core/validate/explain.d.ts +10 -0
  140. package/dist/core/validate/explain.js +135 -0
  141. package/dist/core/validate/explain.js.map +1 -0
  142. package/dist/core/validate/index.d.ts +21 -0
  143. package/dist/core/validate/index.js +96 -0
  144. package/dist/core/validate/index.js.map +1 -0
  145. package/dist/core/validate/structural.d.ts +74 -0
  146. package/dist/core/validate/structural.js +664 -0
  147. package/dist/core/validate/structural.js.map +1 -0
  148. package/dist/core/workspace.d.ts +69 -0
  149. package/dist/core/workspace.js +213 -0
  150. package/dist/core/workspace.js.map +1 -0
  151. package/dist/mcp/jsonpatch.d.ts +30 -0
  152. package/dist/mcp/jsonpatch.js +145 -0
  153. package/dist/mcp/jsonpatch.js.map +1 -0
  154. package/dist/mcp/main.d.ts +2 -0
  155. package/dist/mcp/main.js +5 -0
  156. package/dist/mcp/main.js.map +1 -0
  157. package/dist/mcp/server.d.ts +28 -0
  158. package/dist/mcp/server.js +310 -0
  159. package/dist/mcp/server.js.map +1 -0
  160. package/dist/server/api.d.ts +72 -0
  161. package/dist/server/api.js +507 -0
  162. package/dist/server/api.js.map +1 -0
  163. package/dist/server/events.d.ts +28 -0
  164. package/dist/server/events.js +134 -0
  165. package/dist/server/events.js.map +1 -0
  166. package/dist/server/http.d.ts +36 -0
  167. package/dist/server/http.js +127 -0
  168. package/dist/server/http.js.map +1 -0
  169. package/dist/server/index.d.ts +65 -0
  170. package/dist/server/index.js +193 -0
  171. package/dist/server/index.js.map +1 -0
  172. package/dist/ui/assets/index-DLU8opyI.js +63 -0
  173. package/dist/ui/assets/index-GhUA-T3b.css +1 -0
  174. package/dist/ui/index.html +14 -0
  175. package/package.json +89 -4
  176. package/skills/fritzkit/SKILL.md +690 -0
  177. package/skills/fritzkit/reference/examples.md +275 -0
  178. package/skills/fritzkit/reference/part-json.md +333 -0
  179. package/skills/fritzkit/scripts/measure.py +197 -0
  180. package/tools/fzp-checker/.python-version +1 -0
  181. package/tools/fzp-checker/README.md +45 -0
  182. package/tools/fzp-checker/check_fzp.py +48 -0
  183. package/tools/fzp-checker/pyproject.toml +26 -0
  184. package/tools/fzp-checker/uv.lock +266 -0
  185. package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
  186. package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
  187. package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
  188. package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
  189. package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
  190. package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
  191. package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
  192. package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
  193. package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
  194. package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
  195. package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
  196. package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
  197. package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
  198. package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
  199. package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
  200. package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
  201. package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
  202. package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
  203. package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
  204. package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
  205. package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
  206. package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
  207. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
  208. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
  209. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
  210. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
  211. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
  212. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
  213. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
  214. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
  215. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
  216. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
  217. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
  218. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
  219. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
  220. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
  221. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
  222. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
  223. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
  224. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
  225. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
  226. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
  227. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
  228. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
  229. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
  230. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
  231. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
  232. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
  233. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
  234. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
  235. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
  236. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
  237. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
  238. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
  239. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
  240. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
  241. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
  242. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
  243. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
  244. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
  245. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
  246. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
  247. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
  248. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
  249. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
  250. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
  251. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
  252. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
  253. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
  254. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
  255. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
  256. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
  257. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
  258. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
  259. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
  260. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
  261. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
  262. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
  263. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
  264. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
  265. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
  266. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
  267. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
  268. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
  269. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
  270. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
  271. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
  272. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
  273. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
  274. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
  275. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
  276. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
  277. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
  278. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
  279. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
  280. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
  281. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
  282. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
  283. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
  284. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
  285. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
  286. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
  287. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
  288. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
  289. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
  290. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
  291. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
  292. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
  293. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
  294. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
  295. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
  296. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
  297. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
  298. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
  299. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
  300. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
  301. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
  302. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
  303. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
  304. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
  305. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
  306. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
  307. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
  308. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
  309. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
  310. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
  311. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
  312. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
  313. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
  314. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
  315. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
  316. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
  317. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
  318. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
  319. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Server-sent events for `GET /api/parts/:slug/events`. One fs.watch per part folder
3
+ * (recursive, so assets/ is covered even when it is created later), shared by every
4
+ * subscriber and closed with the last one. Events are debounced per kind:
5
+ *
6
+ * event: changed
7
+ * data: {"kind":"part","hash":"<sha1 of part.json>"} part.json changed
8
+ * data: {"kind":"assets"} something under assets/ changed
9
+ *
10
+ * The hash lets the app ignore the echo of its own save. build/ and *.fzpz writes are
11
+ * ignored: they are output, never input.
12
+ */
13
+ import { createHash } from "node:crypto";
14
+ import { watch } from "node:fs";
15
+ import { readFile } from "node:fs/promises";
16
+ import { join } from "node:path";
17
+ import { ASSETS_DIR, PART_FILE } from "../core/workspace.js";
18
+ /** sha1 hex of a string (the app compares it with the hash of what it saved). */
19
+ export function textHash(text) {
20
+ return createHash("sha1").update(text, "utf8").digest("hex");
21
+ }
22
+ /** Classify a watcher filename relative to the part folder; undefined = ignore. */
23
+ export function classifyChange(filename) {
24
+ if (!filename)
25
+ return "part";
26
+ const f = filename.replace(/\\/g, "/");
27
+ if (f === PART_FILE)
28
+ return "part";
29
+ if (f === ASSETS_DIR || f.startsWith(`${ASSETS_DIR}/`)) {
30
+ // writeFileAtomic's temp file; the rename that follows reports the real name.
31
+ if (/\.tmp-\d+$/.test(f))
32
+ return undefined;
33
+ return "assets";
34
+ }
35
+ return undefined;
36
+ }
37
+ /** Format one SSE frame. */
38
+ export function sseFrame(event, data) {
39
+ return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
40
+ }
41
+ export class EventHub {
42
+ debounceMs;
43
+ channels = new Map();
44
+ constructor(debounceMs = 200) {
45
+ this.debounceMs = debounceMs;
46
+ }
47
+ /** Subscribe an SSE response to changes in `dir` (keyed by `slug`). */
48
+ subscribe(slug, dir, res) {
49
+ let ch = this.channels.get(slug);
50
+ if (!ch) {
51
+ ch = { clients: new Set(), timers: new Map() };
52
+ this.channels.set(slug, ch);
53
+ const channel = ch;
54
+ try {
55
+ channel.watcher = watch(dir, { recursive: true }, (_type, filename) => {
56
+ const kind = classifyChange(filename);
57
+ if (kind)
58
+ this.schedule(slug, dir, channel, kind);
59
+ });
60
+ channel.watcher.on("error", () => undefined);
61
+ }
62
+ catch {
63
+ // The folder vanished; subscribers still get the hello and keep-alives.
64
+ }
65
+ }
66
+ ch.clients.add(res);
67
+ res.writeHead(200, {
68
+ "content-type": "text/event-stream; charset=utf-8",
69
+ "cache-control": "no-store",
70
+ connection: "keep-alive",
71
+ });
72
+ res.write(sseFrame("hello", { slug }));
73
+ const channel = ch;
74
+ res.on("close", () => {
75
+ channel.clients.delete(res);
76
+ if (channel.clients.size === 0)
77
+ this.drop(slug);
78
+ });
79
+ }
80
+ schedule(slug, dir, ch, kind) {
81
+ const old = ch.timers.get(kind);
82
+ if (old)
83
+ clearTimeout(old);
84
+ ch.timers.set(kind, setTimeout(() => {
85
+ ch.timers.delete(kind);
86
+ void this.fire(dir, ch, kind);
87
+ }, this.debounceMs));
88
+ }
89
+ async fire(dir, ch, kind) {
90
+ const ev = { kind };
91
+ if (kind === "part") {
92
+ try {
93
+ ev.hash = textHash(await readFile(join(dir, PART_FILE), "utf8"));
94
+ }
95
+ catch {
96
+ // deleted or mid-rename: send the event without a hash
97
+ }
98
+ }
99
+ const frame = sseFrame("changed", ev);
100
+ for (const c of ch.clients)
101
+ c.write(frame);
102
+ }
103
+ /** Send a comment line to every client (keeps proxies and the browser from timing out). */
104
+ ping() {
105
+ for (const ch of this.channels.values())
106
+ for (const c of ch.clients)
107
+ c.write(": ping\n\n");
108
+ }
109
+ /** Number of open subscriptions (tests). */
110
+ get size() {
111
+ let n = 0;
112
+ for (const ch of this.channels.values())
113
+ n += ch.clients.size;
114
+ return n;
115
+ }
116
+ drop(slug) {
117
+ const ch = this.channels.get(slug);
118
+ if (!ch)
119
+ return;
120
+ for (const t of ch.timers.values())
121
+ clearTimeout(t);
122
+ ch.watcher?.close();
123
+ this.channels.delete(slug);
124
+ }
125
+ /** End every stream and close every watcher. */
126
+ close() {
127
+ for (const [slug, ch] of [...this.channels]) {
128
+ for (const c of ch.clients)
129
+ c.end();
130
+ this.drop(slug);
131
+ }
132
+ }
133
+ }
134
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/server/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Small HTTP helpers for the fritzkit ui server (plain node:http, no framework): JSON
3
+ * responses, bounded body reading, an error type that carries a status code, and the
4
+ * loopback-only Host/Origin guard.
5
+ */
6
+ import type { IncomingMessage, ServerResponse } from "node:http";
7
+ /** An error the API turns into `{ error, ...extra }` with this HTTP status. */
8
+ export declare class HttpError extends Error {
9
+ readonly status: number;
10
+ readonly extra: Record<string, unknown>;
11
+ constructor(status: number, message: string, extra?: Record<string, unknown>);
12
+ }
13
+ /** Write a JSON response (2-space, trailing newline, no caching). */
14
+ export declare function sendJson(res: ServerResponse, status: number, body: unknown): void;
15
+ /** Write a text/binary response. */
16
+ export declare function sendBody(res: ServerResponse, status: number, contentType: string, body: string | Uint8Array, cache?: string, extra?: Record<string, string>): void;
17
+ /** Read the whole request body; 413 past `limit` bytes. */
18
+ export declare function readBody(req: IncomingMessage, limit: number): Promise<Buffer>;
19
+ /** Read and parse a JSON body; 400 on bad JSON. An empty body parses as `{}`. */
20
+ export declare function readJson(req: IncomingMessage, limit: number): Promise<unknown>;
21
+ /**
22
+ * Headers for content derived from user files or user-controlled spec strings (assets,
23
+ * compiled SVG previews): no MIME sniffing, and a sandboxed CSP so an SVG or HTML opened
24
+ * directly cannot run script on the app's origin.
25
+ */
26
+ export declare const UNTRUSTED_HEADERS: Record<string, string>;
27
+ /** Hostname of a Host header or Origin URL ("127.0.0.1:5173" -> "127.0.0.1"). */
28
+ export declare function hostnameOf(value: string): string;
29
+ /**
30
+ * Loopback guard against DNS rebinding and cross-site requests: the Host header must
31
+ * name a loopback host and, when the browser sends an Origin, so must the Origin (an
32
+ * http: URL). With `localPort` (the port the request arrived on) both must also name
33
+ * that port, so a page served by another program on another loopback port cannot send
34
+ * "simple" (no preflight) POSTs to this server.
35
+ */
36
+ export declare function isLoopbackRequest(headers: IncomingMessage["headers"], localPort?: number): boolean;
@@ -0,0 +1,127 @@
1
+ /** An error the API turns into `{ error, ...extra }` with this HTTP status. */
2
+ export class HttpError extends Error {
3
+ status;
4
+ extra;
5
+ constructor(status, message, extra = {}) {
6
+ super(message);
7
+ this.status = status;
8
+ this.extra = extra;
9
+ this.name = "HttpError";
10
+ }
11
+ }
12
+ /** Write a JSON response (2-space, trailing newline, no caching). */
13
+ export function sendJson(res, status, body) {
14
+ const text = JSON.stringify(body, null, 2) + "\n";
15
+ res.writeHead(status, {
16
+ "content-type": "application/json; charset=utf-8",
17
+ "cache-control": "no-store",
18
+ "content-length": Buffer.byteLength(text),
19
+ });
20
+ res.end(text);
21
+ }
22
+ /** Write a text/binary response. */
23
+ export function sendBody(res, status, contentType, body, cache = "no-store", extra = {}) {
24
+ const buf = typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.from(body);
25
+ res.writeHead(status, {
26
+ ...extra,
27
+ "content-type": contentType,
28
+ "cache-control": cache,
29
+ "content-length": buf.length,
30
+ });
31
+ res.end(buf);
32
+ }
33
+ /** Read the whole request body; 413 past `limit` bytes. */
34
+ export async function readBody(req, limit) {
35
+ const declared = Number(req.headers["content-length"] ?? "0");
36
+ if (declared > limit)
37
+ throw new HttpError(413, `request body is larger than ${limit} bytes`);
38
+ const chunks = [];
39
+ let size = 0;
40
+ for await (const chunk of req) {
41
+ const b = chunk;
42
+ size += b.length;
43
+ if (size > limit)
44
+ throw new HttpError(413, `request body is larger than ${limit} bytes`);
45
+ chunks.push(b);
46
+ }
47
+ return Buffer.concat(chunks);
48
+ }
49
+ /** Read and parse a JSON body; 400 on bad JSON. An empty body parses as `{}`. */
50
+ export async function readJson(req, limit) {
51
+ const buf = await readBody(req, limit);
52
+ const text = buf.toString("utf8").replace(/^\uFEFF/u, "");
53
+ if (text.trim() === "")
54
+ return {};
55
+ try {
56
+ return JSON.parse(text);
57
+ }
58
+ catch (e) {
59
+ throw new HttpError(400, `body is not valid JSON: ${e instanceof Error ? e.message : e}`);
60
+ }
61
+ }
62
+ /**
63
+ * Headers for content derived from user files or user-controlled spec strings (assets,
64
+ * compiled SVG previews): no MIME sniffing, and a sandboxed CSP so an SVG or HTML opened
65
+ * directly cannot run script on the app's origin.
66
+ */
67
+ export const UNTRUSTED_HEADERS = {
68
+ "x-content-type-options": "nosniff",
69
+ "content-security-policy": "sandbox; default-src 'none'; img-src data:; style-src 'unsafe-inline'",
70
+ };
71
+ const LOOPBACK = new Set(["127.0.0.1", "localhost", "[::1]", "::1"]);
72
+ /** Hostname of a Host header or Origin URL ("127.0.0.1:5173" -> "127.0.0.1"). */
73
+ export function hostnameOf(value) {
74
+ const v = value.trim().toLowerCase();
75
+ if (v.includes("://")) {
76
+ try {
77
+ return new URL(v).hostname;
78
+ }
79
+ catch {
80
+ return v;
81
+ }
82
+ }
83
+ if (v.startsWith("["))
84
+ return v.slice(0, v.indexOf("]") + 1);
85
+ const colon = v.lastIndexOf(":");
86
+ return colon >= 0 ? v.slice(0, colon) : v;
87
+ }
88
+ /** Port of a Host header ("127.0.0.1:5173" -> 5173; no port -> 80). */
89
+ function hostPortOf(value) {
90
+ const v = value.trim();
91
+ const close = v.lastIndexOf("]");
92
+ const colon = v.lastIndexOf(":");
93
+ if (colon < 0 || colon < close)
94
+ return 80;
95
+ const n = Number(v.slice(colon + 1));
96
+ return Number.isInteger(n) ? n : -1;
97
+ }
98
+ /**
99
+ * Loopback guard against DNS rebinding and cross-site requests: the Host header must
100
+ * name a loopback host and, when the browser sends an Origin, so must the Origin (an
101
+ * http: URL). With `localPort` (the port the request arrived on) both must also name
102
+ * that port, so a page served by another program on another loopback port cannot send
103
+ * "simple" (no preflight) POSTs to this server.
104
+ */
105
+ export function isLoopbackRequest(headers, localPort) {
106
+ const host = headers.host;
107
+ if (host === undefined || !LOOPBACK.has(hostnameOf(host)))
108
+ return false;
109
+ if (localPort !== undefined && hostPortOf(host) !== localPort)
110
+ return false;
111
+ const origin = headers.origin;
112
+ if (origin === undefined)
113
+ return true;
114
+ let url;
115
+ try {
116
+ url = new URL(origin);
117
+ }
118
+ catch {
119
+ return false;
120
+ }
121
+ if (url.protocol !== "http:" || !LOOPBACK.has(url.hostname))
122
+ return false;
123
+ if (localPort !== undefined && Number(url.port || "80") !== localPort)
124
+ return false;
125
+ return true;
126
+ }
127
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { type IncomingMessage, type Server } from "node:http";
2
+ import type { ServerResponse } from "node:http";
3
+ import { type CommandContext } from "../cli/context.js";
4
+ export { HttpError } from "./http.js";
5
+ export { EventHub, classifyChange, sseFrame, textHash } from "./events.js";
6
+ export { apiHandler, assetMimeOf, assetPath, isSafeAssetName, listAssets, mimeOf, partPayload, preparePut, withExplain, } from "./api.js";
7
+ export declare const DEFAULT_PORT = 5173;
8
+ export declare const HOST = "127.0.0.1";
9
+ /** How many ports after the default to try before giving up. */
10
+ export declare const PORT_SCAN = 50;
11
+ export interface ServerOptions {
12
+ ctx: CommandContext;
13
+ /** Workspace folder (default: workspaceOf(ctx)). */
14
+ workspace?: string;
15
+ /** Built app folder holding index.html (default: dist/ui next to this package). */
16
+ uiDir?: string;
17
+ /** SSE keep-alive interval (ms). */
18
+ pingMs?: number;
19
+ /** fs.watch debounce (ms). */
20
+ debounceMs?: number;
21
+ }
22
+ export interface UiServer {
23
+ workspace: string;
24
+ uiDir: string;
25
+ /** Handle one request (the Vite dev server mounts this for /api). */
26
+ handle: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
27
+ /** The node:http server (not listening until listen()). */
28
+ server: Server;
29
+ /** Listen on HOST:port; port 0 picks a free port. Resolves to the bound port. */
30
+ listen: (port: number) => Promise<number>;
31
+ /** Stop listening, end SSE streams, close watchers. */
32
+ close: () => Promise<void>;
33
+ }
34
+ /** dist/ui of this package: "../../dist/ui" works from both src/server and dist/server. */
35
+ export declare function defaultUiDir(): string;
36
+ /** True when `dir` holds a built app. */
37
+ export declare function hasBuiltUi(dir: string): boolean;
38
+ /** Build the server (not yet listening). */
39
+ export declare function createServer(opts: ServerOptions): Promise<UiServer>;
40
+ /**
41
+ * Listen on `start`, or (unless strict) the next free port up to PORT_SCAN above it.
42
+ * Throws the last error (EADDRINUSE) when none is free.
43
+ */
44
+ export declare function listenFrom(ui: UiServer, start: number, strict: boolean): Promise<number>;
45
+ /** The command that opens `url` in the default browser (no shell involved). */
46
+ export declare function openCommand(url: string, platform?: NodeJS.Platform): {
47
+ cmd: string;
48
+ args: string[];
49
+ };
50
+ /** Fire and forget: open the browser; failures are ignored (the URL is printed anyway). */
51
+ export declare function openBrowser(url: string, platform?: NodeJS.Platform): void;
52
+ /** App URL for the server, optionally deep-linked to a part. */
53
+ export declare function appUrl(port: number, slug?: string): string;
54
+ export interface StartOptions extends ServerOptions {
55
+ /** Port to try first (default 5173). */
56
+ port?: number;
57
+ /** Fail instead of trying the next port when `port` is taken. */
58
+ strictPort?: boolean;
59
+ }
60
+ /** createServer + listen; the server is tracked so stopAll() can close it (tests). */
61
+ export declare function startUi(opts: StartOptions): Promise<UiServer & {
62
+ port: number;
63
+ }>;
64
+ /** Close every server startUi started. */
65
+ export declare function stopAll(): Promise<void>;
@@ -0,0 +1,193 @@
1
+ /**
2
+ * The fritzkit ui server (spec D15/D16): a loopback-only node:http server that serves the
3
+ * built app from dist/ui and the JSON API under /api (see api.ts and docs/ui.md).
4
+ *
5
+ * createServer({ workspace, ctx }) takes the same injectable CommandContext as the CLI,
6
+ * so tests fix the date, fake `isRunning` and point the Fritzing folders at a temp dir.
7
+ */
8
+ import { spawn } from "node:child_process";
9
+ import { existsSync } from "node:fs";
10
+ import { readFile, stat } from "node:fs/promises";
11
+ import { createServer as createHttpServer } from "node:http";
12
+ import { join, resolve, sep } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { workspaceOf } from "../cli/context.js";
15
+ import { apiHandler, mimeOf } from "./api.js";
16
+ import { EventHub } from "./events.js";
17
+ import { HttpError, isLoopbackRequest, sendBody, sendJson } from "./http.js";
18
+ export { HttpError } from "./http.js";
19
+ export { EventHub, classifyChange, sseFrame, textHash } from "./events.js";
20
+ export { apiHandler, assetMimeOf, assetPath, isSafeAssetName, listAssets, mimeOf, partPayload, preparePut, withExplain, } from "./api.js";
21
+ export const DEFAULT_PORT = 5173;
22
+ export const HOST = "127.0.0.1";
23
+ /** How many ports after the default to try before giving up. */
24
+ export const PORT_SCAN = 50;
25
+ /** dist/ui of this package: "../../dist/ui" works from both src/server and dist/server. */
26
+ export function defaultUiDir() {
27
+ return fileURLToPath(new URL("../../dist/ui", import.meta.url));
28
+ }
29
+ /** True when `dir` holds a built app. */
30
+ export function hasBuiltUi(dir) {
31
+ return existsSync(join(dir, "index.html"));
32
+ }
33
+ async function serveStatic(uiDir, pathname, res) {
34
+ let rel;
35
+ try {
36
+ rel = decodeURIComponent(pathname);
37
+ }
38
+ catch {
39
+ throw new HttpError(400, "bad percent-encoding in the path");
40
+ }
41
+ if (rel.includes("\0"))
42
+ throw new HttpError(400, "bad path");
43
+ const root = resolve(uiDir);
44
+ let abs = resolve(root, `.${rel}`);
45
+ if (abs !== root && !abs.startsWith(root + sep))
46
+ throw new HttpError(403, "outside the app");
47
+ let isFile;
48
+ try {
49
+ isFile = (await stat(abs)).isFile();
50
+ }
51
+ catch {
52
+ isFile = false;
53
+ }
54
+ if (!isFile) {
55
+ // Client-side routes fall back to the app; missing files with an extension are 404.
56
+ if (/\.[A-Za-z0-9]+$/.test(rel))
57
+ throw new HttpError(404, `no such file ${rel}`);
58
+ abs = join(root, "index.html");
59
+ }
60
+ let data;
61
+ try {
62
+ data = await readFile(abs);
63
+ }
64
+ catch {
65
+ throw new HttpError(404, `the app is not built in ${root}: run npm run ui:build`);
66
+ }
67
+ const immutable = abs.includes(`${sep}assets${sep}`);
68
+ sendBody(res, 200, mimeOf(abs), data, immutable ? "public, max-age=31536000, immutable" : "no-cache");
69
+ }
70
+ /** Build the server (not yet listening). */
71
+ export async function createServer(opts) {
72
+ const workspace = resolve(opts.workspace ?? workspaceOf(opts.ctx));
73
+ const uiDir = resolve(opts.uiDir ?? defaultUiDir());
74
+ const hub = new EventHub(opts.debounceMs ?? 200);
75
+ const api = apiHandler({ workspace, ctx: opts.ctx, hub });
76
+ const handle = async (req, res) => {
77
+ try {
78
+ if (!isLoopbackRequest(req.headers, req.socket.localPort)) {
79
+ throw new HttpError(403, "fritzkit ui only answers loopback requests from its own pages");
80
+ }
81
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
82
+ if (url.pathname === "/api" || url.pathname.startsWith("/api/")) {
83
+ await api(req, res);
84
+ return;
85
+ }
86
+ if (req.method !== "GET" && req.method !== "HEAD") {
87
+ throw new HttpError(405, `${req.method} is not allowed on ${url.pathname}`);
88
+ }
89
+ await serveStatic(uiDir, url.pathname, res);
90
+ }
91
+ catch (e) {
92
+ if (res.headersSent) {
93
+ res.end();
94
+ return;
95
+ }
96
+ if (e instanceof HttpError)
97
+ sendJson(res, e.status, { error: e.message });
98
+ else
99
+ sendJson(res, 500, { error: e instanceof Error ? e.message : String(e) });
100
+ }
101
+ };
102
+ const server = createHttpServer((req, res) => void handle(req, res));
103
+ const ping = setInterval(() => hub.ping(), opts.pingMs ?? 25000);
104
+ ping.unref();
105
+ const listen = (port) => new Promise((ok, fail) => {
106
+ const onError = (e) => {
107
+ server.off("listening", onListening);
108
+ fail(e);
109
+ };
110
+ const onListening = () => {
111
+ server.off("error", onError);
112
+ ok(server.address().port);
113
+ };
114
+ server.once("error", onError);
115
+ server.once("listening", onListening);
116
+ server.listen(port, HOST);
117
+ });
118
+ const close = async () => {
119
+ clearInterval(ping);
120
+ hub.close();
121
+ if (!server.listening)
122
+ return;
123
+ await new Promise((ok) => {
124
+ server.close(() => ok());
125
+ server.closeAllConnections();
126
+ });
127
+ };
128
+ return { workspace, uiDir, handle, server, listen, close };
129
+ }
130
+ /**
131
+ * Listen on `start`, or (unless strict) the next free port up to PORT_SCAN above it.
132
+ * Throws the last error (EADDRINUSE) when none is free.
133
+ */
134
+ export async function listenFrom(ui, start, strict) {
135
+ const last = strict || start === 0 ? start : Math.min(65535, start + PORT_SCAN);
136
+ let err;
137
+ for (let p = start; p <= last; p++) {
138
+ try {
139
+ return await ui.listen(p);
140
+ }
141
+ catch (e) {
142
+ err = e;
143
+ if (e.code !== "EADDRINUSE")
144
+ throw e;
145
+ }
146
+ }
147
+ throw err;
148
+ }
149
+ /** The command that opens `url` in the default browser (no shell involved). */
150
+ export function openCommand(url, platform = process.platform) {
151
+ if (platform === "win32")
152
+ return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
153
+ if (platform === "darwin")
154
+ return { cmd: "open", args: [url] };
155
+ return { cmd: "xdg-open", args: [url] };
156
+ }
157
+ /** Fire and forget: open the browser; failures are ignored (the URL is printed anyway). */
158
+ export function openBrowser(url, platform = process.platform) {
159
+ const { cmd, args } = openCommand(url, platform);
160
+ try {
161
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true, shell: false });
162
+ child.on("error", () => undefined);
163
+ child.unref();
164
+ }
165
+ catch {
166
+ // no browser launcher: the caller prints the URL
167
+ }
168
+ }
169
+ /** App URL for the server, optionally deep-linked to a part. */
170
+ export function appUrl(port, slug) {
171
+ return `http://${HOST}:${port}/${slug ? `#/${encodeURIComponent(slug)}` : ""}`;
172
+ }
173
+ const running = new Set();
174
+ /** createServer + listen; the server is tracked so stopAll() can close it (tests). */
175
+ export async function startUi(opts) {
176
+ const ui = await createServer(opts);
177
+ try {
178
+ const port = await listenFrom(ui, opts.port ?? DEFAULT_PORT, opts.strictPort ?? false);
179
+ running.add(ui);
180
+ return { ...ui, port };
181
+ }
182
+ catch (e) {
183
+ await ui.close();
184
+ throw e;
185
+ }
186
+ }
187
+ /** Close every server startUi started. */
188
+ export async function stopAll() {
189
+ const all = [...running];
190
+ running.clear();
191
+ await Promise.all(all.map((s) => s.close()));
192
+ }
193
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}