@pascal-app/core 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +67 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
- package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
- package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +59 -26
- package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
- package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -6
- package/dist/lib/level-name.d.ts +4 -0
- package/dist/lib/level-name.d.ts.map +1 -0
- package/dist/lib/level-name.js +10 -0
- package/dist/lib/measurement-geometry.d.ts +19 -0
- package/dist/lib/measurement-geometry.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.js +232 -0
- package/dist/lib/measurement-geometry.test.d.ts +2 -0
- package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.test.js +102 -0
- package/dist/lib/polygon-geometry.d.ts +0 -1
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/polygon-geometry.js +0 -18
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/selection-proxy.d.ts +4 -0
- package/dist/lib/selection-proxy.d.ts.map +1 -0
- package/dist/lib/selection-proxy.js +12 -0
- package/dist/lib/slab-polygon.d.ts +31 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +525 -31
- package/dist/lib/slab-polygon.test.d.ts +2 -0
- package/dist/lib/slab-polygon.test.d.ts.map +1 -0
- package/dist/lib/slab-polygon.test.js +635 -0
- package/dist/lib/slots.d.ts +14 -0
- package/dist/lib/slots.d.ts.map +1 -0
- package/dist/lib/slots.js +26 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +54 -2
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +599 -124
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +376 -0
- package/dist/lib/wall-distance.d.ts +60 -0
- package/dist/lib/wall-distance.d.ts.map +1 -0
- package/dist/lib/wall-distance.js +85 -0
- package/dist/lib/zone-quantities.d.ts +21 -0
- package/dist/lib/zone-quantities.d.ts.map +1 -0
- package/dist/lib/zone-quantities.js +183 -0
- package/dist/lib/zone-quantities.test.d.ts +2 -0
- package/dist/lib/zone-quantities.test.d.ts.map +1 -0
- package/dist/lib/zone-quantities.test.js +82 -0
- package/dist/material-library.d.ts +20 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +3737 -200
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +394 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +136 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +282 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +196 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +123 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +163 -0
- package/dist/registry/types.d.ts +2099 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +35 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +32 -4
- package/dist/schema/material.d.ts +13 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +17 -5
- package/dist/schema/material.test.d.ts +2 -0
- package/dist/schema/material.test.d.ts.map +1 -0
- package/dist/schema/material.test.js +43 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/cabinet.d.ts +534 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -0
- package/dist/schema/nodes/cabinet.js +137 -0
- package/dist/schema/nodes/ceiling.d.ts +5 -2
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +5 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +600 -7
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +589 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/door.d.ts +13 -5
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +12 -0
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/duct-fitting.d.ts +91 -0
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/duct-fitting.js +92 -0
- package/dist/schema/nodes/duct-segment.d.ts +61 -0
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
- package/dist/schema/nodes/duct-segment.js +73 -0
- package/dist/schema/nodes/duct-terminal.d.ts +57 -0
- package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
- package/dist/schema/nodes/duct-terminal.js +51 -0
- package/dist/schema/nodes/elevator.d.ts +106 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +57 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +19 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +27 -2
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
- package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
- package/dist/schema/nodes/hvac-equipment.js +55 -0
- package/dist/schema/nodes/item.d.ts +12 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +20 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +4 -0
- package/dist/schema/nodes/lineset.d.ts +42 -0
- package/dist/schema/nodes/lineset.d.ts.map +1 -0
- package/dist/schema/nodes/lineset.js +38 -0
- package/dist/schema/nodes/liquid-line.d.ts +34 -0
- package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
- package/dist/schema/nodes/liquid-line.js +24 -0
- package/dist/schema/nodes/measurement.d.ts +306 -0
- package/dist/schema/nodes/measurement.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.js +87 -0
- package/dist/schema/nodes/measurement.test.d.ts +2 -0
- package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.test.js +139 -0
- package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-fitting.js +45 -0
- package/dist/schema/nodes/pipe-segment.d.ts +48 -0
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-segment.js +36 -0
- package/dist/schema/nodes/pipe-trap.d.ts +43 -0
- package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-trap.js +36 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
- package/dist/schema/nodes/ridge-vent.d.ts +82 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +248 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.js +318 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.js +330 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
- package/dist/schema/nodes/roof-segment.d.ts +273 -3
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +539 -3
- package/dist/schema/nodes/roof.d.ts +8 -8
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -16
- package/dist/schema/nodes/shelf.d.ts +106 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +90 -0
- package/dist/schema/nodes/site.d.ts +1 -122
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +5 -2
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +5 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/stair-segment.d.ts +4 -4
- package/dist/schema/nodes/stair.d.ts +13 -12
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +3 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/nodes/wall.d.ts +245 -6
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +257 -0
- package/dist/schema/nodes/wall.test.d.ts +2 -0
- package/dist/schema/nodes/wall.test.d.ts.map +1 -0
- package/dist/schema/nodes/wall.test.js +208 -0
- package/dist/schema/nodes/window.d.ts +11 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +12 -0
- package/dist/schema/nodes/zone.d.ts +2 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +6 -0
- package/dist/schema/scene-material.d.ts +41 -0
- package/dist/schema/scene-material.d.ts.map +1 -0
- package/dist/schema/scene-material.js +9 -0
- package/dist/schema/types.d.ts +2850 -655
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +49 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +312 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +131 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +171 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +88 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +131 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +230 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/level-height.d.ts +28 -0
- package/dist/services/level-height.d.ts.map +1 -0
- package/dist/services/level-height.js +62 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/opening-guides.d.ts +141 -0
- package/dist/services/opening-guides.d.ts.map +1 -0
- package/dist/services/opening-guides.js +267 -0
- package/dist/services/opening-guides.test.d.ts +2 -0
- package/dist/services/opening-guides.test.d.ts.map +1 -0
- package/dist/services/opening-guides.test.js +208 -0
- package/dist/services/port-connectivity.d.ts +115 -0
- package/dist/services/port-connectivity.d.ts.map +1 -0
- package/dist/services/port-connectivity.js +300 -0
- package/dist/services/port-connectivity.test.d.ts +2 -0
- package/dist/services/port-connectivity.test.d.ts.map +1 -0
- package/dist/services/port-connectivity.test.js +297 -0
- package/dist/services/riser-diagram.d.ts +40 -0
- package/dist/services/riser-diagram.d.ts.map +1 -0
- package/dist/services/riser-diagram.js +110 -0
- package/dist/services/riser-diagram.test.d.ts +2 -0
- package/dist/services/riser-diagram.test.d.ts.map +1 -0
- package/dist/services/riser-diagram.test.js +66 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +80 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +140 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +137 -0
- package/dist/services/system-graph.d.ts +30 -0
- package/dist/services/system-graph.d.ts.map +1 -0
- package/dist/services/system-graph.js +164 -0
- package/dist/services/system-graph.test.d.ts +2 -0
- package/dist/services/system-graph.test.d.ts.map +1 -0
- package/dist/services/system-graph.test.js +138 -0
- package/dist/services/validate-dwv.d.ts +32 -0
- package/dist/services/validate-dwv.d.ts.map +1 -0
- package/dist/services/validate-dwv.js +118 -0
- package/dist/services/validate-dwv.test.d.ts +2 -0
- package/dist/services/validate-dwv.test.d.ts.map +1 -0
- package/dist/services/validate-dwv.test.js +109 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +585 -15
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
- package/dist/store/actions/ridge-vent-update.test.js +264 -0
- package/dist/store/history-control.d.ts +11 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +33 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
- package/dist/store/use-scene-dirty-tracking.test.js +68 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
- package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
- package/dist/store/use-scene-plugins.test.d.ts +2 -0
- package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
- package/dist/store/use-scene-plugins.test.js +51 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
- package/dist/store/use-scene-window-migration.test.d.ts +2 -0
- package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-window-migration.test.js +85 -0
- package/dist/store/use-scene.d.ts +28 -2
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +546 -49
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/fence/fence-centerline.d.ts +12 -0
- package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
- package/dist/systems/fence/fence-centerline.js +37 -0
- package/dist/systems/fence/fence-spline.d.ts +39 -0
- package/dist/systems/fence/fence-spline.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.js +196 -0
- package/dist/systems/fence/fence-spline.test.d.ts +2 -0
- package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.test.js +136 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +178 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +155 -147
- package/dist/systems/stair/stair-opening-sync.test.js +424 -2
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +38 -2
- package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
- package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-mitering.test.js +63 -0
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +7 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +29 -7
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +62 -0
- package/dist/utils/heal-scene-graph.d.ts +18 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.js +90 -0
- package/dist/utils/heal-scene-graph.test.d.ts +2 -0
- package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.test.js +88 -0
- package/dist/validation/validate-build-json.d.ts +44 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +256 -0
- package/package.json +11 -4
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { AnyNodeDefinition, BakePolicy, NodeRegistry, Plugin } from './types';
|
|
2
|
+
export declare const nodeRegistry: NodeRegistry & {
|
|
3
|
+
_register: (def: AnyNodeDefinition) => void;
|
|
4
|
+
_reset: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function registerNode(def: AnyNodeDefinition): void;
|
|
7
|
+
/** The plugin that registered a node kind, when it came through {@link loadPlugin}. */
|
|
8
|
+
export declare function getNodePluginId(kind: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a registered kind should participate in a project. Kinds registered
|
|
11
|
+
* directly by the host and the built-in plugin are always enabled. An omitted
|
|
12
|
+
* install list means a legacy scene whose plugin state predates persistence, so
|
|
13
|
+
* loaded plugins remain visible for backward compatibility.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isNodeKindEnabled(kind: string, installedPlugins?: readonly string[]): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the set of registered kinds whose definition declares the
|
|
18
|
+
* `selectable` capability. Callers that maintain hardcoded "selectable kinds"
|
|
19
|
+
* lists (SelectionManager, FloatingActionMenu) should concat this with their
|
|
20
|
+
* legacy entries instead of editing the hardcoded list per migration.
|
|
21
|
+
*
|
|
22
|
+
* Phase 6 deletes the hardcoded lists entirely and uses this function as the
|
|
23
|
+
* single source of truth. For now it's additive over the legacy lists so the
|
|
24
|
+
* existing kinds keep working unchanged.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getSelectableKinds(): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Returns true when the kind is declared selectable in the registry. Use
|
|
29
|
+
* in expression chains like `if (node.type === 'wall' || isRegistrySelectable(node.type))`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isRegistrySelectable(kind: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Kinds whose `def.floorplanScope` matches the requested scope. Used by
|
|
34
|
+
* `FloorplanRegistryLayer` to discover building-scoped kinds (e.g.
|
|
35
|
+
* elevator) without hardcoding kind names in the editor layer. `'level'`
|
|
36
|
+
* is the default, so `kindsWithFloorplanScope('level')` includes kinds
|
|
37
|
+
* that didn't set the field at all.
|
|
38
|
+
*/
|
|
39
|
+
export declare function kindsWithFloorplanScope(scope: 'level' | 'building'): string[];
|
|
40
|
+
/**
|
|
41
|
+
* A kind's {@link BakePolicy} from the registry, defaulting to `'static'` for
|
|
42
|
+
* kinds that don't declare one (or aren't registered). The bake and the baked
|
|
43
|
+
* `/viewer` consult this instead of hardcoding kind names — see
|
|
44
|
+
* plans/editor-plugin-trees-example.md → Part D.
|
|
45
|
+
*/
|
|
46
|
+
export declare function bakePolicyOf(kind: string): BakePolicy;
|
|
47
|
+
/** Registered kinds whose {@link BakePolicy} matches `policy`. `'static'` is the
|
|
48
|
+
* default, so `kindsWithBakePolicy('static')` includes kinds that didn't set it. */
|
|
49
|
+
export declare function kindsWithBakePolicy(policy: BakePolicy): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Returns true when the kind is movable from a 2D floor-plan handle —
|
|
52
|
+
* either via `capabilities.movable`, an explicit
|
|
53
|
+
* `def.floorplanMoveTarget`, or an `affordanceTools.move` 3D mover that
|
|
54
|
+
* the floating action menu can engage. Replaces the kind-name ternary
|
|
55
|
+
* chain in `floating-action-menu.tsx`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isRegistryMovable(kind: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Whether the kind has a move tool that MOUNTS in the 3D viewport — the
|
|
60
|
+
* generic `capabilities.movable` mover or a bespoke `affordanceTools.move`.
|
|
61
|
+
* Narrower than {@link isRegistryMovable}, which also accepts floorplan-only
|
|
62
|
+
* movers (e.g. zone) that have no 3D tool. Gates 3D direct move: Ctrl/Meta-drag
|
|
63
|
+
* and the move-cross grip. Kept beside `isRegistryMovable` so the 2D and 3D
|
|
64
|
+
* movability predicates can't drift apart.
|
|
65
|
+
*/
|
|
66
|
+
export declare function hasRegistry3DMoveTool(kind: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the kind can be saved as a reusable preset. Default: an
|
|
69
|
+
* explicit `capabilities.presettable` boolean wins; otherwise the kind
|
|
70
|
+
* is presettable iff it declares `def.parametrics`. Read by host apps
|
|
71
|
+
* (community shell) to gate "save as preset" UI on a selection.
|
|
72
|
+
*/
|
|
73
|
+
export declare function isPresettable(def: AnyNodeDefinition): boolean;
|
|
74
|
+
export declare function isPresettableKind(kind: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve a kind's facing-triangle config, or `null` when it has none.
|
|
77
|
+
* `{ reversed }` says whether the triangle points along the node's local -Z
|
|
78
|
+
* (its front) instead of +Z. One reader (the editor-side `<FacingPoseIndicator>`
|
|
79
|
+
* publishers) so placement and move stay consistent.
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveFacingIndicator(kind: string): {
|
|
82
|
+
reversed: boolean;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* Names of schema fields on `def` that are host references (`wallId`,
|
|
86
|
+
* `wallT`, etc.). Read by host apps at preset-save time to strip these
|
|
87
|
+
* from the stored payload — see `def.capabilities.hostRefFields` docs.
|
|
88
|
+
* Returns an empty array for kinds that don't declare any.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getHostRefFields(def: AnyNodeDefinition): ReadonlyArray<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Whether instances of this kind are created by drawing with a build tool
|
|
93
|
+
* (tool id === node `type`) rather than dropping a finished instance. Read
|
|
94
|
+
* by host apps to route preset placement of such kinds through
|
|
95
|
+
* `setToolDefaults(type, params)` + `setTool(type)` — see
|
|
96
|
+
* `def.capabilities.drawTool` docs.
|
|
97
|
+
*/
|
|
98
|
+
export declare function isDrawnViaTool(def: AnyNodeDefinition): boolean;
|
|
99
|
+
export declare function isDrawnViaToolKind(kind: string): boolean;
|
|
100
|
+
export declare function loadPlugin(plugin: Plugin): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* App-level plugin discovery hook. The bootstrap loads `builtinPlugin`
|
|
103
|
+
* unconditionally and then awaits this to pick up any extra plugins
|
|
104
|
+
* (third-party node packs, AI-authored bundles, user-installed kinds).
|
|
105
|
+
* Defaults to returning `[]` — apps that want external plugins call
|
|
106
|
+
* {@link setPluginDiscovery} before the bootstrap module runs.
|
|
107
|
+
*
|
|
108
|
+
* Kept async so a future loader can fetch over the network without
|
|
109
|
+
* changing the contract. See `wiki/architecture/plugin-authoring.md` for
|
|
110
|
+
* the plugin author surface this enables.
|
|
111
|
+
*/
|
|
112
|
+
export type PluginDiscovery = () => Promise<Plugin[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Replace the plugin discovery implementation. Call once at app startup
|
|
115
|
+
* before {@link discoverPlugins} is invoked (bootstrap order matters).
|
|
116
|
+
*
|
|
117
|
+
* The contract is intentionally minimal — just "return a list of
|
|
118
|
+
* plugins to load." The loader can be a static `import.meta.glob`, a
|
|
119
|
+
* `fetch` against a registry endpoint, a worker IPC, etc. Each returned
|
|
120
|
+
* plugin still goes through {@link loadPlugin} so the same API-version
|
|
121
|
+
* gate + duplicate-kind protection applies.
|
|
122
|
+
*/
|
|
123
|
+
export declare function setPluginDiscovery(fn: PluginDiscovery): void;
|
|
124
|
+
/**
|
|
125
|
+
* Extend the current plugin discovery instead of replacing it. Useful for app-
|
|
126
|
+
* bundled example or first-party plugins that should load alongside any host-
|
|
127
|
+
* provided discovery source, not clobber it.
|
|
128
|
+
*/
|
|
129
|
+
export declare function extendPluginDiscovery(fn: PluginDiscovery): void;
|
|
130
|
+
/**
|
|
131
|
+
* Run the active plugin discovery and return the discovered plugins.
|
|
132
|
+
* Bootstrap code is expected to call this after `loadPlugin(builtinPlugin)`
|
|
133
|
+
* and then `await loadPlugin(...)` each result in order.
|
|
134
|
+
*/
|
|
135
|
+
export declare function discoverPlugins(): Promise<Plugin[]>;
|
|
136
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAkFlF,eAAO,MAAM,YAAY,EAAE,YAAY,GAAG;IACxC,SAAS,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC3C,MAAM,EAAE,MAAM,IAAI,CAAA;CACM,CAAA;AAE1B,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAEzD;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI7F;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAQ7C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,EAAE,CAO7E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAErD;AAED;qFACqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAMhE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAK7D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAIjF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,aAAa,CAAC,MAAM,CAAC,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAE9D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;AAMrD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAO5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAM/D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnD"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
const HOST_API_VERSION = 1;
|
|
2
|
+
const BUILTIN_PLUGIN_ID = 'pascal:core';
|
|
3
|
+
const pluginIdsByKind = new Map();
|
|
4
|
+
// True in dev / test builds, false in production. Tries Vite's
|
|
5
|
+
// `import.meta.env.DEV` first (the editor app's bundler) and falls back
|
|
6
|
+
// to `process.env.NODE_ENV !== 'production'` for Node test runners.
|
|
7
|
+
function isDevMode() {
|
|
8
|
+
try {
|
|
9
|
+
const meta = import.meta;
|
|
10
|
+
if (typeof meta?.env?.DEV === 'boolean')
|
|
11
|
+
return meta.env.DEV;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// import.meta unavailable in some CJS contexts — fall through.
|
|
15
|
+
}
|
|
16
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV) {
|
|
17
|
+
return process.env.NODE_ENV !== 'production';
|
|
18
|
+
}
|
|
19
|
+
// No environment signal — be safe and treat as production.
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
class NodeRegistryImpl {
|
|
23
|
+
defs = new Map();
|
|
24
|
+
has(kind) {
|
|
25
|
+
return this.defs.has(kind);
|
|
26
|
+
}
|
|
27
|
+
get(kind) {
|
|
28
|
+
return this.defs.get(kind);
|
|
29
|
+
}
|
|
30
|
+
entries() {
|
|
31
|
+
return this.defs.entries();
|
|
32
|
+
}
|
|
33
|
+
schemas() {
|
|
34
|
+
return Array.from(this.defs.values(), (d) => d.schema);
|
|
35
|
+
}
|
|
36
|
+
get size() {
|
|
37
|
+
return this.defs.size;
|
|
38
|
+
}
|
|
39
|
+
// Internal — exposed via registerNode below.
|
|
40
|
+
_register(def) {
|
|
41
|
+
if (typeof def.kind !== 'string' || def.kind.length === 0) {
|
|
42
|
+
throw new Error('[registry] NodeDefinition.kind must be a non-empty string');
|
|
43
|
+
}
|
|
44
|
+
if (typeof def.schemaVersion !== 'number' || def.schemaVersion < 1) {
|
|
45
|
+
throw new Error(`[registry] NodeDefinition.schemaVersion must be a positive integer (kind: "${def.kind}")`);
|
|
46
|
+
}
|
|
47
|
+
// Duplicate-kind handling depends on environment:
|
|
48
|
+
// - **Production**: throw. The plugin-authoring contract
|
|
49
|
+
// (`wiki/architecture/plugin-authoring.md`) guarantees that two
|
|
50
|
+
// plugins shipping `kind: 'couch'` is a startup-time error, not
|
|
51
|
+
// a silent overwrite — collisions need to be visible.
|
|
52
|
+
// - **Dev (HMR)**: replace with a warning. Saving `def.ts` would
|
|
53
|
+
// otherwise either crash on re-execute or skip it entirely,
|
|
54
|
+
// leaving stale descriptors pinned in memory.
|
|
55
|
+
if (this.defs.has(def.kind)) {
|
|
56
|
+
if (isDevMode()) {
|
|
57
|
+
console.warn(`[registry] re-registering node kind "${def.kind}" (HMR)`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
throw new Error(`[registry] duplicate node kind: "${def.kind}" already registered`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.defs.set(def.kind, def);
|
|
64
|
+
}
|
|
65
|
+
// Test-only — clears the registry. Not exported from the package barrel.
|
|
66
|
+
_reset() {
|
|
67
|
+
this.defs.clear();
|
|
68
|
+
pluginIdsByKind.clear();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export const nodeRegistry = new NodeRegistryImpl();
|
|
72
|
+
export function registerNode(def) {
|
|
73
|
+
nodeRegistry._register(def);
|
|
74
|
+
}
|
|
75
|
+
/** The plugin that registered a node kind, when it came through {@link loadPlugin}. */
|
|
76
|
+
export function getNodePluginId(kind) {
|
|
77
|
+
return pluginIdsByKind.get(kind);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether a registered kind should participate in a project. Kinds registered
|
|
81
|
+
* directly by the host and the built-in plugin are always enabled. An omitted
|
|
82
|
+
* install list means a legacy scene whose plugin state predates persistence, so
|
|
83
|
+
* loaded plugins remain visible for backward compatibility.
|
|
84
|
+
*/
|
|
85
|
+
export function isNodeKindEnabled(kind, installedPlugins) {
|
|
86
|
+
const pluginId = getNodePluginId(kind);
|
|
87
|
+
if (!pluginId || pluginId === BUILTIN_PLUGIN_ID || installedPlugins === undefined)
|
|
88
|
+
return true;
|
|
89
|
+
return installedPlugins.includes(pluginId);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns the set of registered kinds whose definition declares the
|
|
93
|
+
* `selectable` capability. Callers that maintain hardcoded "selectable kinds"
|
|
94
|
+
* lists (SelectionManager, FloatingActionMenu) should concat this with their
|
|
95
|
+
* legacy entries instead of editing the hardcoded list per migration.
|
|
96
|
+
*
|
|
97
|
+
* Phase 6 deletes the hardcoded lists entirely and uses this function as the
|
|
98
|
+
* single source of truth. For now it's additive over the legacy lists so the
|
|
99
|
+
* existing kinds keep working unchanged.
|
|
100
|
+
*/
|
|
101
|
+
export function getSelectableKinds() {
|
|
102
|
+
const result = [];
|
|
103
|
+
for (const [kind, def] of nodeRegistry.entries()) {
|
|
104
|
+
if (def.capabilities.selectable !== undefined) {
|
|
105
|
+
result.push(kind);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Returns true when the kind is declared selectable in the registry. Use
|
|
112
|
+
* in expression chains like `if (node.type === 'wall' || isRegistrySelectable(node.type))`.
|
|
113
|
+
*/
|
|
114
|
+
export function isRegistrySelectable(kind) {
|
|
115
|
+
return nodeRegistry.get(kind)?.capabilities.selectable !== undefined;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Kinds whose `def.floorplanScope` matches the requested scope. Used by
|
|
119
|
+
* `FloorplanRegistryLayer` to discover building-scoped kinds (e.g.
|
|
120
|
+
* elevator) without hardcoding kind names in the editor layer. `'level'`
|
|
121
|
+
* is the default, so `kindsWithFloorplanScope('level')` includes kinds
|
|
122
|
+
* that didn't set the field at all.
|
|
123
|
+
*/
|
|
124
|
+
export function kindsWithFloorplanScope(scope) {
|
|
125
|
+
const result = [];
|
|
126
|
+
for (const [kind, def] of nodeRegistry.entries()) {
|
|
127
|
+
const declared = def.floorplanScope ?? 'level';
|
|
128
|
+
if (declared === scope)
|
|
129
|
+
result.push(kind);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A kind's {@link BakePolicy} from the registry, defaulting to `'static'` for
|
|
135
|
+
* kinds that don't declare one (or aren't registered). The bake and the baked
|
|
136
|
+
* `/viewer` consult this instead of hardcoding kind names — see
|
|
137
|
+
* plans/editor-plugin-trees-example.md → Part D.
|
|
138
|
+
*/
|
|
139
|
+
export function bakePolicyOf(kind) {
|
|
140
|
+
return nodeRegistry.get(kind)?.bake ?? 'static';
|
|
141
|
+
}
|
|
142
|
+
/** Registered kinds whose {@link BakePolicy} matches `policy`. `'static'` is the
|
|
143
|
+
* default, so `kindsWithBakePolicy('static')` includes kinds that didn't set it. */
|
|
144
|
+
export function kindsWithBakePolicy(policy) {
|
|
145
|
+
const result = [];
|
|
146
|
+
for (const [kind, def] of nodeRegistry.entries()) {
|
|
147
|
+
if ((def.bake ?? 'static') === policy)
|
|
148
|
+
result.push(kind);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returns true when the kind is movable from a 2D floor-plan handle —
|
|
154
|
+
* either via `capabilities.movable`, an explicit
|
|
155
|
+
* `def.floorplanMoveTarget`, or an `affordanceTools.move` 3D mover that
|
|
156
|
+
* the floating action menu can engage. Replaces the kind-name ternary
|
|
157
|
+
* chain in `floating-action-menu.tsx`.
|
|
158
|
+
*/
|
|
159
|
+
export function isRegistryMovable(kind) {
|
|
160
|
+
const def = nodeRegistry.get(kind);
|
|
161
|
+
if (!def)
|
|
162
|
+
return false;
|
|
163
|
+
if (def.capabilities.movable !== undefined)
|
|
164
|
+
return true;
|
|
165
|
+
if (def.floorplanMoveTarget !== undefined)
|
|
166
|
+
return true;
|
|
167
|
+
if (def.affordanceTools?.move !== undefined)
|
|
168
|
+
return true;
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Whether the kind has a move tool that MOUNTS in the 3D viewport — the
|
|
173
|
+
* generic `capabilities.movable` mover or a bespoke `affordanceTools.move`.
|
|
174
|
+
* Narrower than {@link isRegistryMovable}, which also accepts floorplan-only
|
|
175
|
+
* movers (e.g. zone) that have no 3D tool. Gates 3D direct move: Ctrl/Meta-drag
|
|
176
|
+
* and the move-cross grip. Kept beside `isRegistryMovable` so the 2D and 3D
|
|
177
|
+
* movability predicates can't drift apart.
|
|
178
|
+
*/
|
|
179
|
+
export function hasRegistry3DMoveTool(kind) {
|
|
180
|
+
const def = nodeRegistry.get(kind);
|
|
181
|
+
if (!def)
|
|
182
|
+
return false;
|
|
183
|
+
return def.capabilities.movable !== undefined || def.affordanceTools?.move !== undefined;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Whether the kind can be saved as a reusable preset. Default: an
|
|
187
|
+
* explicit `capabilities.presettable` boolean wins; otherwise the kind
|
|
188
|
+
* is presettable iff it declares `def.parametrics`. Read by host apps
|
|
189
|
+
* (community shell) to gate "save as preset" UI on a selection.
|
|
190
|
+
*/
|
|
191
|
+
export function isPresettable(def) {
|
|
192
|
+
if (typeof def.capabilities.presettable === 'boolean') {
|
|
193
|
+
return def.capabilities.presettable;
|
|
194
|
+
}
|
|
195
|
+
return def.parametrics !== undefined;
|
|
196
|
+
}
|
|
197
|
+
export function isPresettableKind(kind) {
|
|
198
|
+
const def = nodeRegistry.get(kind);
|
|
199
|
+
return def ? isPresettable(def) : false;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Resolve a kind's facing-triangle config, or `null` when it has none.
|
|
203
|
+
* `{ reversed }` says whether the triangle points along the node's local -Z
|
|
204
|
+
* (its front) instead of +Z. One reader (the editor-side `<FacingPoseIndicator>`
|
|
205
|
+
* publishers) so placement and move stay consistent.
|
|
206
|
+
*/
|
|
207
|
+
export function resolveFacingIndicator(kind) {
|
|
208
|
+
const facing = nodeRegistry.get(kind)?.facingIndicator;
|
|
209
|
+
if (!facing)
|
|
210
|
+
return null;
|
|
211
|
+
return { reversed: facing === true ? false : (facing.reversed ?? false) };
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Names of schema fields on `def` that are host references (`wallId`,
|
|
215
|
+
* `wallT`, etc.). Read by host apps at preset-save time to strip these
|
|
216
|
+
* from the stored payload — see `def.capabilities.hostRefFields` docs.
|
|
217
|
+
* Returns an empty array for kinds that don't declare any.
|
|
218
|
+
*/
|
|
219
|
+
export function getHostRefFields(def) {
|
|
220
|
+
return def.capabilities.hostRefFields ?? [];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Whether instances of this kind are created by drawing with a build tool
|
|
224
|
+
* (tool id === node `type`) rather than dropping a finished instance. Read
|
|
225
|
+
* by host apps to route preset placement of such kinds through
|
|
226
|
+
* `setToolDefaults(type, params)` + `setTool(type)` — see
|
|
227
|
+
* `def.capabilities.drawTool` docs.
|
|
228
|
+
*/
|
|
229
|
+
export function isDrawnViaTool(def) {
|
|
230
|
+
return def.capabilities.drawTool === true;
|
|
231
|
+
}
|
|
232
|
+
export function isDrawnViaToolKind(kind) {
|
|
233
|
+
const def = nodeRegistry.get(kind);
|
|
234
|
+
return def ? isDrawnViaTool(def) : false;
|
|
235
|
+
}
|
|
236
|
+
export async function loadPlugin(plugin) {
|
|
237
|
+
if (plugin.apiVersion !== HOST_API_VERSION) {
|
|
238
|
+
throw new Error(`[registry] plugin "${plugin.id}" requires apiVersion ${plugin.apiVersion}; host supports ${HOST_API_VERSION}`);
|
|
239
|
+
}
|
|
240
|
+
for (const def of plugin.nodes ?? []) {
|
|
241
|
+
registerNode(def);
|
|
242
|
+
pluginIdsByKind.set(def.kind, plugin.id);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const defaultPluginDiscovery = async () => [];
|
|
246
|
+
let pluginDiscovery = defaultPluginDiscovery;
|
|
247
|
+
/**
|
|
248
|
+
* Replace the plugin discovery implementation. Call once at app startup
|
|
249
|
+
* before {@link discoverPlugins} is invoked (bootstrap order matters).
|
|
250
|
+
*
|
|
251
|
+
* The contract is intentionally minimal — just "return a list of
|
|
252
|
+
* plugins to load." The loader can be a static `import.meta.glob`, a
|
|
253
|
+
* `fetch` against a registry endpoint, a worker IPC, etc. Each returned
|
|
254
|
+
* plugin still goes through {@link loadPlugin} so the same API-version
|
|
255
|
+
* gate + duplicate-kind protection applies.
|
|
256
|
+
*/
|
|
257
|
+
export function setPluginDiscovery(fn) {
|
|
258
|
+
if (isDevMode() && pluginDiscovery !== defaultPluginDiscovery) {
|
|
259
|
+
console.warn('[registry] setPluginDiscovery replaced an existing discovery chain — plugins registered earlier (e.g. via extendPluginDiscovery) are dropped. Use extendPluginDiscovery to compose instead.');
|
|
260
|
+
}
|
|
261
|
+
pluginDiscovery = fn;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Extend the current plugin discovery instead of replacing it. Useful for app-
|
|
265
|
+
* bundled example or first-party plugins that should load alongside any host-
|
|
266
|
+
* provided discovery source, not clobber it.
|
|
267
|
+
*/
|
|
268
|
+
export function extendPluginDiscovery(fn) {
|
|
269
|
+
const previous = pluginDiscovery;
|
|
270
|
+
pluginDiscovery = async () => {
|
|
271
|
+
const [base, extra] = await Promise.all([previous(), fn()]);
|
|
272
|
+
return [...base, ...extra];
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Run the active plugin discovery and return the discovered plugins.
|
|
277
|
+
* Bootstrap code is expected to call this after `loadPlugin(builtinPlugin)`
|
|
278
|
+
* and then `await loadPlugin(...)` each result in order.
|
|
279
|
+
*/
|
|
280
|
+
export function discoverPlugins() {
|
|
281
|
+
return pluginDiscovery();
|
|
282
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/registry/registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getHostRefFields, getNodePluginId, isDrawnViaTool, isDrawnViaToolKind, isNodeKindEnabled, isPresettable, isPresettableKind, loadPlugin, nodeRegistry, registerNode, } from './registry';
|
|
4
|
+
// Re-registering a kind warns + replaces in dev (HMR) but throws in
|
|
5
|
+
// production — see `registry._register`. `bun test` runs with
|
|
6
|
+
// NODE_ENV=test (dev path), so the throw-path tests pin NODE_ENV to
|
|
7
|
+
// 'production' for the duration of the call.
|
|
8
|
+
async function inProduction(fn) {
|
|
9
|
+
const prev = process.env.NODE_ENV;
|
|
10
|
+
process.env.NODE_ENV = 'production';
|
|
11
|
+
try {
|
|
12
|
+
return await fn();
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
process.env.NODE_ENV = prev;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function makeDefinition(kind, overrides = {}) {
|
|
19
|
+
return {
|
|
20
|
+
kind,
|
|
21
|
+
schemaVersion: 1,
|
|
22
|
+
schema: z.object({ type: z.literal(kind) }),
|
|
23
|
+
category: 'utility',
|
|
24
|
+
defaults: () => ({}),
|
|
25
|
+
capabilities: {},
|
|
26
|
+
renderer: { kind: 'parametric', module: async () => ({ default: () => null }) },
|
|
27
|
+
...overrides,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
describe('nodeRegistry', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
nodeRegistry._reset();
|
|
33
|
+
});
|
|
34
|
+
test('starts empty', () => {
|
|
35
|
+
expect(nodeRegistry.size).toBe(0);
|
|
36
|
+
expect(nodeRegistry.has('anything')).toBe(false);
|
|
37
|
+
expect(nodeRegistry.get('anything')).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
test('registerNode adds a definition', () => {
|
|
40
|
+
const def = makeDefinition('column');
|
|
41
|
+
registerNode(def);
|
|
42
|
+
expect(nodeRegistry.size).toBe(1);
|
|
43
|
+
expect(nodeRegistry.has('column')).toBe(true);
|
|
44
|
+
expect(nodeRegistry.get('column')).toBe(def);
|
|
45
|
+
});
|
|
46
|
+
test('registerNode throws on duplicate kind in production', async () => {
|
|
47
|
+
await inProduction(() => {
|
|
48
|
+
registerNode(makeDefinition('column'));
|
|
49
|
+
expect(() => registerNode(makeDefinition('column'))).toThrow(/duplicate node kind/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
test('registerNode replaces on duplicate kind in dev (HMR)', () => {
|
|
53
|
+
const first = makeDefinition('column');
|
|
54
|
+
const second = makeDefinition('column');
|
|
55
|
+
registerNode(first);
|
|
56
|
+
registerNode(second);
|
|
57
|
+
expect(nodeRegistry.size).toBe(1);
|
|
58
|
+
expect(nodeRegistry.get('column')).toBe(second);
|
|
59
|
+
});
|
|
60
|
+
test('registerNode rejects empty kind', () => {
|
|
61
|
+
expect(() => registerNode(makeDefinition(''))).toThrow(/non-empty string/);
|
|
62
|
+
});
|
|
63
|
+
test('registerNode rejects invalid schemaVersion', () => {
|
|
64
|
+
expect(() => registerNode(makeDefinition('bad', { schemaVersion: 0 }))).toThrow(/schemaVersion/);
|
|
65
|
+
expect(() => registerNode(makeDefinition('bad', { schemaVersion: -1 }))).toThrow(/schemaVersion/);
|
|
66
|
+
});
|
|
67
|
+
test('entries() iterates registered definitions', () => {
|
|
68
|
+
registerNode(makeDefinition('a'));
|
|
69
|
+
registerNode(makeDefinition('b'));
|
|
70
|
+
const kinds = Array.from(nodeRegistry.entries(), ([k]) => k);
|
|
71
|
+
expect(kinds).toEqual(['a', 'b']);
|
|
72
|
+
});
|
|
73
|
+
test('schemas() returns all registered schemas', () => {
|
|
74
|
+
const a = makeDefinition('a');
|
|
75
|
+
const b = makeDefinition('b');
|
|
76
|
+
registerNode(a);
|
|
77
|
+
registerNode(b);
|
|
78
|
+
expect(nodeRegistry.schemas()).toEqual([a.schema, b.schema]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('isPresettable', () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
nodeRegistry._reset();
|
|
84
|
+
});
|
|
85
|
+
test('explicit true wins', () => {
|
|
86
|
+
const def = makeDefinition('explicit-true', { capabilities: { presettable: true } });
|
|
87
|
+
expect(isPresettable(def)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
test('explicit false wins even with parametrics', () => {
|
|
90
|
+
const def = makeDefinition('explicit-false', {
|
|
91
|
+
capabilities: { presettable: false },
|
|
92
|
+
parametrics: { groups: [] },
|
|
93
|
+
});
|
|
94
|
+
expect(isPresettable(def)).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
test('defaults to true when parametrics exists', () => {
|
|
97
|
+
const def = makeDefinition('param', { parametrics: { groups: [] } });
|
|
98
|
+
expect(isPresettable(def)).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
test('defaults to false without parametrics', () => {
|
|
101
|
+
const def = makeDefinition('no-param');
|
|
102
|
+
expect(isPresettable(def)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
test('isPresettableKind looks up the registry', () => {
|
|
105
|
+
registerNode(makeDefinition('shelfy', { parametrics: { groups: [] } }));
|
|
106
|
+
expect(isPresettableKind('shelfy')).toBe(true);
|
|
107
|
+
expect(isPresettableKind('unknown')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('getHostRefFields', () => {
|
|
111
|
+
test('returns the declared hostRefFields verbatim', () => {
|
|
112
|
+
const def = makeDefinition('door', { capabilities: { hostRefFields: ['wallId'] } });
|
|
113
|
+
expect(getHostRefFields(def)).toEqual(['wallId']);
|
|
114
|
+
});
|
|
115
|
+
test('defaults to an empty array when none declared', () => {
|
|
116
|
+
const def = makeDefinition('shelf');
|
|
117
|
+
expect(getHostRefFields(def)).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('isDrawnViaTool', () => {
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
nodeRegistry._reset();
|
|
123
|
+
});
|
|
124
|
+
test('true when capability set', () => {
|
|
125
|
+
const def = makeDefinition('fence', { capabilities: { drawTool: true } });
|
|
126
|
+
expect(isDrawnViaTool(def)).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
test('false when unset or not exactly true', () => {
|
|
129
|
+
expect(isDrawnViaTool(makeDefinition('column'))).toBe(false);
|
|
130
|
+
expect(isDrawnViaTool(makeDefinition('off', { capabilities: { drawTool: false } }))).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
test('isDrawnViaToolKind looks up the registry', () => {
|
|
133
|
+
registerNode(makeDefinition('fence', { capabilities: { drawTool: true } }));
|
|
134
|
+
expect(isDrawnViaToolKind('fence')).toBe(true);
|
|
135
|
+
expect(isDrawnViaToolKind('unknown')).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe('loadPlugin', () => {
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
nodeRegistry._reset();
|
|
141
|
+
});
|
|
142
|
+
test('registers all nodes from a plugin', async () => {
|
|
143
|
+
const plugin = {
|
|
144
|
+
id: 'test:plugin',
|
|
145
|
+
apiVersion: 1,
|
|
146
|
+
nodes: [makeDefinition('a'), makeDefinition('b')],
|
|
147
|
+
};
|
|
148
|
+
await loadPlugin(plugin);
|
|
149
|
+
expect(nodeRegistry.size).toBe(2);
|
|
150
|
+
expect(nodeRegistry.has('a')).toBe(true);
|
|
151
|
+
expect(nodeRegistry.has('b')).toBe(true);
|
|
152
|
+
expect(getNodePluginId('a')).toBe('test:plugin');
|
|
153
|
+
expect(getNodePluginId('b')).toBe('test:plugin');
|
|
154
|
+
});
|
|
155
|
+
test('enables plugin kinds only when the project has the plugin installed', async () => {
|
|
156
|
+
await loadPlugin({ id: 'test:plugin', apiVersion: 1, nodes: [makeDefinition('plugin:node')] });
|
|
157
|
+
expect(isNodeKindEnabled('plugin:node', [])).toBe(false);
|
|
158
|
+
expect(isNodeKindEnabled('plugin:node', ['test:plugin'])).toBe(true);
|
|
159
|
+
expect(isNodeKindEnabled('plugin:node')).toBe(true);
|
|
160
|
+
expect(isNodeKindEnabled('host:node', [])).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
test('keeps built-in plugin kinds enabled independently of project installs', async () => {
|
|
163
|
+
await loadPlugin({ id: 'pascal:core', apiVersion: 1, nodes: [makeDefinition('wall')] });
|
|
164
|
+
expect(isNodeKindEnabled('wall', [])).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
test('handles plugin with no nodes', async () => {
|
|
167
|
+
await loadPlugin({ id: 'empty', apiVersion: 1 });
|
|
168
|
+
expect(nodeRegistry.size).toBe(0);
|
|
169
|
+
});
|
|
170
|
+
test('handles plugin with empty nodes array', async () => {
|
|
171
|
+
await loadPlugin({ id: 'empty', apiVersion: 1, nodes: [] });
|
|
172
|
+
expect(nodeRegistry.size).toBe(0);
|
|
173
|
+
});
|
|
174
|
+
test('throws on apiVersion mismatch', async () => {
|
|
175
|
+
const plugin = {
|
|
176
|
+
id: 'old-plugin',
|
|
177
|
+
apiVersion: 99,
|
|
178
|
+
nodes: [],
|
|
179
|
+
};
|
|
180
|
+
await expect(loadPlugin(plugin)).rejects.toThrow(/apiVersion/);
|
|
181
|
+
});
|
|
182
|
+
test('propagates duplicate-kind error from a single plugin in production', async () => {
|
|
183
|
+
const plugin = {
|
|
184
|
+
id: 'broken',
|
|
185
|
+
apiVersion: 1,
|
|
186
|
+
nodes: [makeDefinition('dup'), makeDefinition('dup')],
|
|
187
|
+
};
|
|
188
|
+
await inProduction(() => expect(loadPlugin(plugin)).rejects.toThrow(/duplicate node kind/));
|
|
189
|
+
});
|
|
190
|
+
test('propagates duplicate-kind error across plugins in production', async () => {
|
|
191
|
+
await inProduction(async () => {
|
|
192
|
+
await loadPlugin({ id: 'a', apiVersion: 1, nodes: [makeDefinition('shared')] });
|
|
193
|
+
await expect(loadPlugin({ id: 'b', apiVersion: 1, nodes: [makeDefinition('shared')] })).rejects.toThrow(/duplicate node kind/);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AnyNode, AnyNodeId } from '../schema/types';
|
|
2
|
+
import type { SceneApi } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Spatial neighbor query — given a node and a set of kinds, returns IDs of
|
|
5
|
+
* neighboring nodes of those kinds. The runtime provides this from
|
|
6
|
+
* `spatialGridManager`; tests can pass a stub.
|
|
7
|
+
*/
|
|
8
|
+
export type SpatialQuery = (node: AnyNode, kinds: readonly string[]) => Iterable<AnyNodeId>;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the IDs of nodes that share `node` as a parent. The runtime can
|
|
11
|
+
* pass an optimized index; the default fallback iterates the scene.
|
|
12
|
+
*/
|
|
13
|
+
export type ChildQuery = (node: AnyNode) => Iterable<AnyNodeId>;
|
|
14
|
+
export type CascadeContext = {
|
|
15
|
+
scene: SceneApi;
|
|
16
|
+
/** Optional: bounded spatial neighbor lookup. Required for `affectsSpatial`. */
|
|
17
|
+
spatialQuery?: SpatialQuery;
|
|
18
|
+
/** Optional: children-by-parent lookup. Defaults to iterating the scene. */
|
|
19
|
+
childQuery?: ChildQuery;
|
|
20
|
+
/** Safety cap on cascade depth — guards against bad data and pathological
|
|
21
|
+
* registry configurations. Default 16 (deeper than the maxHostDepth of 6). */
|
|
22
|
+
maxDepth?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Walks the relations graph from one dirty node and returns the full set of
|
|
26
|
+
* IDs (including the starting one) that should be marked dirty. Pure — does
|
|
27
|
+
* NOT call `scene.markDirty`; callers iterate the result.
|
|
28
|
+
*
|
|
29
|
+
* Phase 1 implements:
|
|
30
|
+
* - `hosts`: marks children whose `type` matches the kind list
|
|
31
|
+
* - `affectsSpatial`: marks neighbors found via `spatialQuery`
|
|
32
|
+
*
|
|
33
|
+
* Phase 3 will add `linkedBy: 'endpoint-match'` for wall corner propagation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function cascadeDirty(startId: AnyNodeId, ctx: CascadeContext): Set<AnyNodeId>;
|
|
36
|
+
/**
|
|
37
|
+
* Recursively collects every descendant of a node, plus the node itself.
|
|
38
|
+
* Used by `cascadeDelete: 'descendants'` and by tools that need to delete a
|
|
39
|
+
* subtree atomically. Independent of dirty-marking — pure traversal.
|
|
40
|
+
*/
|
|
41
|
+
export declare function collectDescendants(startId: AnyNodeId, ctx: Pick<CascadeContext, 'scene' | 'childQuery' | 'maxDepth'>): Set<AnyNodeId>;
|
|
42
|
+
//# sourceMappingURL=relations-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations-resolver.d.ts","sourceRoot":"","sources":["../../src/registry/relations-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAA;AAE3F;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAA;AAE/D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,QAAQ,CAAA;IACf,gFAAgF;IAChF,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,4EAA4E;IAC5E,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB;kFAC8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,cAAc,GAAG,GAAG,CAAC,SAAS,CAAC,CAKpF;AA+CD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,EAClB,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,YAAY,GAAG,UAAU,CAAC,GAC7D,GAAG,CAAC,SAAS,CAAC,CAKhB"}
|