@pascal-app/core 0.7.0 → 0.9.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.
- package/dist/events/bus.d.ts +47 -18
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +33 -26
- 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 +273 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
- 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 +91 -18
- package/dist/index.d.ts +22 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- 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/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 +30 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +222 -41
- 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 +77 -0
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +1957 -141
- 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 +321 -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 +93 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +201 -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 +183 -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 +119 -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 +133 -0
- package/dist/registry/types.d.ts +1420 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/asset-url.test.js +0 -4
- package/dist/schema/index.d.ts +17 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +17 -3
- package/dist/schema/material.d.ts +12 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +10 -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/ceiling.d.ts +3 -1
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +1 -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 +598 -6
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +586 -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 +1 -1
- 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/elevator.d.ts +105 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +54 -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 +3 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +4 -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/item.d.ts +16 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +21 -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 +2 -0
- package/dist/schema/nodes/ridge-vent.d.ts +67 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +30 -0
- package/dist/schema/nodes/roof-segment.d.ts +198 -4
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +254 -3
- package/dist/schema/nodes/roof.d.ts +6 -6
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +9 -21
- package/dist/schema/nodes/shelf.d.ts +105 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +84 -0
- package/dist/schema/nodes/site.d.ts +1 -116
- 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 +3 -1
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -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 +1 -1
- package/dist/schema/nodes/stair.d.ts +6 -6
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +9 -7
- 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 +3 -3
- package/dist/schema/nodes/window.d.ts +1 -1
- package/dist/schema/types.d.ts +1448 -236
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +26 -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 +283 -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 +126 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +169 -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 +80 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +122 -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 +199 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -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/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 +72 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +120 -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 +95 -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 +499 -20
- 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/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-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.d.ts +11 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +207 -11
- 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/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 +168 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1777 -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 +183 -185
- package/dist/systems/stair/stair-opening-sync.test.js +385 -4
- 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-curve.d.ts +1 -1
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +1 -1
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +2 -6
- 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 +6 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +6 -4
- 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 +59 -0
- package/dist/validation/validate-build-json.d.ts +43 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +227 -0
- package/package.json +11 -3
- package/dist/materials.d.ts +0 -10
- package/dist/materials.d.ts.map +0 -1
- package/dist/materials.js +0 -22
- package/dist/systems/ceiling/ceiling-system.d.ts +0 -8
- package/dist/systems/ceiling/ceiling-system.d.ts.map +0 -1
- package/dist/systems/ceiling/ceiling-system.js +0 -92
- package/dist/systems/door/door-system.d.ts +0 -2
- package/dist/systems/door/door-system.d.ts.map +0 -1
- package/dist/systems/door/door-system.js +0 -195
- package/dist/systems/fence/fence-system.d.ts +0 -2
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -187
- package/dist/systems/item/item-system.d.ts +0 -2
- package/dist/systems/item/item-system.d.ts.map +0 -1
- package/dist/systems/item/item-system.js +0 -48
- package/dist/systems/roof/roof-system.d.ts +0 -16
- package/dist/systems/roof/roof-system.d.ts.map +0 -1
- package/dist/systems/roof/roof-system.js +0 -797
- package/dist/systems/slab/slab-system.d.ts +0 -8
- package/dist/systems/slab/slab-system.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.js +0 -214
- package/dist/systems/stair/stair-system.d.ts +0 -2
- package/dist/systems/stair/stair-system.d.ts.map +0 -1
- package/dist/systems/stair/stair-system.js +0 -776
- package/dist/systems/wall/wall-system.d.ts +0 -12
- package/dist/systems/wall/wall-system.d.ts.map +0 -1
- package/dist/systems/wall/wall-system.js +0 -455
- package/dist/systems/window/window-system.d.ts +0 -2
- package/dist/systems/window/window-system.d.ts.map +0 -1
- package/dist/systems/window/window-system.js +0 -131
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AA8EtE,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;;;;;;;;;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;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvD;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,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,CAS9D;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;AAIrD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnD"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
const HOST_API_VERSION = 1;
|
|
2
|
+
// True in dev / test builds, false in production. Tries Vite's
|
|
3
|
+
// `import.meta.env.DEV` first (the editor app's bundler) and falls back
|
|
4
|
+
// to `process.env.NODE_ENV !== 'production'` for Node test runners.
|
|
5
|
+
function isDevMode() {
|
|
6
|
+
try {
|
|
7
|
+
const meta = import.meta;
|
|
8
|
+
if (typeof meta?.env?.DEV === 'boolean')
|
|
9
|
+
return meta.env.DEV;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// import.meta unavailable in some CJS contexts — fall through.
|
|
13
|
+
}
|
|
14
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV) {
|
|
15
|
+
return process.env.NODE_ENV !== 'production';
|
|
16
|
+
}
|
|
17
|
+
// No environment signal — be safe and treat as production.
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
class NodeRegistryImpl {
|
|
21
|
+
defs = new Map();
|
|
22
|
+
has(kind) {
|
|
23
|
+
return this.defs.has(kind);
|
|
24
|
+
}
|
|
25
|
+
get(kind) {
|
|
26
|
+
return this.defs.get(kind);
|
|
27
|
+
}
|
|
28
|
+
entries() {
|
|
29
|
+
return this.defs.entries();
|
|
30
|
+
}
|
|
31
|
+
schemas() {
|
|
32
|
+
return Array.from(this.defs.values(), (d) => d.schema);
|
|
33
|
+
}
|
|
34
|
+
get size() {
|
|
35
|
+
return this.defs.size;
|
|
36
|
+
}
|
|
37
|
+
// Internal — exposed via registerNode below.
|
|
38
|
+
_register(def) {
|
|
39
|
+
if (typeof def.kind !== 'string' || def.kind.length === 0) {
|
|
40
|
+
throw new Error('[registry] NodeDefinition.kind must be a non-empty string');
|
|
41
|
+
}
|
|
42
|
+
if (typeof def.schemaVersion !== 'number' || def.schemaVersion < 1) {
|
|
43
|
+
throw new Error(`[registry] NodeDefinition.schemaVersion must be a positive integer (kind: "${def.kind}")`);
|
|
44
|
+
}
|
|
45
|
+
// Duplicate-kind handling depends on environment:
|
|
46
|
+
// - **Production**: throw. The plugin-authoring contract
|
|
47
|
+
// (`wiki/architecture/plugin-authoring.md`) guarantees that two
|
|
48
|
+
// plugins shipping `kind: 'couch'` is a startup-time error, not
|
|
49
|
+
// a silent overwrite — collisions need to be visible.
|
|
50
|
+
// - **Dev (HMR)**: replace with a warning. Saving `def.ts` would
|
|
51
|
+
// otherwise either crash on re-execute or skip it entirely,
|
|
52
|
+
// leaving stale descriptors pinned in memory.
|
|
53
|
+
if (this.defs.has(def.kind)) {
|
|
54
|
+
if (isDevMode()) {
|
|
55
|
+
console.warn(`[registry] re-registering node kind "${def.kind}" (HMR)`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw new Error(`[registry] duplicate node kind: "${def.kind}" already registered`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.defs.set(def.kind, def);
|
|
62
|
+
}
|
|
63
|
+
// Test-only — clears the registry. Not exported from the package barrel.
|
|
64
|
+
_reset() {
|
|
65
|
+
this.defs.clear();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export const nodeRegistry = new NodeRegistryImpl();
|
|
69
|
+
export function registerNode(def) {
|
|
70
|
+
nodeRegistry._register(def);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the set of registered kinds whose definition declares the
|
|
74
|
+
* `selectable` capability. Callers that maintain hardcoded "selectable kinds"
|
|
75
|
+
* lists (SelectionManager, FloatingActionMenu) should concat this with their
|
|
76
|
+
* legacy entries instead of editing the hardcoded list per migration.
|
|
77
|
+
*
|
|
78
|
+
* Phase 6 deletes the hardcoded lists entirely and uses this function as the
|
|
79
|
+
* single source of truth. For now it's additive over the legacy lists so the
|
|
80
|
+
* existing kinds keep working unchanged.
|
|
81
|
+
*/
|
|
82
|
+
export function getSelectableKinds() {
|
|
83
|
+
const result = [];
|
|
84
|
+
for (const [kind, def] of nodeRegistry.entries()) {
|
|
85
|
+
if (def.capabilities.selectable !== undefined) {
|
|
86
|
+
result.push(kind);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns true when the kind is declared selectable in the registry. Use
|
|
93
|
+
* in expression chains like `if (node.type === 'wall' || isRegistrySelectable(node.type))`.
|
|
94
|
+
*/
|
|
95
|
+
export function isRegistrySelectable(kind) {
|
|
96
|
+
return nodeRegistry.get(kind)?.capabilities.selectable !== undefined;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Kinds whose `def.floorplanScope` matches the requested scope. Used by
|
|
100
|
+
* `FloorplanRegistryLayer` to discover building-scoped kinds (e.g.
|
|
101
|
+
* elevator) without hardcoding kind names in the editor layer. `'level'`
|
|
102
|
+
* is the default, so `kindsWithFloorplanScope('level')` includes kinds
|
|
103
|
+
* that didn't set the field at all.
|
|
104
|
+
*/
|
|
105
|
+
export function kindsWithFloorplanScope(scope) {
|
|
106
|
+
const result = [];
|
|
107
|
+
for (const [kind, def] of nodeRegistry.entries()) {
|
|
108
|
+
const declared = def.floorplanScope ?? 'level';
|
|
109
|
+
if (declared === scope)
|
|
110
|
+
result.push(kind);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns true when the kind is movable from a 2D floor-plan handle —
|
|
116
|
+
* either via `capabilities.movable`, an explicit
|
|
117
|
+
* `def.floorplanMoveTarget`, or an `affordanceTools.move` 3D mover that
|
|
118
|
+
* the floating action menu can engage. Replaces the kind-name ternary
|
|
119
|
+
* chain in `floating-action-menu.tsx`.
|
|
120
|
+
*/
|
|
121
|
+
export function isRegistryMovable(kind) {
|
|
122
|
+
const def = nodeRegistry.get(kind);
|
|
123
|
+
if (!def)
|
|
124
|
+
return false;
|
|
125
|
+
if (def.capabilities.movable !== undefined)
|
|
126
|
+
return true;
|
|
127
|
+
if (def.floorplanMoveTarget !== undefined)
|
|
128
|
+
return true;
|
|
129
|
+
if (def.affordanceTools?.move !== undefined)
|
|
130
|
+
return true;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Whether the kind can be saved as a reusable preset. Default: an
|
|
135
|
+
* explicit `capabilities.presettable` boolean wins; otherwise the kind
|
|
136
|
+
* is presettable iff it declares `def.parametrics`. Read by host apps
|
|
137
|
+
* (community shell) to gate "save as preset" UI on a selection.
|
|
138
|
+
*/
|
|
139
|
+
export function isPresettable(def) {
|
|
140
|
+
if (typeof def.capabilities.presettable === 'boolean') {
|
|
141
|
+
return def.capabilities.presettable;
|
|
142
|
+
}
|
|
143
|
+
return def.parametrics !== undefined;
|
|
144
|
+
}
|
|
145
|
+
export function isPresettableKind(kind) {
|
|
146
|
+
const def = nodeRegistry.get(kind);
|
|
147
|
+
return def ? isPresettable(def) : false;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Names of schema fields on `def` that are host references (`wallId`,
|
|
151
|
+
* `wallT`, etc.). Read by host apps at preset-save time to strip these
|
|
152
|
+
* from the stored payload — see `def.capabilities.hostRefFields` docs.
|
|
153
|
+
* Returns an empty array for kinds that don't declare any.
|
|
154
|
+
*/
|
|
155
|
+
export function getHostRefFields(def) {
|
|
156
|
+
return def.capabilities.hostRefFields ?? [];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Whether instances of this kind are created by drawing with a build tool
|
|
160
|
+
* (tool id === node `type`) rather than dropping a finished instance. Read
|
|
161
|
+
* by host apps to route preset placement of such kinds through
|
|
162
|
+
* `setToolDefaults(type, params)` + `setTool(type)` — see
|
|
163
|
+
* `def.capabilities.drawTool` docs.
|
|
164
|
+
*/
|
|
165
|
+
export function isDrawnViaTool(def) {
|
|
166
|
+
return def.capabilities.drawTool === true;
|
|
167
|
+
}
|
|
168
|
+
export function isDrawnViaToolKind(kind) {
|
|
169
|
+
const def = nodeRegistry.get(kind);
|
|
170
|
+
return def ? isDrawnViaTool(def) : false;
|
|
171
|
+
}
|
|
172
|
+
export async function loadPlugin(plugin) {
|
|
173
|
+
if (plugin.apiVersion !== HOST_API_VERSION) {
|
|
174
|
+
throw new Error(`[registry] plugin "${plugin.id}" requires apiVersion ${plugin.apiVersion}; host supports ${HOST_API_VERSION}`);
|
|
175
|
+
}
|
|
176
|
+
for (const def of plugin.nodes ?? []) {
|
|
177
|
+
registerNode(def);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
let pluginDiscovery = async () => [];
|
|
181
|
+
/**
|
|
182
|
+
* Replace the plugin discovery implementation. Call once at app startup
|
|
183
|
+
* before {@link discoverPlugins} is invoked (bootstrap order matters).
|
|
184
|
+
*
|
|
185
|
+
* The contract is intentionally minimal — just "return a list of
|
|
186
|
+
* plugins to load." The loader can be a static `import.meta.glob`, a
|
|
187
|
+
* `fetch` against a registry endpoint, a worker IPC, etc. Each returned
|
|
188
|
+
* plugin still goes through {@link loadPlugin} so the same API-version
|
|
189
|
+
* gate + duplicate-kind protection applies.
|
|
190
|
+
*/
|
|
191
|
+
export function setPluginDiscovery(fn) {
|
|
192
|
+
pluginDiscovery = fn;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Run the active plugin discovery and return the discovered plugins.
|
|
196
|
+
* Bootstrap code is expected to call this after `loadPlugin(builtinPlugin)`
|
|
197
|
+
* and then `await loadPlugin(...)` each result in order.
|
|
198
|
+
*/
|
|
199
|
+
export function discoverPlugins() {
|
|
200
|
+
return pluginDiscovery();
|
|
201
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/registry/registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getHostRefFields, isDrawnViaTool, isDrawnViaToolKind, 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
|
+
});
|
|
153
|
+
test('handles plugin with no nodes', async () => {
|
|
154
|
+
await loadPlugin({ id: 'empty', apiVersion: 1 });
|
|
155
|
+
expect(nodeRegistry.size).toBe(0);
|
|
156
|
+
});
|
|
157
|
+
test('handles plugin with empty nodes array', async () => {
|
|
158
|
+
await loadPlugin({ id: 'empty', apiVersion: 1, nodes: [] });
|
|
159
|
+
expect(nodeRegistry.size).toBe(0);
|
|
160
|
+
});
|
|
161
|
+
test('throws on apiVersion mismatch', async () => {
|
|
162
|
+
const plugin = {
|
|
163
|
+
id: 'old-plugin',
|
|
164
|
+
apiVersion: 99,
|
|
165
|
+
nodes: [],
|
|
166
|
+
};
|
|
167
|
+
await expect(loadPlugin(plugin)).rejects.toThrow(/apiVersion/);
|
|
168
|
+
});
|
|
169
|
+
test('propagates duplicate-kind error from a single plugin in production', async () => {
|
|
170
|
+
const plugin = {
|
|
171
|
+
id: 'broken',
|
|
172
|
+
apiVersion: 1,
|
|
173
|
+
nodes: [makeDefinition('dup'), makeDefinition('dup')],
|
|
174
|
+
};
|
|
175
|
+
await inProduction(() => expect(loadPlugin(plugin)).rejects.toThrow(/duplicate node kind/));
|
|
176
|
+
});
|
|
177
|
+
test('propagates duplicate-kind error across plugins in production', async () => {
|
|
178
|
+
await inProduction(async () => {
|
|
179
|
+
await loadPlugin({ id: 'a', apiVersion: 1, nodes: [makeDefinition('shared')] });
|
|
180
|
+
await expect(loadPlugin({ id: 'b', apiVersion: 1, nodes: [makeDefinition('shared')] })).rejects.toThrow(/duplicate node kind/);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { nodeRegistry } from './registry';
|
|
2
|
+
const DEFAULT_MAX_DEPTH = 16;
|
|
3
|
+
/**
|
|
4
|
+
* Walks the relations graph from one dirty node and returns the full set of
|
|
5
|
+
* IDs (including the starting one) that should be marked dirty. Pure — does
|
|
6
|
+
* NOT call `scene.markDirty`; callers iterate the result.
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 implements:
|
|
9
|
+
* - `hosts`: marks children whose `type` matches the kind list
|
|
10
|
+
* - `affectsSpatial`: marks neighbors found via `spatialQuery`
|
|
11
|
+
*
|
|
12
|
+
* Phase 3 will add `linkedBy: 'endpoint-match'` for wall corner propagation.
|
|
13
|
+
*/
|
|
14
|
+
export function cascadeDirty(startId, ctx) {
|
|
15
|
+
const result = new Set();
|
|
16
|
+
const maxDepth = ctx.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
17
|
+
walk(startId, ctx, result, 0, maxDepth);
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
function walk(id, ctx, result, depth, maxDepth) {
|
|
21
|
+
if (result.has(id) || depth > maxDepth)
|
|
22
|
+
return;
|
|
23
|
+
result.add(id);
|
|
24
|
+
const node = ctx.scene.get(id);
|
|
25
|
+
if (!node)
|
|
26
|
+
return;
|
|
27
|
+
const def = nodeRegistry.get(node.type);
|
|
28
|
+
if (!def?.relations)
|
|
29
|
+
return;
|
|
30
|
+
const { hosts, affectsSpatial } = def.relations;
|
|
31
|
+
if (hosts && hosts.length > 0) {
|
|
32
|
+
const childIds = ctx.childQuery ? ctx.childQuery(node) : defaultChildIds(node, ctx.scene);
|
|
33
|
+
for (const childId of childIds) {
|
|
34
|
+
const child = ctx.scene.get(childId);
|
|
35
|
+
if (child && hosts.includes(child.type)) {
|
|
36
|
+
walk(childId, ctx, result, depth + 1, maxDepth);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (affectsSpatial && affectsSpatial.length > 0 && ctx.spatialQuery) {
|
|
41
|
+
for (const neighborId of ctx.spatialQuery(node, affectsSpatial)) {
|
|
42
|
+
walk(neighborId, ctx, result, depth + 1, maxDepth);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fallback children lookup that reads the node's `children: AnyNodeId[]`
|
|
48
|
+
* field if present. Most parametric nodes carry one; nodes that don't will
|
|
49
|
+
* need a `childQuery` override on the context.
|
|
50
|
+
*/
|
|
51
|
+
function defaultChildIds(node, _scene) {
|
|
52
|
+
const maybeChildren = node.children;
|
|
53
|
+
return Array.isArray(maybeChildren) ? maybeChildren : [];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Recursively collects every descendant of a node, plus the node itself.
|
|
57
|
+
* Used by `cascadeDelete: 'descendants'` and by tools that need to delete a
|
|
58
|
+
* subtree atomically. Independent of dirty-marking — pure traversal.
|
|
59
|
+
*/
|
|
60
|
+
export function collectDescendants(startId, ctx) {
|
|
61
|
+
const result = new Set();
|
|
62
|
+
const maxDepth = ctx.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
63
|
+
walkDescendants(startId, ctx, result, 0, maxDepth);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function walkDescendants(id, ctx, result, depth, maxDepth) {
|
|
67
|
+
if (result.has(id) || depth > maxDepth)
|
|
68
|
+
return;
|
|
69
|
+
result.add(id);
|
|
70
|
+
const node = ctx.scene.get(id);
|
|
71
|
+
if (!node)
|
|
72
|
+
return;
|
|
73
|
+
const childIds = ctx.childQuery ? ctx.childQuery(node) : defaultChildIds(node, ctx.scene);
|
|
74
|
+
for (const childId of childIds) {
|
|
75
|
+
walkDescendants(childId, ctx, result, depth + 1, maxDepth);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations-resolver.test.d.ts","sourceRoot":"","sources":["../../src/registry/relations-resolver.test.ts"],"names":[],"mappings":""}
|