fluidcad 0.0.33 → 0.0.35

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 (363) hide show
  1. package/README.md +72 -2
  2. package/bin/commands/init.js +55 -0
  3. package/bin/commands/login.js +120 -0
  4. package/bin/commands/mcp.js +33 -0
  5. package/bin/commands/pack.js +49 -0
  6. package/bin/commands/publish.js +136 -0
  7. package/bin/commands/serve.js +77 -0
  8. package/bin/fluidcad.js +21 -107
  9. package/bin/lib/api-client.js +40 -0
  10. package/bin/lib/browser.js +16 -0
  11. package/bin/lib/config.js +39 -0
  12. package/bin/lib/model-config.js +38 -0
  13. package/bin/lib/workspace.js +57 -0
  14. package/lib/dist/common/scene-object.d.ts +2 -1
  15. package/lib/dist/common/scene-object.js +3 -2
  16. package/lib/dist/common/shape-factory.d.ts +2 -1
  17. package/lib/dist/common/shape-factory.js +4 -0
  18. package/lib/dist/common/transformable-primitive.d.ts +6 -5
  19. package/lib/dist/common/transformable-primitive.js +8 -7
  20. package/lib/dist/common/vertex.js +0 -1
  21. package/lib/dist/core/2d/aline.d.ts +4 -3
  22. package/lib/dist/core/2d/aline.js +3 -2
  23. package/lib/dist/core/2d/arc.d.ts +3 -2
  24. package/lib/dist/core/2d/arc.js +4 -3
  25. package/lib/dist/core/2d/bezier.d.ts +8 -6
  26. package/lib/dist/core/2d/circle.d.ts +4 -3
  27. package/lib/dist/core/2d/circle.js +3 -2
  28. package/lib/dist/core/2d/ellipse.d.ts +5 -4
  29. package/lib/dist/core/2d/ellipse.js +5 -4
  30. package/lib/dist/core/2d/hline.d.ts +4 -3
  31. package/lib/dist/core/2d/hline.js +5 -3
  32. package/lib/dist/core/2d/line.js +1 -0
  33. package/lib/dist/core/2d/offset.d.ts +3 -2
  34. package/lib/dist/core/2d/offset.js +6 -5
  35. package/lib/dist/core/2d/polygon.d.ts +5 -4
  36. package/lib/dist/core/2d/polygon.js +10 -9
  37. package/lib/dist/core/2d/rect.d.ts +4 -3
  38. package/lib/dist/core/2d/rect.js +10 -9
  39. package/lib/dist/core/2d/slot.d.ts +14 -6
  40. package/lib/dist/core/2d/slot.js +19 -8
  41. package/lib/dist/core/2d/tarc.d.ts +20 -2
  42. package/lib/dist/core/2d/tarc.js +24 -0
  43. package/lib/dist/core/2d/vline.d.ts +4 -3
  44. package/lib/dist/core/2d/vline.js +5 -3
  45. package/lib/dist/core/chamfer.d.ts +5 -4
  46. package/lib/dist/core/chamfer.js +7 -6
  47. package/lib/dist/core/color.d.ts +3 -2
  48. package/lib/dist/core/color.js +2 -1
  49. package/lib/dist/core/cut.d.ts +4 -3
  50. package/lib/dist/core/cut.js +5 -4
  51. package/lib/dist/core/cylinder.d.ts +2 -1
  52. package/lib/dist/core/cylinder.js +2 -1
  53. package/lib/dist/core/draft.d.ts +3 -2
  54. package/lib/dist/core/draft.js +3 -2
  55. package/lib/dist/core/extrude.d.ts +4 -3
  56. package/lib/dist/core/extrude.js +5 -4
  57. package/lib/dist/core/fillet.d.ts +5 -4
  58. package/lib/dist/core/fillet.js +6 -5
  59. package/lib/dist/core/index.d.ts +1 -0
  60. package/lib/dist/core/index.js +1 -0
  61. package/lib/dist/core/interfaces.d.ts +55 -25
  62. package/lib/dist/core/param.d.ts +74 -0
  63. package/lib/dist/core/param.js +147 -0
  64. package/lib/dist/core/repeat.d.ts +2 -1
  65. package/lib/dist/core/repeat.js +72 -54
  66. package/lib/dist/core/revolve.d.ts +2 -1
  67. package/lib/dist/core/revolve.js +3 -2
  68. package/lib/dist/core/rib.d.ts +3 -2
  69. package/lib/dist/core/rib.js +6 -2
  70. package/lib/dist/core/rotate.d.ts +5 -4
  71. package/lib/dist/core/rotate.js +4 -3
  72. package/lib/dist/core/shell.d.ts +3 -2
  73. package/lib/dist/core/shell.js +3 -2
  74. package/lib/dist/core/sphere.d.ts +3 -2
  75. package/lib/dist/core/sphere.js +2 -1
  76. package/lib/dist/core/translate.d.ts +7 -6
  77. package/lib/dist/core/translate.js +6 -5
  78. package/lib/dist/features/2d/arc.d.ts +8 -2
  79. package/lib/dist/features/2d/arc.js +94 -17
  80. package/lib/dist/features/2d/back.js +3 -2
  81. package/lib/dist/features/2d/bezier.js +16 -16
  82. package/lib/dist/features/2d/circle.js +4 -0
  83. package/lib/dist/features/2d/ellipse.js +4 -0
  84. package/lib/dist/features/2d/hline.d.ts +3 -0
  85. package/lib/dist/features/2d/hline.js +9 -2
  86. package/lib/dist/features/2d/line.d.ts +3 -0
  87. package/lib/dist/features/2d/line.js +11 -3
  88. package/lib/dist/features/2d/sketch.d.ts +4 -0
  89. package/lib/dist/features/2d/sketch.js +25 -0
  90. package/lib/dist/features/2d/slot.d.ts +5 -0
  91. package/lib/dist/features/2d/slot.js +52 -7
  92. package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
  93. package/lib/dist/features/2d/tarc-constrained.js +8 -0
  94. package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
  95. package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
  96. package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
  97. package/lib/dist/features/2d/tarc-to-object.js +66 -0
  98. package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
  99. package/lib/dist/features/2d/tarc-to-point-tangent.js +6 -0
  100. package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
  101. package/lib/dist/features/2d/tarc-to-point.js +6 -0
  102. package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
  103. package/lib/dist/features/2d/tarc-with-tangent.js +6 -0
  104. package/lib/dist/features/2d/tarc.d.ts +2 -0
  105. package/lib/dist/features/2d/tarc.js +6 -0
  106. package/lib/dist/features/2d/vline.d.ts +3 -0
  107. package/lib/dist/features/2d/vline.js +9 -2
  108. package/lib/dist/features/copy-circular.d.ts +4 -3
  109. package/lib/dist/features/copy-circular.js +16 -9
  110. package/lib/dist/features/copy-circular2d.js +16 -9
  111. package/lib/dist/features/copy-linear.d.ts +4 -3
  112. package/lib/dist/features/copy-linear.js +18 -12
  113. package/lib/dist/features/copy-linear2d.js +18 -12
  114. package/lib/dist/features/extrude-base.d.ts +13 -3
  115. package/lib/dist/features/extrude-base.js +32 -3
  116. package/lib/dist/features/extrude-to-face.js +1 -5
  117. package/lib/dist/features/extrude-two-distances.js +1 -2
  118. package/lib/dist/features/extrude.js +1 -2
  119. package/lib/dist/features/mirror-feature.d.ts +3 -2
  120. package/lib/dist/features/mirror-feature.js +1 -1
  121. package/lib/dist/features/mirror-shape2d.js +2 -2
  122. package/lib/dist/features/repeat-base.d.ts +13 -0
  123. package/lib/dist/features/repeat-base.js +21 -0
  124. package/lib/dist/features/repeat-circular.d.ts +8 -7
  125. package/lib/dist/features/repeat-circular.js +7 -3
  126. package/lib/dist/features/repeat-linear.d.ts +9 -7
  127. package/lib/dist/features/repeat-linear.js +9 -3
  128. package/lib/dist/features/repeat-matrix.d.ts +3 -1
  129. package/lib/dist/features/repeat-matrix.js +7 -2
  130. package/lib/dist/features/shell.d.ts +4 -1
  131. package/lib/dist/features/shell.js +14 -3
  132. package/lib/dist/helpers/clone-transform.d.ts +2 -1
  133. package/lib/dist/index.d.ts +12 -1
  134. package/lib/dist/index.js +11 -4
  135. package/lib/dist/io/file-import.d.ts +7 -0
  136. package/lib/dist/io/file-import.js +30 -10
  137. package/lib/dist/math/lazy-matrix.d.ts +36 -0
  138. package/lib/dist/math/lazy-matrix.js +134 -0
  139. package/lib/dist/oc/boolean-ops.d.ts +2 -2
  140. package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
  141. package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
  142. package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
  143. package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
  144. package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
  145. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
  146. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
  147. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
  148. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
  149. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
  150. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
  151. package/lib/dist/oc/mesh.d.ts +9 -4
  152. package/lib/dist/oc/mesh.js +14 -13
  153. package/lib/dist/oc/shell-ops.d.ts +2 -1
  154. package/lib/dist/oc/shell-ops.js +5 -2
  155. package/lib/dist/param-registry.d.ts +34 -0
  156. package/lib/dist/param-registry.js +60 -0
  157. package/lib/dist/rendering/mesh-builder.d.ts +3 -0
  158. package/lib/dist/rendering/mesh-builder.js +10 -5
  159. package/lib/dist/rendering/render-edge.d.ts +2 -1
  160. package/lib/dist/rendering/render-edge.js +2 -2
  161. package/lib/dist/rendering/render-face.d.ts +2 -1
  162. package/lib/dist/rendering/render-face.js +2 -2
  163. package/lib/dist/rendering/render-solid.d.ts +2 -1
  164. package/lib/dist/rendering/render-solid.js +2 -2
  165. package/lib/dist/rendering/render-wire.d.ts +2 -1
  166. package/lib/dist/rendering/render-wire.js +2 -2
  167. package/lib/dist/rendering/render.d.ts +3 -0
  168. package/lib/dist/rendering/render.js +7 -2
  169. package/lib/dist/scene-manager.d.ts +4 -2
  170. package/lib/dist/scene-manager.js +12 -4
  171. package/lib/dist/tests/features/2d/arc.test.js +64 -0
  172. package/lib/dist/tests/features/2d/back.test.js +17 -1
  173. package/lib/dist/tests/features/2d/tarc.test.js +157 -0
  174. package/lib/dist/tests/features/copy-circular.test.js +1 -1
  175. package/lib/dist/tests/features/copy-linear.test.js +10 -10
  176. package/lib/dist/tests/features/repeat-user-repro-cache.test.d.ts +1 -0
  177. package/lib/dist/tests/features/repeat-user-repro-cache.test.js +97 -0
  178. package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
  179. package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
  180. package/lib/dist/tests/features/shell.test.js +36 -0
  181. package/lib/dist/tests/global-setup.js +2 -1
  182. package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
  183. package/lib/dist/tests/helpers/extract-blocks.js +56 -0
  184. package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
  185. package/lib/dist/tests/llm-docs-examples.test.js +62 -0
  186. package/lib/dist/tests/setup.js +2 -1
  187. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  188. package/llm-docs/.coverage-allowlist.txt +9 -0
  189. package/llm-docs/api/arc.md +48 -0
  190. package/llm-docs/api/axis.md +42 -0
  191. package/llm-docs/api/bezier.md +41 -0
  192. package/llm-docs/api/booleans.md +44 -0
  193. package/llm-docs/api/chamfer.md +40 -0
  194. package/llm-docs/api/circle.md +36 -0
  195. package/llm-docs/api/color.md +34 -0
  196. package/llm-docs/api/connect.md +41 -0
  197. package/llm-docs/api/constraint-qualifiers.md +48 -0
  198. package/llm-docs/api/copy.md +63 -0
  199. package/llm-docs/api/cursor-lines.md +50 -0
  200. package/llm-docs/api/cursor-move.md +61 -0
  201. package/llm-docs/api/cut.md +55 -0
  202. package/llm-docs/api/draft.md +36 -0
  203. package/llm-docs/api/edge-filter.md +57 -0
  204. package/llm-docs/api/ellipse.md +34 -0
  205. package/llm-docs/api/extrude.md +74 -0
  206. package/llm-docs/api/face-filter.md +61 -0
  207. package/llm-docs/api/fillet.md +51 -0
  208. package/llm-docs/api/index.json +139 -0
  209. package/llm-docs/api/line.md +42 -0
  210. package/llm-docs/api/load.md +37 -0
  211. package/llm-docs/api/local.md +38 -0
  212. package/llm-docs/api/loft.md +37 -0
  213. package/llm-docs/api/mirror.md +44 -0
  214. package/llm-docs/api/offset.md +36 -0
  215. package/llm-docs/api/part.md +40 -0
  216. package/llm-docs/api/plane.md +44 -0
  217. package/llm-docs/api/polygon.md +37 -0
  218. package/llm-docs/api/primitive-solids.md +39 -0
  219. package/llm-docs/api/project-intersect.md +48 -0
  220. package/llm-docs/api/rect.md +48 -0
  221. package/llm-docs/api/remove.md +32 -0
  222. package/llm-docs/api/repeat.md +79 -0
  223. package/llm-docs/api/revolve.md +38 -0
  224. package/llm-docs/api/rib.md +40 -0
  225. package/llm-docs/api/rotate.md +37 -0
  226. package/llm-docs/api/select.md +41 -0
  227. package/llm-docs/api/shell.md +41 -0
  228. package/llm-docs/api/sketch.md +76 -0
  229. package/llm-docs/api/slot.md +36 -0
  230. package/llm-docs/api/split-trim.md +42 -0
  231. package/llm-docs/api/sweep.md +43 -0
  232. package/llm-docs/api/tarc.md +45 -0
  233. package/llm-docs/api/tcircle.md +38 -0
  234. package/llm-docs/api/tline.md +42 -0
  235. package/llm-docs/api/translate.md +40 -0
  236. package/llm-docs/api/types/aline.md +35 -0
  237. package/llm-docs/api/types/arc-angles.md +29 -0
  238. package/llm-docs/api/types/arc-points.md +48 -0
  239. package/llm-docs/api/types/axis-like.md +38 -0
  240. package/llm-docs/api/types/axis.md +21 -0
  241. package/llm-docs/api/types/boolean-operation.md +50 -0
  242. package/llm-docs/api/types/circular-repeat-options.md +31 -0
  243. package/llm-docs/api/types/common.md +32 -0
  244. package/llm-docs/api/types/cut.md +125 -0
  245. package/llm-docs/api/types/draft.md +21 -0
  246. package/llm-docs/api/types/extrudable-geometry.md +23 -0
  247. package/llm-docs/api/types/extrude.md +194 -0
  248. package/llm-docs/api/types/geometry.md +51 -0
  249. package/llm-docs/api/types/hline.md +35 -0
  250. package/llm-docs/api/types/linear-repeat-options.md +31 -0
  251. package/llm-docs/api/types/loft.md +154 -0
  252. package/llm-docs/api/types/mirror.md +35 -0
  253. package/llm-docs/api/types/offset.md +31 -0
  254. package/llm-docs/api/types/plane-like.md +35 -0
  255. package/llm-docs/api/types/plane-transform-options.md +29 -0
  256. package/llm-docs/api/types/plane.md +21 -0
  257. package/llm-docs/api/types/point-like.md +22 -0
  258. package/llm-docs/api/types/point2dlike.md +26 -0
  259. package/llm-docs/api/types/polygon.md +46 -0
  260. package/llm-docs/api/types/rect.md +128 -0
  261. package/llm-docs/api/types/revolve.md +102 -0
  262. package/llm-docs/api/types/rib.md +133 -0
  263. package/llm-docs/api/types/scene-object.md +33 -0
  264. package/llm-docs/api/types/select.md +21 -0
  265. package/llm-docs/api/types/shell.md +54 -0
  266. package/llm-docs/api/types/slot.md +43 -0
  267. package/llm-docs/api/types/sweep.md +189 -0
  268. package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
  269. package/llm-docs/api/types/transformable.md +93 -0
  270. package/llm-docs/api/types/trim.md +27 -0
  271. package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
  272. package/llm-docs/api/types/vertex.md +17 -0
  273. package/llm-docs/api/types/vline.md +35 -0
  274. package/llm-docs/concepts/coordinate-system.md +45 -0
  275. package/llm-docs/concepts/history-and-rollback.md +40 -0
  276. package/llm-docs/concepts/last-selection.md +49 -0
  277. package/llm-docs/concepts/scene-graph.md +37 -0
  278. package/llm-docs/index.json +1750 -0
  279. package/mcp/dist/client.d.ts +65 -0
  280. package/mcp/dist/client.js +255 -0
  281. package/mcp/dist/discovery.d.ts +11 -0
  282. package/mcp/dist/discovery.js +78 -0
  283. package/mcp/dist/docs-index.d.ts +81 -0
  284. package/mcp/dist/docs-index.js +261 -0
  285. package/mcp/dist/resources.d.ts +4 -0
  286. package/mcp/dist/resources.js +115 -0
  287. package/mcp/dist/server.d.ts +12 -0
  288. package/mcp/dist/server.js +502 -0
  289. package/mcp/dist/tools/coordination.d.ts +9 -0
  290. package/mcp/dist/tools/coordination.js +46 -0
  291. package/mcp/dist/tools/docs.d.ts +66 -0
  292. package/mcp/dist/tools/docs.js +122 -0
  293. package/mcp/dist/tools/engine.d.ts +72 -0
  294. package/mcp/dist/tools/engine.js +190 -0
  295. package/mcp/dist/tools/inspection.d.ts +75 -0
  296. package/mcp/dist/tools/inspection.js +121 -0
  297. package/mcp/dist/tools/screenshot.d.ts +63 -0
  298. package/mcp/dist/tools/screenshot.js +263 -0
  299. package/mcp/dist/tools/source.d.ts +84 -0
  300. package/mcp/dist/tools/source.js +434 -0
  301. package/mcp/dist/tools/workspaces.d.ts +13 -0
  302. package/mcp/dist/tools/workspaces.js +33 -0
  303. package/mcp/dist/types.d.ts +18 -0
  304. package/mcp/dist/types.js +11 -0
  305. package/package.json +27 -7
  306. package/server/dist/api.d.ts +37 -0
  307. package/server/dist/api.js +44 -0
  308. package/server/dist/code-editor.d.ts +100 -0
  309. package/server/dist/code-editor.js +528 -2
  310. package/server/dist/fluidcad-server.d.ts +118 -1
  311. package/server/dist/fluidcad-server.js +350 -62
  312. package/server/dist/global-registry.d.ts +30 -0
  313. package/server/dist/global-registry.js +126 -0
  314. package/server/dist/host/blocked-imports.d.ts +8 -0
  315. package/server/dist/host/blocked-imports.js +30 -0
  316. package/server/dist/{vite-manager.d.ts → host/local-scene-host.d.ts} +3 -1
  317. package/server/dist/{vite-manager.js → host/local-scene-host.js} +6 -26
  318. package/server/dist/host/scene-host.d.ts +19 -0
  319. package/server/dist/host/scene-host.js +1 -0
  320. package/server/dist/index.js +175 -123
  321. package/server/dist/instance-file.d.ts +31 -0
  322. package/server/dist/instance-file.js +73 -0
  323. package/server/dist/lint-fluid-js.d.ts +15 -0
  324. package/server/dist/lint-fluid-js.js +271 -0
  325. package/server/dist/model-package/capture-params.d.ts +19 -0
  326. package/server/dist/model-package/capture-params.js +42 -0
  327. package/server/dist/model-package/pack.d.ts +23 -0
  328. package/server/dist/model-package/pack.js +229 -0
  329. package/server/dist/model-package/types.d.ts +78 -0
  330. package/server/dist/model-package/types.js +17 -0
  331. package/server/dist/routes/editor.d.ts +24 -0
  332. package/server/dist/routes/editor.js +44 -0
  333. package/server/dist/routes/export.d.ts +1 -1
  334. package/server/dist/routes/export.js +45 -8
  335. package/server/dist/routes/health.d.ts +7 -0
  336. package/server/dist/routes/health.js +14 -0
  337. package/server/dist/routes/hit-test.d.ts +3 -0
  338. package/server/dist/routes/hit-test.js +17 -0
  339. package/server/dist/routes/lint.d.ts +10 -0
  340. package/server/dist/routes/lint.js +28 -0
  341. package/server/dist/routes/pack.d.ts +10 -0
  342. package/server/dist/routes/pack.js +47 -0
  343. package/server/dist/routes/params.d.ts +3 -0
  344. package/server/dist/routes/params.js +75 -0
  345. package/server/dist/routes/render.d.ts +33 -0
  346. package/server/dist/routes/render.js +34 -0
  347. package/server/dist/routes/scene.d.ts +5 -0
  348. package/server/dist/routes/scene.js +48 -0
  349. package/server/dist/routes/screenshot.js +68 -1
  350. package/server/dist/routes/sketch-edits.d.ts +3 -0
  351. package/server/dist/routes/sketch-edits.js +542 -0
  352. package/server/dist/routes/timeline.d.ts +3 -0
  353. package/server/dist/routes/timeline.js +49 -0
  354. package/server/dist/server-core.d.ts +53 -0
  355. package/server/dist/server-core.js +147 -0
  356. package/server/dist/ws-protocol.d.ts +156 -3
  357. package/ui/dist/assets/index-CDJmUpFI.css +2 -0
  358. package/ui/dist/assets/index-MRqwG9Vh.js +5417 -0
  359. package/ui/dist/index.html +2 -2
  360. package/server/dist/routes/actions.d.ts +0 -3
  361. package/server/dist/routes/actions.js +0 -309
  362. package/ui/dist/assets/index-CFi9p7wR.js +0 -4946
  363. package/ui/dist/assets/index-DR7c2Qk9.css +0 -2
@@ -1,91 +1,221 @@
1
+ import { createHash } from 'crypto';
1
2
  import { join } from 'path';
2
3
  import { existsSync } from 'fs';
3
- import { ViteManager } from "./vite-manager.js";
4
+ import { LocalSceneHost } from "./host/local-scene-host.js";
4
5
  import { normalizePath } from "./normalize-path.js";
5
6
  import { BreakpointHit } from '../../lib/dist/common/breakpoint-hit.js';
7
+ import { createParamRegistry, getParamRegistry } from '../../lib/dist/index.js';
8
+ /**
9
+ * `sessionId` is the per-renderer state key. In desktop mode it equals the
10
+ * file path being edited (so per-file state survives switching files). In
11
+ * hub mode it's a WebSocket connection UUID (so concurrent viewers stay
12
+ * isolated). Map keys called `sessionId` accept either flavour.
13
+ */
6
14
  export class FluidCadServer {
7
- viteManager = new ViteManager();
15
+ host;
8
16
  sceneManager;
17
+ // Per-session render output, scene cache, and param overrides. Desktop's
18
+ // sessionId is the normalized filePath; hub mode's sessionId is the WS
19
+ // connection UUID. Maps must be cleared via `destroySession` on hub-side
20
+ // disconnect to avoid leaks.
9
21
  previousScenes = new Map();
10
22
  renderingCache = new Map();
23
+ // Records the last successful render per session as `{ paramsHash, data }`.
24
+ // Any subsequent render request short-circuits when the new params hash to
25
+ // the same value — avoids redundant OCC work when desktop producers see the
26
+ // same code+params, or hub clients re-emit the same param mutation.
27
+ lastRendered = new Map();
28
+ paramOverrides = new Map();
29
+ // What file each session is rendering. For desktop, sessionId === filePath
30
+ // (set lazily on first processFile call). For hub, set explicitly via
31
+ // createSession with the bundle's manifest entry.
32
+ sessionFiles = new Map();
33
+ // Serializes OCC calls across all sessions. OCC isn't thread-safe and we
34
+ // share one engine instance per host process; concurrent param edits from
35
+ // multiple hub clients have to queue. Promise-chain pattern: each render
36
+ // awaits the previous one's settlement before starting.
37
+ renderMutex = Promise.resolve();
11
38
  currentFileName = '';
12
39
  currentFilePath = '';
40
+ lastRollbackStop = -1;
41
+ compileError = null;
42
+ constructor(host = new LocalSceneHost()) {
43
+ this.host = host;
44
+ }
45
+ getCurrentCode() {
46
+ if (!this.currentFileName)
47
+ return null;
48
+ return this.host.getBuffer(this.currentFileName);
49
+ }
13
50
  async init(workspacePath) {
14
- await this.viteManager.init(workspacePath);
51
+ await this.host.init(workspacePath);
15
52
  const initFilePath = normalizePath(join(workspacePath, 'init.js'));
16
53
  if (existsSync(initFilePath)) {
17
- const { default: _sceneManager } = await this.viteManager.loadModule(initFilePath);
54
+ const { default: _sceneManager } = await this.host.loadModule(initFilePath);
18
55
  this.sceneManager = await _sceneManager;
19
56
  }
20
57
  }
21
- async processFile(filePath, ignoreCache = false) {
22
- if (!this.sceneManager) {
23
- return null;
58
+ /**
59
+ * Capture an already-initialized SceneManager. Used by the hub-mode entry
60
+ * after running the packed bundle once to materialize the engine globals.
61
+ */
62
+ setSceneManager(manager) {
63
+ this.sceneManager = manager;
64
+ }
65
+ /**
66
+ * Run `fn` with exclusive access to the OCC engine. The mutex is process-
67
+ * wide: in hub mode concurrent client sessions land here too. Order is
68
+ * first-come, first-served via Promise chain.
69
+ */
70
+ async serialized(fn) {
71
+ const prev = this.renderMutex;
72
+ let release = () => { };
73
+ const next = new Promise((resolve) => { release = resolve; });
74
+ this.renderMutex = next;
75
+ try {
76
+ await prev;
77
+ return await fn();
24
78
  }
25
- filePath = normalizePath(filePath);
26
- const normalizedFileName = filePath.replace('virtual:live-render:', '');
27
- this.currentFileName = normalizedFileName;
28
- this.currentFilePath = filePath;
29
- if (!ignoreCache) {
30
- const fromCache = this.renderingCache.get(normalizedFileName);
31
- if (fromCache) {
32
- return {
33
- absPath: normalizedFileName,
34
- result: fromCache,
35
- rollbackStop: fromCache.length - 1,
36
- };
37
- }
79
+ finally {
80
+ release();
38
81
  }
39
- try {
40
- let scene = this.sceneManager.startScene();
41
- this.sceneManager.setCurrentFile(normalizedFileName);
42
- this.viteManager.invalidateModule();
43
- let breakpointHit = false;
44
- try {
45
- await this.viteManager.loadModule(filePath);
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Session lifecycle (hub mode)
85
+ // ---------------------------------------------------------------------------
86
+ createSession(sessionId, entryFilePath) {
87
+ this.sessionFiles.set(sessionId, normalizePath(entryFilePath));
88
+ }
89
+ destroySession(sessionId) {
90
+ this.previousScenes.delete(sessionId);
91
+ this.renderingCache.delete(sessionId);
92
+ this.lastRendered.delete(sessionId);
93
+ this.paramOverrides.delete(sessionId);
94
+ this.sessionFiles.delete(sessionId);
95
+ }
96
+ /**
97
+ * Re-render the session's entry, ignoring caches. Hub clients call this
98
+ * after editing a param. Returns the fresh render or null if no manager.
99
+ */
100
+ async recomputeForSession(sessionId) {
101
+ const filePath = this.sessionFiles.get(sessionId);
102
+ if (!filePath)
103
+ return null;
104
+ this.renderingCache.delete(sessionId);
105
+ this.lastRendered.delete(sessionId);
106
+ return this.processFileInternal(sessionId, filePath, true);
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // Render — internal core used by both desktop and hub entry points
110
+ // ---------------------------------------------------------------------------
111
+ async processFileInternal(sessionId, filePath, ignoreCache) {
112
+ return this.serialized(async () => {
113
+ if (!this.sceneManager) {
114
+ return null;
46
115
  }
47
- catch (e) {
48
- if (e instanceof BreakpointHit) {
49
- breakpointHit = true;
50
- }
51
- else {
52
- throw e;
116
+ const normalizedFileName = filePath.replace('virtual:live-render:', '');
117
+ this.currentFileName = normalizedFileName;
118
+ this.currentFilePath = filePath;
119
+ if (!ignoreCache) {
120
+ const fromCache = this.renderingCache.get(sessionId);
121
+ if (fromCache) {
122
+ this.lastRollbackStop = fromCache.length - 1;
123
+ this.compileError = null;
124
+ return {
125
+ absPath: normalizedFileName,
126
+ result: fromCache,
127
+ rollbackStop: fromCache.length - 1,
128
+ };
53
129
  }
54
130
  }
55
- if (this.previousScenes.has(normalizedFileName)) {
56
- const previousScene = this.previousScenes.get(normalizedFileName);
57
- scene = this.sceneManager.compare(previousScene, scene);
58
- }
59
- this.previousScenes.set(normalizedFileName, scene);
60
- this.sceneManager.renderScene(scene);
61
- const result = scene.getRenderedObjects();
62
- for (const obj of result) {
63
- if (obj.sourceLocation) {
64
- obj.sourceLocation.filePath = obj.sourceLocation.filePath.replace('virtual:live-render:', '');
131
+ try {
132
+ let scene = this.sceneManager.startScene();
133
+ this.sceneManager.setCurrentFile(normalizedFileName);
134
+ this.host.invalidateModule();
135
+ const registry = createParamRegistry();
136
+ const overrides = this.paramOverrides.get(sessionId);
137
+ if (overrides) {
138
+ registry.setOverrides(overrides);
139
+ }
140
+ let breakpointHit = false;
141
+ try {
142
+ await this.host.loadModule(filePath);
143
+ }
144
+ catch (e) {
145
+ if (e instanceof BreakpointHit) {
146
+ breakpointHit = true;
147
+ }
148
+ else {
149
+ throw e;
150
+ }
151
+ }
152
+ const params = getParamRegistry().getDefinitions();
153
+ if (this.previousScenes.has(sessionId)) {
154
+ const previousScene = this.previousScenes.get(sessionId);
155
+ scene = this.sceneManager.compare(previousScene, scene);
156
+ }
157
+ this.previousScenes.set(sessionId, scene);
158
+ this.sceneManager.renderScene(scene);
159
+ const result = scene.getRenderedObjects();
160
+ for (const obj of result) {
161
+ if (obj.sourceLocation) {
162
+ obj.sourceLocation.filePath = obj.sourceLocation.filePath.replace('virtual:live-render:', '');
163
+ }
65
164
  }
165
+ if (!filePath.startsWith('virtual:live-render')) {
166
+ this.renderingCache.set(sessionId, result);
167
+ }
168
+ this.lastRollbackStop = result.length - 1;
169
+ this.compileError = null;
170
+ return {
171
+ absPath: normalizedFileName,
172
+ result,
173
+ rollbackStop: result.length - 1,
174
+ breakpointHit,
175
+ params,
176
+ };
66
177
  }
67
- if (!filePath.startsWith('virtual:live-render')) {
68
- this.renderingCache.set(normalizedFileName, result);
178
+ catch (error) {
179
+ this.host.invalidateModule();
180
+ console.log('Error processing file:', error);
181
+ throw error;
69
182
  }
70
- return {
71
- absPath: normalizedFileName,
72
- result,
73
- rollbackStop: result.length - 1,
74
- breakpointHit,
75
- };
76
- }
77
- catch (error) {
78
- this.viteManager.invalidateModule();
79
- console.log('Error processing file:', error);
80
- throw error;
81
- }
183
+ });
184
+ }
185
+ // ---------------------------------------------------------------------------
186
+ // Desktop API — sessionId is implicit (filePath)
187
+ // ---------------------------------------------------------------------------
188
+ async processFile(filePath, ignoreCache = false) {
189
+ filePath = normalizePath(filePath);
190
+ const sessionId = filePath.replace('virtual:live-render:', '');
191
+ this.sessionFiles.set(sessionId, sessionId);
192
+ return this.processFileInternal(sessionId, filePath, ignoreCache);
82
193
  }
83
194
  async updateLiveCode(fileName, code) {
84
195
  fileName = normalizePath(fileName);
196
+ // Dedup against the last successful render. Multiple producers (editor
197
+ // live-update, save-triggered process-file, watcher, MCP /api/render)
198
+ // commonly hand us identical content; without this short-circuit each
199
+ // would trigger a redundant OCC pass. paramsHash mixes code content with
200
+ // current param overrides so a param change invalidates the cache.
201
+ const paramsHash = this.computeParamsHash(fileName, code);
202
+ const cached = this.lastRendered.get(fileName);
203
+ if (cached && cached.paramsHash === paramsHash) {
204
+ this.compileError = null;
205
+ this.currentFileName = fileName;
206
+ this.currentFilePath = `virtual:live-render:${fileName}`;
207
+ this.lastRollbackStop = cached.data.rollbackStop;
208
+ return cached.data;
209
+ }
85
210
  const id = `virtual:live-render:${fileName}`;
86
- this.viteManager.setBuffer(id, code);
211
+ this.host.setBuffer(id, code);
87
212
  this.renderingCache.delete(fileName);
88
- return this.processFile(id, true);
213
+ this.sessionFiles.set(fileName, fileName);
214
+ const result = await this.processFileInternal(fileName, id, true);
215
+ if (result) {
216
+ this.lastRendered.set(fileName, { paramsHash, data: result });
217
+ }
218
+ return result;
89
219
  }
90
220
  async rollbackFromUI(index) {
91
221
  return this.rollback(this.currentFileName, index);
@@ -94,9 +224,10 @@ export class FluidCadServer {
94
224
  if (!this.currentFilePath) {
95
225
  return null;
96
226
  }
97
- this.previousScenes.delete(this.currentFileName);
98
- this.renderingCache.delete(this.currentFileName);
99
- return this.processFile(this.currentFilePath, true);
227
+ const sessionId = this.currentFileName;
228
+ this.renderingCache.delete(sessionId);
229
+ this.lastRendered.delete(sessionId);
230
+ return this.processFileInternal(sessionId, this.currentFilePath, true);
100
231
  }
101
232
  async rollback(fileName, index) {
102
233
  if (!this.sceneManager) {
@@ -110,6 +241,7 @@ export class FluidCadServer {
110
241
  const rollbackIndex = index >= totalObjects - 1 ? totalObjects - 1 : index;
111
242
  this.sceneManager.rollbackScene(scene, rollbackIndex);
112
243
  const result = scene.getRenderedObjects();
244
+ this.lastRollbackStop = index;
113
245
  return {
114
246
  absPath: fileName,
115
247
  result,
@@ -173,4 +305,160 @@ export class FluidCadServer {
173
305
  }
174
306
  return this.sceneManager.hitTest(scene, shapeId, rayOrigin, rayDir, edgeThreshold);
175
307
  }
308
+ hitTestForSession(sessionId, shapeId, rayOrigin, rayDir, edgeThreshold) {
309
+ if (!this.sceneManager) {
310
+ return null;
311
+ }
312
+ const scene = this.previousScenes.get(sessionId);
313
+ if (!scene) {
314
+ return null;
315
+ }
316
+ return this.sceneManager.hitTest(scene, shapeId, rayOrigin, rayDir, edgeThreshold);
317
+ }
318
+ setCompileError(err) {
319
+ this.compileError = err;
320
+ }
321
+ getCompileError() {
322
+ return this.compileError;
323
+ }
324
+ setParam(sessionId, label, value) {
325
+ sessionId = normalizePath(sessionId);
326
+ if (!this.paramOverrides.has(sessionId)) {
327
+ this.paramOverrides.set(sessionId, new Map());
328
+ }
329
+ this.paramOverrides.get(sessionId).set(label, value);
330
+ this.lastRendered.delete(sessionId);
331
+ }
332
+ resetParams(sessionId) {
333
+ sessionId = normalizePath(sessionId);
334
+ this.paramOverrides.delete(sessionId);
335
+ this.lastRendered.delete(sessionId);
336
+ }
337
+ getParamOverrides(sessionId) {
338
+ const map = this.paramOverrides.get(normalizePath(sessionId));
339
+ if (!map)
340
+ return {};
341
+ return Object.fromEntries(map);
342
+ }
343
+ getCurrentFileName() {
344
+ return this.currentFileName;
345
+ }
346
+ /**
347
+ * Test-only seam: stage a scene under the given file name so the inspection
348
+ * accessors can read it without running the vite pipeline. Production code
349
+ * never calls this — `processFile` populates the same map.
350
+ */
351
+ _setSceneForTesting(fileName, scene, rollbackStop = -1) {
352
+ this.currentFileName = fileName;
353
+ this.previousScenes.set(fileName, scene);
354
+ this.lastRollbackStop = rollbackStop;
355
+ }
356
+ getSceneSummary() {
357
+ if (!this.currentFileName) {
358
+ return null;
359
+ }
360
+ const scene = this.previousScenes.get(this.currentFileName);
361
+ if (!scene) {
362
+ return null;
363
+ }
364
+ const rendered = scene.getRenderedObjects();
365
+ const objects = rendered.map((r, index) => ({
366
+ index,
367
+ id: r.id,
368
+ kind: r.type,
369
+ uniqueKind: r.uniqueType,
370
+ name: r.name,
371
+ params: sanitizeParams(r.object),
372
+ sourceLocation: r.sourceLocation,
373
+ shapeIds: (r.sceneShapes ?? []).map((s) => s.shapeId),
374
+ fromCache: !!r.fromCache,
375
+ hasError: !!r.hasError,
376
+ errorMessage: r.errorMessage,
377
+ containerId: r.parentId ?? null,
378
+ isContainer: !!r.isContainer,
379
+ visible: r.visible !== false,
380
+ }));
381
+ return {
382
+ schemaVersion: 1,
383
+ file: this.currentFileName,
384
+ objects,
385
+ rollbackStop: this.lastRollbackStop,
386
+ compileError: this.compileError,
387
+ };
388
+ }
389
+ getShapesList() {
390
+ if (!this.currentFileName) {
391
+ return null;
392
+ }
393
+ const scene = this.previousScenes.get(this.currentFileName);
394
+ if (!scene) {
395
+ return null;
396
+ }
397
+ const rendered = scene.getRenderedObjects();
398
+ const shapes = [];
399
+ for (const r of rendered) {
400
+ const sceneShapes = (r.sceneShapes ?? []);
401
+ for (const s of sceneShapes) {
402
+ shapes.push({
403
+ shapeId: s.shapeId,
404
+ type: s.shapeType,
405
+ sceneObjectId: r.id,
406
+ });
407
+ }
408
+ }
409
+ return { shapes };
410
+ }
411
+ /**
412
+ * Compose a stable cache key over the rendering inputs: the source bytes
413
+ * being rendered plus the param overrides currently in effect for the
414
+ * session. Param changes flip the hash so cached entries don't shadow a
415
+ * recompute, even when the code text is byte-identical.
416
+ */
417
+ computeParamsHash(sessionId, codeOrBundle) {
418
+ const overrides = this.paramOverrides.get(sessionId);
419
+ const sortedEntries = overrides ? [...overrides.entries()].sort(([a], [b]) => a.localeCompare(b)) : [];
420
+ const normalized = codeOrBundle.replace(/\r\n/g, '\n');
421
+ return createHash('sha1')
422
+ .update(normalized)
423
+ .update('\0')
424
+ .update(JSON.stringify(sortedEntries))
425
+ .digest('hex');
426
+ }
427
+ }
428
+ const MAX_PARAM_DEPTH = 6;
429
+ function sanitizeParams(value, depth = 0) {
430
+ if (value === null || value === undefined) {
431
+ return value ?? null;
432
+ }
433
+ if (typeof value === 'number') {
434
+ return Number.isFinite(value) ? value : null;
435
+ }
436
+ if (typeof value === 'string' || typeof value === 'boolean') {
437
+ return value;
438
+ }
439
+ if (depth >= MAX_PARAM_DEPTH) {
440
+ return null;
441
+ }
442
+ if (Array.isArray(value)) {
443
+ return value.map((v) => sanitizeParams(v, depth + 1));
444
+ }
445
+ if (typeof value === 'object') {
446
+ // A scene-object reference. Render as { ref: id } so the agent can chase
447
+ // it through other tools without us shipping the whole subtree.
448
+ const maybeId = value.id;
449
+ const isSceneObjectRef = typeof maybeId === 'string' &&
450
+ typeof value.getType === 'function';
451
+ if (isSceneObjectRef) {
452
+ return { ref: maybeId };
453
+ }
454
+ const out = {};
455
+ for (const [k, v] of Object.entries(value)) {
456
+ if (typeof v === 'function') {
457
+ continue;
458
+ }
459
+ out[k] = sanitizeParams(v, depth + 1);
460
+ }
461
+ return out;
462
+ }
463
+ return null;
176
464
  }
@@ -0,0 +1,30 @@
1
+ export declare const REGISTRY_DIR_NAME = ".fluidcad";
2
+ export declare const REGISTRY_FILE_NAME = "instances.json";
3
+ export type RegistryEntry = {
4
+ workspacePath: string;
5
+ port: number;
6
+ pid: number;
7
+ version: string;
8
+ startedAt: string;
9
+ };
10
+ export declare function registryFilePath(): string;
11
+ /**
12
+ * Add or replace an entry keyed by `workspacePath`. A new entry for an
13
+ * existing workspace (second window) supersedes the prior one.
14
+ */
15
+ export declare function addInstance(entry: RegistryEntry): void;
16
+ /**
17
+ * Remove the entry that matches both workspacePath and pid. Matching on pid
18
+ * prevents a clean shutdown from clobbering a freshly-restarted instance.
19
+ */
20
+ export declare function removeInstance(workspacePath: string, pid: number): void;
21
+ /**
22
+ * Read the registry, prune entries whose PIDs are no longer alive, and
23
+ * persist the pruned result. Returns the live entries.
24
+ *
25
+ * Liveness is `process.kill(pid, 0)` — sending signal 0 doesn't deliver
26
+ * anything but throws ESRCH if the process is gone. EPERM (running but owned
27
+ * by another user) counts as alive.
28
+ */
29
+ export declare function readInstances(): RegistryEntry[];
30
+ export declare function isPidAlive(pid: number): boolean;
@@ -0,0 +1,126 @@
1
+ // Global instance registry at ~/.fluidcad/instances.json — lets a single MCP
2
+ // process enumerate every running FluidCAD workspace on this machine.
3
+ //
4
+ // Read-modify-write is not crash-safe across multiple writers (we have no
5
+ // file lock), but the worst case is a dropped registry entry that the next
6
+ // successful write or stale-prune restores. Atomicity for the file itself is
7
+ // via tmp + rename.
8
+ import fs from 'fs';
9
+ import os from 'os';
10
+ import path from 'path';
11
+ export const REGISTRY_DIR_NAME = '.fluidcad';
12
+ export const REGISTRY_FILE_NAME = 'instances.json';
13
+ const EMPTY = { schemaVersion: 1, instances: [] };
14
+ function registryDir() {
15
+ return path.join(os.homedir(), REGISTRY_DIR_NAME);
16
+ }
17
+ export function registryFilePath() {
18
+ return path.join(registryDir(), REGISTRY_FILE_NAME);
19
+ }
20
+ function readRaw() {
21
+ try {
22
+ const raw = fs.readFileSync(registryFilePath(), 'utf8');
23
+ const parsed = JSON.parse(raw);
24
+ if (isRegistryFile(parsed)) {
25
+ return parsed;
26
+ }
27
+ return EMPTY;
28
+ }
29
+ catch {
30
+ return EMPTY;
31
+ }
32
+ }
33
+ function writeRaw(file) {
34
+ const dir = registryDir();
35
+ fs.mkdirSync(dir, { recursive: true });
36
+ const destination = registryFilePath();
37
+ const tmp = path.join(dir, `${REGISTRY_FILE_NAME}.${process.pid}.tmp`);
38
+ const payload = JSON.stringify(file, null, 2) + '\n';
39
+ fs.writeFileSync(tmp, payload, { encoding: 'utf8', mode: 0o644 });
40
+ fs.renameSync(tmp, destination);
41
+ }
42
+ /**
43
+ * Add or replace an entry keyed by `workspacePath`. A new entry for an
44
+ * existing workspace (second window) supersedes the prior one.
45
+ */
46
+ export function addInstance(entry) {
47
+ const file = readRaw();
48
+ const filtered = file.instances.filter((e) => e.workspacePath !== entry.workspacePath);
49
+ filtered.push(entry);
50
+ writeRaw({ schemaVersion: 1, instances: filtered });
51
+ }
52
+ /**
53
+ * Remove the entry that matches both workspacePath and pid. Matching on pid
54
+ * prevents a clean shutdown from clobbering a freshly-restarted instance.
55
+ */
56
+ export function removeInstance(workspacePath, pid) {
57
+ const file = readRaw();
58
+ const filtered = file.instances.filter((e) => !(e.workspacePath === workspacePath && e.pid === pid));
59
+ if (filtered.length === file.instances.length) {
60
+ return;
61
+ }
62
+ writeRaw({ schemaVersion: 1, instances: filtered });
63
+ }
64
+ /**
65
+ * Read the registry, prune entries whose PIDs are no longer alive, and
66
+ * persist the pruned result. Returns the live entries.
67
+ *
68
+ * Liveness is `process.kill(pid, 0)` — sending signal 0 doesn't deliver
69
+ * anything but throws ESRCH if the process is gone. EPERM (running but owned
70
+ * by another user) counts as alive.
71
+ */
72
+ export function readInstances() {
73
+ const file = readRaw();
74
+ const alive = [];
75
+ const dead = [];
76
+ for (const entry of file.instances) {
77
+ if (isPidAlive(entry.pid)) {
78
+ alive.push(entry);
79
+ }
80
+ else {
81
+ dead.push(entry);
82
+ }
83
+ }
84
+ if (dead.length > 0) {
85
+ try {
86
+ writeRaw({ schemaVersion: 1, instances: alive });
87
+ }
88
+ catch {
89
+ // Pruning is best-effort; readers still get the live set.
90
+ }
91
+ }
92
+ return alive;
93
+ }
94
+ export function isPidAlive(pid) {
95
+ try {
96
+ process.kill(pid, 0);
97
+ return true;
98
+ }
99
+ catch (err) {
100
+ if (err && err.code === 'EPERM') {
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+ }
106
+ function isRegistryFile(value) {
107
+ if (typeof value !== 'object' || value === null) {
108
+ return false;
109
+ }
110
+ const v = value;
111
+ if (v.schemaVersion !== 1 || !Array.isArray(v.instances)) {
112
+ return false;
113
+ }
114
+ return v.instances.every(isRegistryEntry);
115
+ }
116
+ function isRegistryEntry(value) {
117
+ if (typeof value !== 'object' || value === null) {
118
+ return false;
119
+ }
120
+ const v = value;
121
+ return (typeof v.workspacePath === 'string' &&
122
+ typeof v.port === 'number' &&
123
+ typeof v.pid === 'number' &&
124
+ typeof v.version === 'string' &&
125
+ typeof v.startedAt === 'string');
126
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Node built-in modules that user `.fluid.js` code must not import. Same set
3
+ * enforced by LocalSceneHost (at SSR transform time) and the model packer
4
+ * (at bundle time) so a packed model can't smuggle in capabilities a live
5
+ * workspace would have rejected.
6
+ */
7
+ export declare const BLOCKED_NODE_MODULES: Set<string>;
8
+ export declare function getBlockedNodeModule(id: string): string | null;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Node built-in modules that user `.fluid.js` code must not import. Same set
3
+ * enforced by LocalSceneHost (at SSR transform time) and the model packer
4
+ * (at bundle time) so a packed model can't smuggle in capabilities a live
5
+ * workspace would have rejected.
6
+ */
7
+ export const BLOCKED_NODE_MODULES = new Set([
8
+ 'fs',
9
+ 'child_process',
10
+ 'net',
11
+ 'dgram',
12
+ 'tls',
13
+ 'http',
14
+ 'https',
15
+ 'http2',
16
+ 'os',
17
+ 'worker_threads',
18
+ 'vm',
19
+ 'cluster',
20
+ 'dns',
21
+ 'module',
22
+ ]);
23
+ export function getBlockedNodeModule(id) {
24
+ let name = id;
25
+ if (name.startsWith('node:')) {
26
+ name = name.slice(5);
27
+ }
28
+ const baseName = name.split('/')[0];
29
+ return BLOCKED_NODE_MODULES.has(baseName) ? baseName : null;
30
+ }
@@ -1,10 +1,12 @@
1
1
  import { type ViteDevServer } from 'vite';
2
- export declare class ViteManager {
2
+ import type { SceneHost } from './scene-host.ts';
3
+ export declare class LocalSceneHost implements SceneHost {
3
4
  server: ViteDevServer;
4
5
  private rootPath;
5
6
  private buffers;
6
7
  init(rootPath: string): Promise<void>;
7
8
  setBuffer(id: string, code: string): void;
9
+ getBuffer(fileName: string): string | null;
8
10
  loadModule(filePath: string): Promise<Record<string, any>>;
9
11
  invalidateModule(): void;
10
12
  }