@pascal-app/core 0.9.2 → 1.0.0-beta.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 (339) hide show
  1. package/README.md +17 -10
  2. package/dist/events/bus.d.ts +21 -2
  3. package/dist/events/bus.d.ts.map +1 -1
  4. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
  5. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  6. package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +116 -66
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  9. package/dist/hooks/spatial-grid/spatial-grid-manager.js +316 -441
  10. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
  11. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  12. package/dist/hooks/spatial-grid/spatial-grid-sync.js +193 -12
  13. package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
  14. package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
  15. package/dist/hooks/spatial-grid/support-host-id.js +2 -0
  16. package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
  17. package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
  18. package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
  19. package/dist/index.d.ts +23 -11
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +22 -10
  22. package/dist/lib/measurement-geometry.d.ts +4 -0
  23. package/dist/lib/measurement-geometry.d.ts.map +1 -1
  24. package/dist/lib/measurement-geometry.js +27 -10
  25. package/dist/lib/slab-polygon.d.ts.map +1 -1
  26. package/dist/lib/slab-polygon.js +33 -1
  27. package/dist/lib/space-detection.d.ts +15 -4
  28. package/dist/lib/space-detection.d.ts.map +1 -1
  29. package/dist/lib/space-detection.js +174 -88
  30. package/dist/lib/terrain-brush.d.ts +163 -0
  31. package/dist/lib/terrain-brush.d.ts.map +1 -0
  32. package/dist/lib/terrain-brush.js +359 -0
  33. package/dist/lib/terrain-codec.d.ts +51 -0
  34. package/dist/lib/terrain-codec.d.ts.map +1 -0
  35. package/dist/lib/terrain-codec.js +167 -0
  36. package/dist/lib/terrain-field.d.ts +150 -0
  37. package/dist/lib/terrain-field.d.ts.map +1 -0
  38. package/dist/lib/terrain-field.js +248 -0
  39. package/dist/lib/terrain-raycast.d.ts +44 -0
  40. package/dist/lib/terrain-raycast.d.ts.map +1 -0
  41. package/dist/lib/terrain-raycast.js +188 -0
  42. package/dist/lib/terrain-source.d.ts +60 -0
  43. package/dist/lib/terrain-source.d.ts.map +1 -0
  44. package/dist/lib/terrain-source.js +90 -0
  45. package/dist/lib/terrain-support.d.ts +60 -0
  46. package/dist/lib/terrain-support.d.ts.map +1 -0
  47. package/dist/lib/terrain-support.js +150 -0
  48. package/dist/lib/zone-quantities.d.ts +1 -1
  49. package/dist/lib/zone-quantities.d.ts.map +1 -1
  50. package/dist/lib/zone-quantities.js +311 -66
  51. package/dist/material-library.d.ts +9 -1
  52. package/dist/material-library.d.ts.map +1 -1
  53. package/dist/material-library.js +56 -4
  54. package/dist/registry/handles.d.ts +16 -1
  55. package/dist/registry/handles.d.ts.map +1 -1
  56. package/dist/registry/index.d.ts +1 -1
  57. package/dist/registry/index.d.ts.map +1 -1
  58. package/dist/registry/subtree.d.ts.map +1 -1
  59. package/dist/registry/subtree.js +5 -2
  60. package/dist/registry/types.d.ts +64 -1
  61. package/dist/registry/types.d.ts.map +1 -1
  62. package/dist/schema/index.d.ts +6 -3
  63. package/dist/schema/index.d.ts.map +1 -1
  64. package/dist/schema/index.js +6 -3
  65. package/dist/schema/nodes/cabinet.d.ts +2 -0
  66. package/dist/schema/nodes/cabinet.d.ts.map +1 -1
  67. package/dist/schema/nodes/cabinet.js +4 -2
  68. package/dist/schema/nodes/ceiling.d.ts +1 -1
  69. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  70. package/dist/schema/nodes/ceiling.js +7 -1
  71. package/dist/schema/nodes/column.d.ts +1 -0
  72. package/dist/schema/nodes/column.d.ts.map +1 -1
  73. package/dist/schema/nodes/column.js +2 -0
  74. package/dist/schema/nodes/construction-dimension.d.ts +195 -0
  75. package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
  76. package/dist/schema/nodes/construction-dimension.js +151 -0
  77. package/dist/schema/nodes/door.d.ts +29 -0
  78. package/dist/schema/nodes/door.d.ts.map +1 -1
  79. package/dist/schema/nodes/door.js +20 -0
  80. package/dist/schema/nodes/duct-terminal.d.ts +1 -0
  81. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -1
  82. package/dist/schema/nodes/duct-terminal.js +2 -0
  83. package/dist/schema/nodes/fence.d.ts +2 -0
  84. package/dist/schema/nodes/fence.d.ts.map +1 -1
  85. package/dist/schema/nodes/fence.js +8 -0
  86. package/dist/schema/nodes/hvac-equipment.d.ts +1 -0
  87. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -1
  88. package/dist/schema/nodes/hvac-equipment.js +2 -0
  89. package/dist/schema/nodes/item.d.ts +1 -0
  90. package/dist/schema/nodes/item.d.ts.map +1 -1
  91. package/dist/schema/nodes/item.js +13 -0
  92. package/dist/schema/nodes/level.d.ts +2 -1
  93. package/dist/schema/nodes/level.d.ts.map +1 -1
  94. package/dist/schema/nodes/level.js +12 -33
  95. package/dist/schema/nodes/shelf.d.ts +1 -0
  96. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  97. package/dist/schema/nodes/shelf.js +2 -0
  98. package/dist/schema/nodes/site.d.ts +9 -0
  99. package/dist/schema/nodes/site.d.ts.map +1 -1
  100. package/dist/schema/nodes/site.js +8 -6
  101. package/dist/schema/nodes/slab.d.ts +5 -0
  102. package/dist/schema/nodes/slab.d.ts.map +1 -1
  103. package/dist/schema/nodes/slab.js +14 -2
  104. package/dist/schema/nodes/spawn.d.ts +1 -0
  105. package/dist/schema/nodes/spawn.d.ts.map +1 -1
  106. package/dist/schema/nodes/spawn.js +2 -0
  107. package/dist/schema/nodes/stair.d.ts +3 -1
  108. package/dist/schema/nodes/stair.d.ts.map +1 -1
  109. package/dist/schema/nodes/stair.js +8 -1
  110. package/dist/schema/nodes/structural-grid.d.ts +27 -0
  111. package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
  112. package/dist/schema/nodes/structural-grid.js +17 -0
  113. package/dist/schema/nodes/wall.d.ts +5 -2
  114. package/dist/schema/nodes/wall.d.ts.map +1 -1
  115. package/dist/schema/nodes/wall.js +13 -4
  116. package/dist/schema/nodes/window.d.ts +29 -0
  117. package/dist/schema/nodes/window.d.ts.map +1 -1
  118. package/dist/schema/nodes/window.js +18 -0
  119. package/dist/schema/nodes/zone.d.ts +20 -0
  120. package/dist/schema/nodes/zone.d.ts.map +1 -1
  121. package/dist/schema/nodes/zone.js +15 -0
  122. package/dist/schema/terrain.d.ts +28 -0
  123. package/dist/schema/terrain.d.ts.map +1 -0
  124. package/dist/schema/terrain.js +30 -0
  125. package/dist/schema/types.d.ts +211 -3
  126. package/dist/schema/types.d.ts.map +1 -1
  127. package/dist/schema/types.js +4 -0
  128. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
  129. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
  130. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
  131. package/dist/services/index.d.ts +2 -1
  132. package/dist/services/index.d.ts.map +1 -1
  133. package/dist/services/index.js +2 -1
  134. package/dist/services/level-height.d.ts +8 -9
  135. package/dist/services/level-height.d.ts.map +1 -1
  136. package/dist/services/level-height.js +48 -16
  137. package/dist/services/storey.d.ts +103 -0
  138. package/dist/services/storey.d.ts.map +1 -0
  139. package/dist/services/storey.js +296 -0
  140. package/dist/store/actions/node-actions.d.ts.map +1 -1
  141. package/dist/store/actions/node-actions.js +47 -3
  142. package/dist/store/history-control.d.ts +20 -0
  143. package/dist/store/history-control.d.ts.map +1 -1
  144. package/dist/store/history-control.js +110 -13
  145. package/dist/store/use-live-terrain.d.ts +65 -0
  146. package/dist/store/use-live-terrain.d.ts.map +1 -0
  147. package/dist/store/use-live-terrain.js +31 -0
  148. package/dist/store/use-live-transforms.d.ts +8 -0
  149. package/dist/store/use-live-transforms.d.ts.map +1 -1
  150. package/dist/store/use-scene.d.ts +31 -2
  151. package/dist/store/use-scene.d.ts.map +1 -1
  152. package/dist/store/use-scene.js +638 -8
  153. package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
  154. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
  155. package/dist/systems/elevator/elevator-opening-system.js +27 -21
  156. package/dist/systems/elevator/elevator-service.d.ts +0 -2
  157. package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
  158. package/dist/systems/elevator/elevator-service.js +2 -24
  159. package/dist/systems/slab/slab-placement.d.ts +11 -0
  160. package/dist/systems/slab/slab-placement.d.ts.map +1 -0
  161. package/dist/systems/slab/slab-placement.js +14 -0
  162. package/dist/systems/slab/slab-support.d.ts +137 -0
  163. package/dist/systems/slab/slab-support.d.ts.map +1 -0
  164. package/dist/systems/slab/slab-support.js +557 -0
  165. package/dist/systems/stair/stair-opening-preview.d.ts +1060 -935
  166. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  167. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  168. package/dist/systems/stair/stair-opening-sync.js +18 -15
  169. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
  170. package/dist/systems/stair/stair-opening-system.js +32 -4
  171. package/dist/systems/stair/stair-rise.d.ts +22 -0
  172. package/dist/systems/stair/stair-rise.d.ts.map +1 -0
  173. package/dist/systems/stair/stair-rise.js +79 -0
  174. package/dist/systems/wall/wall-curve.d.ts +10 -0
  175. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  176. package/dist/systems/wall/wall-curve.js +1 -1
  177. package/dist/systems/wall/wall-mitering.js +6 -6
  178. package/dist/systems/wall/wall-top.d.ts +40 -0
  179. package/dist/systems/wall/wall-top.d.ts.map +1 -0
  180. package/dist/systems/wall/wall-top.js +46 -0
  181. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  182. package/dist/utils/clone-scene-graph.js +34 -3
  183. package/dist/utils/heal-scene-graph.d.ts +1 -1
  184. package/dist/utils/heal-scene-graph.d.ts.map +1 -1
  185. package/dist/utils/heal-scene-graph.js +25 -9
  186. package/dist/validation/validate-build-json.d.ts +2 -0
  187. package/dist/validation/validate-build-json.d.ts.map +1 -1
  188. package/dist/validation/validate-build-json.js +59 -3
  189. package/package.json +2 -2
  190. package/dist/hooks/scene-registry/scene-registry.test.d.ts +0 -2
  191. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +0 -1
  192. package/dist/hooks/scene-registry/scene-registry.test.js +0 -26
  193. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
  194. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
  195. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -366
  196. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +0 -2
  197. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +0 -1
  198. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +0 -363
  199. package/dist/lib/measurement-geometry.test.d.ts +0 -2
  200. package/dist/lib/measurement-geometry.test.d.ts.map +0 -1
  201. package/dist/lib/measurement-geometry.test.js +0 -102
  202. package/dist/lib/slab-polygon.test.d.ts +0 -2
  203. package/dist/lib/slab-polygon.test.d.ts.map +0 -1
  204. package/dist/lib/slab-polygon.test.js +0 -635
  205. package/dist/lib/space-detection-pause.test.d.ts +0 -2
  206. package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
  207. package/dist/lib/space-detection-pause.test.js +0 -42
  208. package/dist/lib/space-detection.test.d.ts +0 -2
  209. package/dist/lib/space-detection.test.d.ts.map +0 -1
  210. package/dist/lib/space-detection.test.js +0 -376
  211. package/dist/lib/zone-quantities.test.d.ts +0 -2
  212. package/dist/lib/zone-quantities.test.d.ts.map +0 -1
  213. package/dist/lib/zone-quantities.test.js +0 -82
  214. package/dist/registry/registry.test.d.ts +0 -2
  215. package/dist/registry/registry.test.d.ts.map +0 -1
  216. package/dist/registry/registry.test.js +0 -196
  217. package/dist/registry/relations-resolver.test.d.ts +0 -2
  218. package/dist/registry/relations-resolver.test.d.ts.map +0 -1
  219. package/dist/registry/relations-resolver.test.js +0 -183
  220. package/dist/registry/scene-api.test.d.ts +0 -2
  221. package/dist/registry/scene-api.test.d.ts.map +0 -1
  222. package/dist/registry/scene-api.test.js +0 -183
  223. package/dist/registry/subtree.test.d.ts +0 -2
  224. package/dist/registry/subtree.test.d.ts.map +0 -1
  225. package/dist/registry/subtree.test.js +0 -163
  226. package/dist/schema/asset-url.test.d.ts +0 -2
  227. package/dist/schema/asset-url.test.d.ts.map +0 -1
  228. package/dist/schema/asset-url.test.js +0 -134
  229. package/dist/schema/material.test.d.ts +0 -2
  230. package/dist/schema/material.test.d.ts.map +0 -1
  231. package/dist/schema/material.test.js +0 -43
  232. package/dist/schema/nodes/measurement.test.d.ts +0 -2
  233. package/dist/schema/nodes/measurement.test.d.ts.map +0 -1
  234. package/dist/schema/nodes/measurement.test.js +0 -139
  235. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +0 -2
  236. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +0 -1
  237. package/dist/schema/nodes/ridge-vent-defaults.test.js +0 -205
  238. package/dist/schema/nodes/roof-segment-shape.test.d.ts +0 -2
  239. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +0 -1
  240. package/dist/schema/nodes/roof-segment-shape.test.js +0 -175
  241. package/dist/schema/nodes/roof-segment-surface.test.d.ts +0 -2
  242. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +0 -1
  243. package/dist/schema/nodes/roof-segment-surface.test.js +0 -48
  244. package/dist/schema/nodes/roof-segment-trim.test.d.ts +0 -2
  245. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +0 -1
  246. package/dist/schema/nodes/roof-segment-trim.test.js +0 -107
  247. package/dist/schema/nodes/roof-segment-walls.test.d.ts +0 -2
  248. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +0 -1
  249. package/dist/schema/nodes/roof-segment-walls.test.js +0 -92
  250. package/dist/schema/nodes/wall.test.d.ts +0 -2
  251. package/dist/schema/nodes/wall.test.d.ts.map +0 -1
  252. package/dist/schema/nodes/wall.test.js +0 -208
  253. package/dist/services/alignment-anchors.test.d.ts +0 -2
  254. package/dist/services/alignment-anchors.test.d.ts.map +0 -1
  255. package/dist/services/alignment-anchors.test.js +0 -383
  256. package/dist/services/alignment.test.d.ts +0 -2
  257. package/dist/services/alignment.test.d.ts.map +0 -1
  258. package/dist/services/alignment.test.js +0 -92
  259. package/dist/services/drag-session.test.d.ts +0 -2
  260. package/dist/services/drag-session.test.d.ts.map +0 -1
  261. package/dist/services/drag-session.test.js +0 -219
  262. package/dist/services/hosting.test.d.ts +0 -2
  263. package/dist/services/hosting.test.d.ts.map +0 -1
  264. package/dist/services/hosting.test.js +0 -230
  265. package/dist/services/movement.test.d.ts +0 -2
  266. package/dist/services/movement.test.d.ts.map +0 -1
  267. package/dist/services/movement.test.js +0 -133
  268. package/dist/services/opening-guides.test.d.ts +0 -2
  269. package/dist/services/opening-guides.test.d.ts.map +0 -1
  270. package/dist/services/opening-guides.test.js +0 -208
  271. package/dist/services/port-connectivity.test.d.ts +0 -2
  272. package/dist/services/port-connectivity.test.d.ts.map +0 -1
  273. package/dist/services/port-connectivity.test.js +0 -297
  274. package/dist/services/riser-diagram.test.d.ts +0 -2
  275. package/dist/services/riser-diagram.test.d.ts.map +0 -1
  276. package/dist/services/riser-diagram.test.js +0 -66
  277. package/dist/services/single-undo-dance.test.d.ts +0 -2
  278. package/dist/services/single-undo-dance.test.d.ts.map +0 -1
  279. package/dist/services/single-undo-dance.test.js +0 -210
  280. package/dist/services/snap.test.d.ts +0 -2
  281. package/dist/services/snap.test.d.ts.map +0 -1
  282. package/dist/services/snap.test.js +0 -137
  283. package/dist/services/system-graph.test.d.ts +0 -2
  284. package/dist/services/system-graph.test.d.ts.map +0 -1
  285. package/dist/services/system-graph.test.js +0 -138
  286. package/dist/services/validate-dwv.test.d.ts +0 -2
  287. package/dist/services/validate-dwv.test.d.ts.map +0 -1
  288. package/dist/services/validate-dwv.test.js +0 -109
  289. package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
  290. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
  291. package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
  292. package/dist/store/actions/reparent.test.d.ts +0 -2
  293. package/dist/store/actions/reparent.test.d.ts.map +0 -1
  294. package/dist/store/actions/reparent.test.js +0 -212
  295. package/dist/store/actions/ridge-vent-update.test.d.ts +0 -2
  296. package/dist/store/actions/ridge-vent-update.test.d.ts.map +0 -1
  297. package/dist/store/actions/ridge-vent-update.test.js +0 -264
  298. package/dist/store/use-scene-dirty-tracking.test.d.ts +0 -2
  299. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +0 -1
  300. package/dist/store/use-scene-dirty-tracking.test.js +0 -68
  301. package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
  302. package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
  303. package/dist/store/use-scene-elevator-migration.test.js +0 -109
  304. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +0 -2
  305. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +0 -1
  306. package/dist/store/use-scene-measurement-roundtrip.test.js +0 -97
  307. package/dist/store/use-scene-plugins.test.d.ts +0 -2
  308. package/dist/store/use-scene-plugins.test.d.ts.map +0 -1
  309. package/dist/store/use-scene-plugins.test.js +0 -51
  310. package/dist/store/use-scene-wall-slot-migration.test.d.ts +0 -2
  311. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +0 -1
  312. package/dist/store/use-scene-wall-slot-migration.test.js +0 -210
  313. package/dist/store/use-scene-window-migration.test.d.ts +0 -2
  314. package/dist/store/use-scene-window-migration.test.d.ts.map +0 -1
  315. package/dist/store/use-scene-window-migration.test.js +0 -85
  316. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
  317. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
  318. package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
  319. package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
  320. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
  321. package/dist/systems/elevator/elevator-runtime.test.js +0 -64
  322. package/dist/systems/fence/fence-spline.test.d.ts +0 -2
  323. package/dist/systems/fence/fence-spline.test.d.ts.map +0 -1
  324. package/dist/systems/fence/fence-spline.test.js +0 -136
  325. package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
  326. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
  327. package/dist/systems/stair/stair-opening-preview.test.js +0 -80
  328. package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
  329. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
  330. package/dist/systems/stair/stair-opening-sync.test.js +0 -485
  331. package/dist/systems/wall/wall-mitering.test.d.ts +0 -2
  332. package/dist/systems/wall/wall-mitering.test.d.ts.map +0 -1
  333. package/dist/systems/wall/wall-mitering.test.js +0 -63
  334. package/dist/utils/clone-scene-graph.test.d.ts +0 -2
  335. package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
  336. package/dist/utils/clone-scene-graph.test.js +0 -62
  337. package/dist/utils/heal-scene-graph.test.d.ts +0 -2
  338. package/dist/utils/heal-scene-graph.test.d.ts.map +0 -1
  339. package/dist/utils/heal-scene-graph.test.js +0 -88
@@ -1,210 +0,0 @@
1
- import { beforeEach, describe, expect, test } from 'bun:test';
2
- import { createSceneApi } from '../registry/scene-api';
3
- import useScene from '../store/use-scene';
4
- globalThis.requestAnimationFrame ??= ((cb) => {
5
- cb(0);
6
- return 0;
7
- });
8
- globalThis.cancelAnimationFrame ??=
9
- () => { };
10
- /**
11
- * Validates the "single-undo dance" pattern used by Stage D actions:
12
- *
13
- * action.commit:
14
- * scene.restoreAll() // revert via snapshot (paused → no zundo record)
15
- * scene.resumeHistory() // unpause zundo
16
- * scene.update(...) // re-apply final → zundo records one diff
17
- * return true
18
- *
19
- * After the dance, undo() should roll back ONLY the drag's commit —
20
- * never further back than that. The fence-bend regression that surfaced
21
- * after Phase 5 Stage D porting was reportedly losing the prior create
22
- * step on undo; this test pins the correct behavior.
23
- */
24
- const FENCE_ID = 'fence_test';
25
- function makeFence(curveOffset) {
26
- return {
27
- id: FENCE_ID,
28
- type: 'fence',
29
- parentId: null,
30
- object: 'node',
31
- visible: true,
32
- metadata: {},
33
- start: [0, 0],
34
- end: [3, 0],
35
- height: 1.8,
36
- thickness: 0.08,
37
- baseHeight: 0.22,
38
- postSpacing: 2,
39
- postSize: 0.1,
40
- topRailHeight: 0.04,
41
- groundClearance: 0,
42
- edgeInset: 0.015,
43
- baseStyle: 'grounded',
44
- showInfill: true,
45
- color: '#ffffff',
46
- style: 'slat',
47
- curveOffset,
48
- };
49
- }
50
- describe('Single-undo dance', () => {
51
- beforeEach(() => {
52
- useScene.setState({ nodes: {}, rootNodeIds: [] });
53
- useScene.temporal.getState().clear();
54
- });
55
- test('curve-style commit yields a single undo step', () => {
56
- // 1. Create the fence (recorded by zundo).
57
- useScene.getState().createNode(makeFence(0));
58
- const pastCountAfterCreate = useScene.temporal.getState().pastStates.length;
59
- // 2. Simulate the drag.
60
- const scene = createSceneApi(useScene);
61
- scene.pauseHistory();
62
- scene.update(FENCE_ID, { curveOffset: 0.2 });
63
- scene.update(FENCE_ID, { curveOffset: 0.5 });
64
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0.5);
65
- // 3. Commit dance.
66
- scene.restoreAll();
67
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0);
68
- scene.resumeHistory();
69
- scene.update(FENCE_ID, { curveOffset: 0.5 });
70
- const pastCountAfterDance = useScene.temporal.getState().pastStates.length;
71
- expect(pastCountAfterDance).toBe(pastCountAfterCreate + 1);
72
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0.5);
73
- // 4. Undo — should return fence to curveOffset 0, NOT delete it.
74
- useScene.temporal.getState().undo();
75
- const fenceAfterUndo = useScene.getState().nodes[FENCE_ID];
76
- expect(fenceAfterUndo).toBeDefined();
77
- expect(fenceAfterUndo.curveOffset).toBe(0);
78
- });
79
- test('StrictMode double-mount: dispose-then-restart preserves history', () => {
80
- useScene.getState().createNode(makeFence(0));
81
- const pastBeforeBend = useScene.temporal.getState().pastStates.length;
82
- // Simulate StrictMode: first mount → cleanup (dispose) → second mount → drag → commit.
83
- const scene = createSceneApi(useScene);
84
- // Mount 1.
85
- scene.pauseHistory();
86
- // Mount 1 cleanup (StrictMode): no apply happened yet. dispose-equivalent.
87
- scene.restoreAll(); // snapshot empty, no-op.
88
- scene.resumeHistory();
89
- // Mount 2.
90
- scene.pauseHistory();
91
- // Drag.
92
- scene.update(FENCE_ID, { curveOffset: 0.3 });
93
- scene.update(FENCE_ID, { curveOffset: 0.7 });
94
- // Commit dance.
95
- scene.restoreAll();
96
- scene.resumeHistory();
97
- scene.update(FENCE_ID, { curveOffset: 0.7 });
98
- const pastAfterBend = useScene.temporal.getState().pastStates.length;
99
- // Exactly one new entry: the pre-bend state. Not two (which would mean
100
- // StrictMode's first mount/cleanup polluted history).
101
- expect(pastAfterBend).toBe(pastBeforeBend + 1);
102
- useScene.temporal.getState().undo();
103
- expect(useScene.getState().nodes[FENCE_ID]).toBeDefined();
104
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0);
105
- });
106
- test('commit-returns-false (no change) does NOT consume the prior pastState', () => {
107
- // This is the suspected bend regression: when action.commit returns
108
- // false (draft.curveOffset === ctx.originalCurveOffset), session.commit
109
- // calls scene.restoreAll() but doesn't push to pastStates. Subsequent
110
- // undo pops the PRIOR action (e.g. fence creation), not the no-op
111
- // bend.
112
- useScene.getState().createNode(makeFence(0));
113
- const pastBeforeBend = useScene.temporal.getState().pastStates.length;
114
- const scene = createSceneApi(useScene);
115
- scene.pauseHistory();
116
- // Drag back to original — simulates no-op bend.
117
- scene.update(FENCE_ID, { curveOffset: 0.0 });
118
- // Cancel path (mimics session.commit → action.commit returns false → scene.restoreAll → terminate).
119
- scene.restoreAll();
120
- scene.resumeHistory();
121
- const pastAfterNoOp = useScene.temporal.getState().pastStates.length;
122
- expect(pastAfterNoOp).toBe(pastBeforeBend); // no entries added
123
- // Now undo — this should be a no-op (state unchanged), but pops the create.
124
- useScene.temporal.getState().undo();
125
- // ⚠️ Reproduces the bug — undo removes the fence:
126
- const fence = useScene.getState().nodes[FENCE_ID];
127
- if (fence === undefined) {
128
- // Bug reproduced. The "no-op bend" allowed Ctrl-Z to fall through
129
- // to the fence creation. Fix is in action.commit: don't return false
130
- // — push a no-op entry instead, or guard against the cancel path.
131
- expect(fence).toBeUndefined();
132
- }
133
- else {
134
- expect(fence.curveOffset).toBe(0);
135
- }
136
- });
137
- test('full session flow via createDragSession with real action.commit dance', async () => {
138
- // Reproduces the actual wrapper flow:
139
- // - createNode → session.start → moves → grid:click → session.commit.
140
- // The action.commit does the dance internally.
141
- const { createDragSession } = await import('./drag-session');
142
- useScene.getState().createNode(makeFence(0));
143
- const pastBeforeBend = useScene.temporal.getState().pastStates.length;
144
- const scene = createSceneApi(useScene);
145
- const action = {
146
- begin: () => ({ original: 0 }),
147
- preview: (_ctx, point) => ({ offset: point[0] }),
148
- apply: (draft, _ctx, s) => {
149
- s.update(FENCE_ID, { curveOffset: draft.offset });
150
- return [FENCE_ID];
151
- },
152
- commit: (draft, ctx, s) => {
153
- if (draft.offset === ctx.original)
154
- return false;
155
- s.restoreAll();
156
- s.resumeHistory();
157
- s.update(FENCE_ID, { curveOffset: draft.offset });
158
- return true;
159
- },
160
- cancel: () => { },
161
- };
162
- const session = createDragSession(action, scene);
163
- session.start({ point: [0, 0] });
164
- session.move([0.3, 0], { shift: false, alt: false, ctrl: false, meta: false });
165
- session.move([0.5, 0], { shift: false, alt: false, ctrl: false, meta: false });
166
- const okCommit = session.commit();
167
- expect(okCommit).toBe(true);
168
- const pastAfter = useScene.temporal.getState().pastStates.length;
169
- expect(pastAfter).toBe(pastBeforeBend + 1);
170
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0.5);
171
- useScene.temporal.getState().undo();
172
- const after = useScene.getState().nodes[FENCE_ID];
173
- expect(after).toBeDefined();
174
- expect(after.curveOffset).toBe(0);
175
- });
176
- test('REAL bend (draft != original): one Ctrl-Z undoes only the bend', () => {
177
- useScene.getState().createNode(makeFence(0));
178
- const stateAfterCreate = useScene.getState().nodes[FENCE_ID];
179
- expect(stateAfterCreate.curveOffset).toBe(0);
180
- const scene = createSceneApi(useScene);
181
- scene.pauseHistory();
182
- // Simulate a real drag: capture original, mutate to non-zero.
183
- scene.update(FENCE_ID, { curveOffset: 0.5 });
184
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0.5);
185
- // Dance.
186
- scene.restoreAll();
187
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0);
188
- scene.resumeHistory();
189
- scene.update(FENCE_ID, { curveOffset: 0.5 });
190
- expect(useScene.getState().nodes[FENCE_ID].curveOffset).toBe(0.5);
191
- // First Ctrl-Z should undo the bend.
192
- useScene.temporal.getState().undo();
193
- const afterFirstUndo = useScene.getState().nodes[FENCE_ID];
194
- expect(afterFirstUndo).toBeDefined();
195
- expect(afterFirstUndo?.curveOffset).toBe(0);
196
- });
197
- test('a SECOND undo rolls the create step back', () => {
198
- useScene.getState().createNode(makeFence(0));
199
- const scene = createSceneApi(useScene);
200
- scene.pauseHistory();
201
- scene.update(FENCE_ID, { curveOffset: 0.5 });
202
- scene.restoreAll();
203
- scene.resumeHistory();
204
- scene.update(FENCE_ID, { curveOffset: 0.5 });
205
- useScene.temporal.getState().undo();
206
- expect(useScene.getState().nodes[FENCE_ID]).toBeDefined();
207
- useScene.temporal.getState().undo();
208
- expect(useScene.getState().nodes[FENCE_ID]).toBeUndefined();
209
- });
210
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=snap.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"snap.test.d.ts","sourceRoot":"","sources":["../../src/services/snap.test.ts"],"names":[],"mappings":""}
@@ -1,137 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { DEFAULT_ANGLE_STEP, DEFAULT_GRID_STEP, snapAngleToList, snapPointAlongAngleRay, snapPointToAngle, snapPointToGrid, snapScalar, snapServices, snapVec3ToGrid, } from './snap';
3
- describe('snapScalar', () => {
4
- test('rounds to multiples of step', () => {
5
- expect(snapScalar(0.27, 0.25)).toBe(0.25);
6
- expect(snapScalar(0.13, 0.25)).toBe(0.25);
7
- expect(snapScalar(0.12, 0.25)).toBe(0);
8
- expect(snapScalar(1.7, 0.25)).toBeCloseTo(1.75);
9
- });
10
- test('returns input unchanged when step is non-positive', () => {
11
- expect(snapScalar(0.42, 0)).toBe(0.42);
12
- expect(snapScalar(0.42, -1)).toBe(0.42);
13
- });
14
- test('default step is 0.25m', () => {
15
- expect(snapScalar(0.3)).toBe(0.25);
16
- expect(snapScalar(0.4)).toBe(0.5);
17
- expect(DEFAULT_GRID_STEP).toBe(0.25);
18
- });
19
- });
20
- describe('snapPointToGrid', () => {
21
- test('snaps both components independently', () => {
22
- expect(snapPointToGrid([0.3, 0.6], 0.25)).toEqual([0.25, 0.5]);
23
- });
24
- test('preserves exact-grid points', () => {
25
- expect(snapPointToGrid([1, 2], 0.5)).toEqual([1, 2]);
26
- });
27
- });
28
- describe('snapVec3ToGrid', () => {
29
- test('snaps X and Z, leaves Y untouched', () => {
30
- expect(snapVec3ToGrid([0.3, 1.7, 0.6], 0.25)).toEqual([0.25, 1.7, 0.5]);
31
- });
32
- });
33
- describe('snapPointToAngle', () => {
34
- test('snaps to axis (0°) when cursor is near horizontal', () => {
35
- const from = [0, 0];
36
- const cursor = [1, 0.05]; // near 0°
37
- const snapped = snapPointToAngle(from, cursor, Math.PI / 4);
38
- expect(snapped[0]).toBeCloseTo(1, 1);
39
- expect(snapped[1]).toBeCloseTo(0, 5);
40
- });
41
- test('snaps to 45° at π/4 step', () => {
42
- const from = [0, 0];
43
- const cursor = [1, 0.9]; // near 45°
44
- const snapped = snapPointToAngle(from, cursor, Math.PI / 4);
45
- // distance preserved (≈ √(1² + 0.9²) ≈ 1.345), angle locked to 45°
46
- const expectedDist = Math.hypot(1, 0.9);
47
- expect(snapped[0]).toBeCloseTo(expectedDist * Math.cos(Math.PI / 4));
48
- expect(snapped[1]).toBeCloseTo(expectedDist * Math.sin(Math.PI / 4));
49
- });
50
- test('default angle step is π/12 (15°)', () => {
51
- expect(DEFAULT_ANGLE_STEP).toBeCloseTo(Math.PI / 12);
52
- });
53
- test('grid-snaps the projected point when gridStep is provided', () => {
54
- const from = [0, 0];
55
- const cursor = [1.05, 0.02]; // ~horizontal, slightly off grid
56
- const snapped = snapPointToAngle(from, cursor, Math.PI / 4, 0.25);
57
- // After 0° lock + 0.25m grid, X must be a 0.25 multiple.
58
- expect(snapped[0] / 0.25).toBeCloseTo(Math.round(snapped[0] / 0.25));
59
- });
60
- test('preserves distance from `from`', () => {
61
- const from = [2, 3];
62
- const cursor = [3, 4];
63
- const distance = Math.hypot(1, 1);
64
- const snapped = snapPointToAngle(from, cursor, Math.PI / 4);
65
- expect(Math.hypot(snapped[0] - 2, snapped[1] - 3)).toBeCloseTo(distance);
66
- });
67
- });
68
- describe('snapPointAlongAngleRay', () => {
69
- test('stays exactly on the 15° ray while distance-snapping to the grid step', () => {
70
- const from = [0, 0];
71
- const cursor = [2, 0.5]; // ≈14° — snaps to 15°
72
- const snapped = snapPointAlongAngleRay(from, cursor, Math.PI / 12, 0.25);
73
- expect(Math.atan2(snapped[1], snapped[0])).toBeCloseTo(Math.PI / 12, 10);
74
- const distance = Math.hypot(snapped[0], snapped[1]);
75
- expect(distance / 0.25).toBeCloseTo(Math.round(distance / 0.25), 10);
76
- });
77
- test('grid-snapping after the angle projection would pull the point off the ray', () => {
78
- const from = [0, 0];
79
- const cursor = [2, 0.5];
80
- const offRay = snapPointToAngle(from, cursor, Math.PI / 12, 0.25);
81
- expect(Math.atan2(offRay[1], offRay[0])).not.toBeCloseTo(Math.PI / 12, 4);
82
- });
83
- test('45° back-compat: locks to the diagonal with grid-multiple distance', () => {
84
- const from = [1, 1];
85
- const cursor = [2.1, 1.9]; // near 45° from `from`
86
- const snapped = snapPointAlongAngleRay(from, cursor, Math.PI / 4, 0.25);
87
- expect(Math.atan2(snapped[1] - 1, snapped[0] - 1)).toBeCloseTo(Math.PI / 4, 10);
88
- const distance = Math.hypot(snapped[0] - 1, snapped[1] - 1);
89
- expect(distance / 0.25).toBeCloseTo(Math.round(distance / 0.25), 10);
90
- });
91
- test('preserves the projected distance when no distanceStep is given', () => {
92
- const from = [0, 0];
93
- const cursor = [1, 0.05]; // near 0°
94
- const snapped = snapPointAlongAngleRay(from, cursor, Math.PI / 12);
95
- expect(snapped[0]).toBeCloseTo(1, 10); // projection of (1, 0.05) onto 0° ray
96
- expect(snapped[1]).toBeCloseTo(0, 10);
97
- });
98
- test('returns `from` for a zero-length segment', () => {
99
- expect(snapPointAlongAngleRay([2, 3], [2, 3], Math.PI / 12, 0.25)).toEqual([2, 3]);
100
- });
101
- test('is idempotent on its own output', () => {
102
- const from = [0.5, -1];
103
- const cursor = [3.2, 0.4];
104
- const once = snapPointAlongAngleRay(from, cursor, Math.PI / 12, 0.5);
105
- const twice = snapPointAlongAngleRay(from, once, Math.PI / 12, 0.5);
106
- expect(twice[0]).toBeCloseTo(once[0], 10);
107
- expect(twice[1]).toBeCloseTo(once[1], 10);
108
- });
109
- });
110
- describe('snapAngleToList', () => {
111
- test('snaps to the nearest entry within tolerance', () => {
112
- const targets = [0, Math.PI / 2, Math.PI, (3 * Math.PI) / 2];
113
- expect(snapAngleToList(0.05, targets, Math.PI / 36)).toBe(0);
114
- expect(snapAngleToList(Math.PI / 2 + 0.02, targets, Math.PI / 36)).toBe(Math.PI / 2);
115
- });
116
- test('returns original angle when no target is within tolerance', () => {
117
- const targets = [0, Math.PI / 2];
118
- expect(snapAngleToList(0.5, targets, Math.PI / 36)).toBe(0.5);
119
- });
120
- test('handles wrap-around near ±π', () => {
121
- const targets = [Math.PI];
122
- expect(snapAngleToList(-Math.PI + 0.01, targets, Math.PI / 36)).toBe(Math.PI);
123
- });
124
- });
125
- describe('snapServices facade', () => {
126
- test('grid.snap matches snapPointToGrid', () => {
127
- expect(snapServices.grid.snap([0.3, 0.6], 0.25)).toEqual(snapPointToGrid([0.3, 0.6], 0.25));
128
- });
129
- test('grid.snapScalar matches snapScalar', () => {
130
- expect(snapServices.grid.snapScalar(0.3, 0.25)).toBe(snapScalar(0.3, 0.25));
131
- });
132
- test('angle.snapTo matches snapPointToAngle', () => {
133
- const from = [0, 0];
134
- const cursor = [1, 0.9];
135
- expect(snapServices.angle.snapTo(from, cursor, Math.PI / 4)).toEqual(snapPointToAngle(from, cursor, Math.PI / 4));
136
- });
137
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=system-graph.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"system-graph.test.d.ts","sourceRoot":"","sources":["../../src/services/system-graph.test.ts"],"names":[],"mappings":""}
@@ -1,138 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { registerNode } from '../registry';
3
- import { buildPortComponents, summarizeSystemFor } from './system-graph';
4
- // Stub registrations: the graph consults `def.ports` for the connectivity
5
- // graph and `def.distributionRole` to classify each node. Mirrors the real
6
- // kinds' port + role conventions (duct runs expose start/end, equipment a
7
- // supply collar, terminals one collar) without importing the nodes package.
8
- function stubDef(kind, distributionRole, ports) {
9
- registerNode({
10
- kind,
11
- schemaVersion: 1,
12
- schema: {},
13
- category: 'utility',
14
- distributionRole,
15
- defaults: () => ({}),
16
- capabilities: {},
17
- ports,
18
- });
19
- }
20
- stubDef('duct-segment', 'run', (node) => {
21
- const path = node.path;
22
- const system = node.system;
23
- return [
24
- { id: 'start', position: path[0], direction: [-1, 0, 0], diameter: 6, system },
25
- {
26
- id: 'end',
27
- position: path[path.length - 1],
28
- direction: [1, 0, 0],
29
- diameter: 6,
30
- system,
31
- },
32
- ];
33
- });
34
- stubDef('hvac-equipment', 'equipment', (node) => {
35
- const position = node.position;
36
- return [{ id: 'supply', position, direction: [0, 1, 0], diameter: 12, system: 'supply' }];
37
- });
38
- stubDef('duct-terminal', 'terminal', (node) => {
39
- const position = node.position;
40
- return [{ id: 'collar', position, direction: [0, -1, 0], diameter: 6, system: 'supply' }];
41
- });
42
- let nextId = 0;
43
- function makeNode(type, fields) {
44
- nextId += 1;
45
- return { id: `${type}_${nextId}`, type, object: 'node', parentId: null, ...fields };
46
- }
47
- function sceneOf(...nodes) {
48
- return Object.fromEntries(nodes.map((n) => [n.id, n]));
49
- }
50
- function run(path, system = 'supply') {
51
- return makeNode('duct-segment', { path, system, diameter: 6 });
52
- }
53
- describe('buildPortComponents', () => {
54
- test('chained runs land in one component; a distant run is separate', () => {
55
- const a = run([
56
- [0, 0, 0],
57
- [3, 0, 0],
58
- ]);
59
- const b = run([
60
- [3, 0, 0],
61
- [3, 0, 4],
62
- ]); // shares a's end
63
- const c = run([
64
- [20, 0, 0],
65
- [24, 0, 0],
66
- ]); // far away
67
- const components = buildPortComponents(sceneOf(a, b, c));
68
- expect(components.length).toBe(2);
69
- const joined = components.find((g) => g.length === 2);
70
- expect(new Set(joined)).toEqual(new Set([a.id, b.id]));
71
- });
72
- test('joints within tolerance still join; outside do not', () => {
73
- const a = run([
74
- [0, 0, 0],
75
- [3, 0, 0],
76
- ]);
77
- const near = run([
78
- [3.03, 0, 0],
79
- [6, 0, 0],
80
- ]); // 3 cm — joined
81
- const far = run([
82
- [3.2, 0, 4],
83
- [6, 0, 4],
84
- ]); // 20 cm in another row — separate
85
- const components = buildPortComponents(sceneOf(a, near, far));
86
- expect(components.length).toBe(2);
87
- });
88
- test('nodes without ports do not participate', () => {
89
- const wall = makeNode('wall', {});
90
- const a = run([
91
- [0, 0, 0],
92
- [3, 0, 0],
93
- ]);
94
- const components = buildPortComponents(sceneOf(wall, a));
95
- expect(components.length).toBe(1);
96
- expect(components[0]).toEqual([a.id]);
97
- });
98
- });
99
- describe('summarizeSystemFor', () => {
100
- test('full tree: equipment → run → terminal, stats add up', () => {
101
- const furnace = makeNode('hvac-equipment', { position: [0, 0, 0] });
102
- const trunk = run([
103
- [0, 0, 0],
104
- [4, 0, 0],
105
- ]);
106
- const branch = run([
107
- [4, 0, 0],
108
- [4, 0, 3],
109
- ]);
110
- const register = makeNode('duct-terminal', {
111
- position: [4, 0, 3],
112
- terminalType: 'supply-register',
113
- });
114
- const scene = sceneOf(furnace, trunk, branch, register);
115
- const summary = summarizeSystemFor(register.id, scene);
116
- expect(summary.nodeIds.length).toBe(4);
117
- expect(summary.connectedToEquipment).toBe(true);
118
- expect(summary.runCount).toBe(2);
119
- expect(summary.runLengthM).toBeCloseTo(7, 6);
120
- expect(summary.terminalCount).toBe(1);
121
- expect(summary.equipmentCount).toBe(1);
122
- expect(summary.systems).toEqual(['supply']);
123
- });
124
- test('orphaned run reports no equipment', () => {
125
- const lonely = run([
126
- [10, 0, 10],
127
- [14, 0, 10],
128
- ]);
129
- const summary = summarizeSystemFor(lonely.id, sceneOf(lonely));
130
- expect(summary.connectedToEquipment).toBe(false);
131
- expect(summary.runCount).toBe(1);
132
- expect(summary.runLengthM).toBeCloseTo(4, 6);
133
- });
134
- test('port-less node → null', () => {
135
- const wall = makeNode('wall', {});
136
- expect(summarizeSystemFor(wall.id, sceneOf(wall))).toBeNull();
137
- });
138
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=validate-dwv.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validate-dwv.test.d.ts","sourceRoot":"","sources":["../../src/services/validate-dwv.test.ts"],"names":[],"mappings":""}
@@ -1,109 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { registerNode } from '../registry';
3
- import { validateDwv } from './validate-dwv';
4
- // The validator reads node fields directly + buildPortComponents (which
5
- // consults def.ports), so register stub port-providers for the DWV kinds
6
- // it groups by. Mirrors the system-graph test's approach.
7
- function stubDef(kind, ports) {
8
- registerNode({
9
- kind,
10
- schemaVersion: 1,
11
- schema: {},
12
- category: 'utility',
13
- defaults: () => ({}),
14
- capabilities: {},
15
- ports,
16
- });
17
- }
18
- stubDef('pipe-segment', (node) => {
19
- const path = node.path;
20
- const diameter = node.diameter;
21
- const system = node.system;
22
- return [
23
- { id: 'start', position: path[0], direction: [-1, 0, 0], diameter, system },
24
- {
25
- id: 'end',
26
- position: path[path.length - 1],
27
- direction: [1, 0, 0],
28
- diameter,
29
- system,
30
- },
31
- ];
32
- });
33
- stubDef('pipe-trap', (node) => {
34
- const position = node.position;
35
- return [{ id: 'inlet', position, direction: [0, 1, 0], diameter: 1.5, system: 'waste' }];
36
- });
37
- let nextId = 0;
38
- function makeNode(type, fields) {
39
- nextId += 1;
40
- return { id: `${type}_${nextId}`, type, object: 'node', parentId: null, ...fields };
41
- }
42
- function sceneOf(...nodes) {
43
- return Object.fromEntries(nodes.map((n) => [n.id, n]));
44
- }
45
- /** A waste run from a→b. Drop the end Y to slope it. */
46
- function waste(path, diameter = 2) {
47
- return makeNode('pipe-segment', { path, diameter, system: 'waste' });
48
- }
49
- const QUARTER_PER_FOOT = 1 / 48;
50
- describe('validateDwv — slope', () => {
51
- test('flags a flat waste run', () => {
52
- const run = waste([
53
- [0, 0, 0],
54
- [3, 0, 0], // dead level
55
- ]);
56
- const findings = validateDwv(sceneOf(run));
57
- expect(findings.some((f) => f.code === 'slope-too-flat')).toBe(true);
58
- });
59
- test('passes a run sloped at quarter-inch per foot', () => {
60
- const drop = 3 * QUARTER_PER_FOOT;
61
- const run = waste([
62
- [0, 0, 0],
63
- [3, -drop, 0],
64
- ]);
65
- const findings = validateDwv(sceneOf(run));
66
- expect(findings.some((f) => f.code === 'slope-too-flat')).toBe(false);
67
- });
68
- test('flags an over-steep run (siphoning risk)', () => {
69
- // 2" pipe, max slope = 2/12 ≈ 0.167; drop 2m over 1m horizontal.
70
- const run = waste([
71
- [0, 0, 0],
72
- [1, -2, 0],
73
- ]);
74
- const findings = validateDwv(sceneOf(run));
75
- expect(findings.some((f) => f.code === 'slope-too-steep')).toBe(true);
76
- });
77
- test('ignores vents (level is fine)', () => {
78
- const vent = makeNode('pipe-segment', {
79
- path: [
80
- [0, 0, 0],
81
- [0, 3, 0],
82
- ],
83
- diameter: 2,
84
- system: 'vent',
85
- });
86
- const findings = validateDwv(sceneOf(vent));
87
- expect(findings.length).toBe(0);
88
- });
89
- });
90
- describe('validateDwv — trap arm', () => {
91
- test('flags an over-long trap arm', () => {
92
- const trap = makeNode('pipe-trap', {
93
- position: [0, 0, 0],
94
- diameter: 1.5, // max arm 42in = 1.067m
95
- armLengthM: 2, // way over
96
- });
97
- const findings = validateDwv(sceneOf(trap));
98
- expect(findings.some((f) => f.code === 'trap-arm-too-long')).toBe(true);
99
- });
100
- test('passes a trap arm within the limit', () => {
101
- const trap = makeNode('pipe-trap', {
102
- position: [0, 0, 0],
103
- diameter: 2, // max arm 60in = 1.524m
104
- armLengthM: 1,
105
- });
106
- const findings = validateDwv(sceneOf(trap));
107
- expect(findings.some((f) => f.code === 'trap-arm-too-long')).toBe(false);
108
- });
109
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=node-mutation-sanitize.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"node-mutation-sanitize.test.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-mutation-sanitize.test.ts"],"names":[],"mappings":""}