@relu-interactives/spatial-ecs 0.2.20 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/{System-BjwOBx81.d.cts → System-CYWZ_Zx5.d.cts} +9 -4
  2. package/dist/{System-BjwOBx81.d.ts → System-CYWZ_Zx5.d.ts} +9 -4
  3. package/dist/{Video-B8yVI1tF.d.cts → Video-C1IOat7L.d.ts} +38 -9
  4. package/dist/{Video-B9z5AtIf.d.ts → Video-ppAXaKt0.d.cts} +38 -9
  5. package/dist/{chunk-HUHN6IFD.js → chunk-52CHDPRQ.js} +118 -92
  6. package/dist/chunk-52CHDPRQ.js.map +1 -0
  7. package/dist/{chunk-67MFQDHX.js → chunk-GZ2NKSMV.js} +609 -90
  8. package/dist/chunk-GZ2NKSMV.js.map +1 -0
  9. package/dist/{chunk-Z6OMYEXU.js → chunk-JHCPRVBC.js} +34 -5
  10. package/dist/chunk-JHCPRVBC.js.map +1 -0
  11. package/dist/{chunk-DY4ETMP2.js → chunk-WKGCC7YL.js} +3 -3
  12. package/dist/{chunk-DY4ETMP2.js.map → chunk-WKGCC7YL.js.map} +1 -1
  13. package/dist/components/index.cjs +31 -2
  14. package/dist/components/index.cjs.map +1 -1
  15. package/dist/components/index.d.cts +3 -3
  16. package/dist/components/index.d.ts +3 -3
  17. package/dist/components/index.js +1 -1
  18. package/dist/docs/guide/authoring-a-behaviour.md +3 -40
  19. package/dist/docs/guide/authoring-a-script-object.md +102 -0
  20. package/dist/docs/guide/behaviour-lifecycle.md +4 -4
  21. package/dist/docs/guide/prefabs.md +24 -5
  22. package/dist/docs/guide/spawning/script-object.md +36 -0
  23. package/dist/docs/guide/systems/_sidebar.json +4 -0
  24. package/dist/docs/guide/systems/script-behaviour-system.md +1 -17
  25. package/dist/docs/guide/systems/script-object-system.md +44 -0
  26. package/dist/factories/index.cjs +164 -117
  27. package/dist/factories/index.cjs.map +1 -1
  28. package/dist/factories/index.d.cts +5 -3
  29. package/dist/factories/index.d.ts +5 -3
  30. package/dist/factories/index.js +3 -3
  31. package/dist/{index-LHLKpJhZ.d.cts → index-Cu2Pt7wN.d.cts} +33 -14
  32. package/dist/{index-BBI2YdCu.d.ts → index-DdiWu1b6.d.ts} +33 -14
  33. package/dist/index.cjs +1132 -302
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +219 -32
  36. package/dist/index.d.ts +219 -32
  37. package/dist/index.js +254 -11
  38. package/dist/index.js.map +1 -1
  39. package/dist/systems/index.cjs +429 -43
  40. package/dist/systems/index.cjs.map +1 -1
  41. package/dist/systems/index.d.cts +44 -3
  42. package/dist/systems/index.d.ts +44 -3
  43. package/dist/systems/index.js +3 -3
  44. package/dist/types.d.cts +1 -1
  45. package/dist/types.d.ts +1 -1
  46. package/dist/worldSnapshotUtils.d.cts +1 -1
  47. package/dist/worldSnapshotUtils.d.ts +1 -1
  48. package/package.json +1 -1
  49. package/dist/chunk-67MFQDHX.js.map +0 -1
  50. package/dist/chunk-HUHN6IFD.js.map +0 -1
  51. package/dist/chunk-Z6OMYEXU.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,11 +1,11 @@
1
- import { k as EcsSystem, C as Component, W as WorldEntityView, T as TransformComponent, l as World, S as SpawnPrefabSource, m as SpawnPrefabOptions, n as ComponentClass, o as ParentLookup, p as EcsEntityKind, O as ObjectManagementSystem, q as EntityId, r as SubtreeSnapshot, s as ExternalRefWarning } from './System-BjwOBx81.cjs';
2
- export { A as AssetInput, t as CheckboxInput, u as ColorInput, v as ComponentFactory, w as ComponentInput, x as ComponentKind, y as DropdownInput, z as DropdownOption, B as EcsComponentFactoryKind, F as EcsEntityEditorKind, G as EntityInput, H as FloatInput, I as InputSystem, J as IntersectionResult, K as KeyboardKey, L as KeyboardKeys, M as MouseButton, N as PREFAB_FILE_VERSION, Q as PrefabAsset, R as RemapSnapshotOptions, U as RenderSystem, X as SelectionListener, Y as SliderInput, Z as TextInput, _ as ToggleInput, $ as TransformSyncSystem, a0 as Vec3, a1 as Vector2Input, a2 as Vector3Input, a3 as clearPrefabCache, a4 as despawnPrefab, a5 as loadPrefab, a6 as parsePrefabAsset, a7 as remapSnapshotEntityIds, a8 as setPrefabAssetUrlResolver, a9 as spawnPrefab } from './System-BjwOBx81.cjs';
1
+ import { k as EcsSystem, C as Component, W as WorldEntityView, T as TransformComponent, l as World, S as SpawnPrefabSource, m as SpawnPrefabOptions, n as ComponentClass, o as ParentLookup, p as EcsEntityKind, O as ObjectManagementSystem, q as SubtreeSnapshot, r as EntityId, s as ExternalRefWarning } from './System-CYWZ_Zx5.cjs';
2
+ export { A as AssetInput, t as CheckboxInput, u as ColorInput, v as ComponentFactory, w as ComponentInput, x as ComponentKind, y as DropdownInput, z as DropdownOption, B as EcsComponentFactoryKind, F as EcsEntityEditorKind, G as EntityInput, H as FloatInput, I as InputSystem, J as IntersectionResult, K as KeyboardKey, L as KeyboardKeys, M as MouseButton, N as PREFAB_FILE_VERSION, Q as PREFAB_FILE_VERSION_NESTED, R as PrefabAsset, U as RemapSnapshotOptions, X as RenderSystem, Y as SelectionListener, Z as SliderInput, _ as TextInput, $ as ToggleInput, a0 as TransformSyncSystem, a1 as Vec3, a2 as Vector2Input, a3 as Vector3Input, a4 as clearPrefabCache, a5 as despawnPrefab, a6 as loadPrefab, a7 as parsePrefabAsset, a8 as prefabFileVersionFor, a9 as remapSnapshotEntityIds, aa as setPrefabAssetUrlResolver, ab as spawnPrefab } from './System-CYWZ_Zx5.cjs';
3
3
  import * as THREE from 'three';
4
- import { O as Object3DRef } from './Video-B8yVI1tF.cjs';
5
- export { a3 as AnimationStateCallbacks, a4 as AnimationStateMachine, a5 as AnimationStateManager, a6 as StateMachine } from './Video-B8yVI1tF.cjs';
6
- export { i as CoreComponents } from './index-LHLKpJhZ.cjs';
7
- export { AnimationSystem, AudioSystem, CameraSyncSystem, EnvironmentSyncSystem, InstancedMeshSystem, LightSyncSystem, MaterialSyncSystem, MeshGeometrySystem, ObjectVisibilitySystem, PhysicsSyncSystem, PostprocessingSyncSystem, ScriptBehaviourSystem, SelectionContext, VideoSystem } from './systems/index.cjs';
8
- export { createAnimationComponent, createAudioComponent, createCameraComponent, createEntityTypeComponent, createEnvironmentComponent, createImageComponent, createImageTargetAnchorComponent, createImageTargetComponent, createInstanceSlotComponent, createInstancedMeshComponent, createLightComponent, createMaterialComponent, createMeshComponent, createMeshGeometryComponent, createModelComponent, createNameComponent, createObject3DRefComponent, createParentIdComponent, createPhysicsComponent, createPostprocessingComponent, createScriptComponent, createSelectableComponent, createSelectionTransformStateComponent, createSpriteComponent, createTransformComponent, createVideoComponent, registerDefaultComponentFactories, registerDefaultFactories } from './factories/index.cjs';
4
+ import { O as Object3DRef } from './Video-ppAXaKt0.cjs';
5
+ export { a6 as AnimationStateCallbacks, a7 as AnimationStateMachine, a8 as AnimationStateManager, a9 as StateMachine } from './Video-ppAXaKt0.cjs';
6
+ export { i as CoreComponents } from './index-Cu2Pt7wN.cjs';
7
+ export { AnimationSystem, AudioSystem, CameraSyncSystem, EnvironmentSyncSystem, InstancedMeshSystem, LightSyncSystem, MaterialSyncSystem, MeshGeometrySystem, ModuleLoader, ObjectVisibilitySystem, PhysicsSyncSystem, PostprocessingSyncSystem, SCRIPT_OBJECT_ROOT_FLAG, ScriptBehaviourSystem, ScriptObjectSystem, SelectionContext, VideoSystem } from './systems/index.cjs';
8
+ export { createAnimationComponent, createAudioComponent, createCameraComponent, createEntityTypeComponent, createEnvironmentComponent, createImageComponent, createImageTargetAnchorComponent, createImageTargetComponent, createInstanceSlotComponent, createInstancedMeshComponent, createLightComponent, createMaterialComponent, createMeshComponent, createMeshGeometryComponent, createModelComponent, createNameComponent, createObject3DRefComponent, createParentIdComponent, createPhysicsComponent, createPostprocessingComponent, createScriptComponent, createScriptObjectComponent, createSelectableComponent, createSelectionTransformStateComponent, createSpriteComponent, createTransformComponent, createVideoComponent, registerDefaultComponentFactories, registerDefaultFactories } from './factories/index.cjs';
9
9
  import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
10
10
  import '@dimforge/rapier3d-compat';
11
11
 
@@ -25,15 +25,10 @@ interface CustomEcsSystem extends EcsSystem {
25
25
  * Override {@link Behaviour.onUpdate} for per-frame logic — never override
26
26
  * {@link Behaviour.update}, which is the engine-managed entry point.
27
27
  *
28
- * **Editor scripts**: By default, `Behaviour` subclasses do not run inside the
29
- * editor. Set `executeInEditor: true` on the script entry in the inspector to
30
- * enable the full lifecycle (`init` → `onUpdate` → `dispose`) while the editor
31
- * is open. This gives direct access to the Three.js scene, camera, and renderer
32
- * via {@link World.getScene}, {@link World.getCamera}, and
33
- * {@link World.getRenderer} — useful for custom shaders, procedural geometry,
34
- * grass, and any Three.js feature the editor's object palette does not expose.
35
- * Avoid mutating ECS component state every frame in editor mode, as it creates
36
- * undo history entries on every tick.
28
+ * **Editor vs runtime**: `Behaviour` subclasses run ONLY at preview/runtime —
29
+ * they never execute in the editor. For a visual that must render and animate
30
+ * inside the editor (custom shaders, procedural geometry, grass, anything the
31
+ * object palette can't produce), use {@link ScriptObject} instead.
37
32
  *
38
33
  * The runtime recognizes a script by its **default export** plus the
39
34
  * inherited static marker `__reluBehaviourClass`. Do not override the marker.
@@ -248,21 +243,13 @@ declare class Behaviour implements CustomEcsSystem {
248
243
  /**
249
244
  * Called once before the first {@link Behaviour.onUpdate}. Override to
250
245
  * default and validate {@link Behaviour.data}, cache references, or set
251
- * initial state.
252
- *
253
- * @remarks
254
- * Also called in editor mode when `executeInEditor` is `true` on the script
255
- * entry.
246
+ * initial state. Runs at preview/runtime only — never in the editor.
256
247
  */
257
248
  protected init(): void;
258
249
  /**
259
250
  * Called every frame while the script is enabled. Override with the
260
- * script's main per-frame logic.
261
- *
262
- * @remarks
263
- * Also called every frame in editor mode when `executeInEditor` is `true`.
264
- * Access the Three.js scene with `this.world.getScene()` for editor-only
265
- * rendering work such as custom shaders or procedural geometry.
251
+ * script's main per-frame logic. Runs at preview/runtime only — never in the
252
+ * editor (use {@link ScriptObject} for editor-visible visuals).
266
253
  */
267
254
  protected onUpdate(): void;
268
255
  /** Called when the editor selects this entity. Optional UX hook. */
@@ -285,6 +272,105 @@ declare class Behaviour implements CustomEcsSystem {
285
272
  onTargetLost(target: WorldEntityView): void;
286
273
  }
287
274
 
275
+ /**
276
+ * Base class for **script objects** — entities whose THREE.js visual is built
277
+ * entirely by user code (procedural geometry, shaders, particle systems,
278
+ * anything the editor can't author natively).
279
+ *
280
+ * The critical difference from {@link Behaviour}: a `ScriptObject` runs in the
281
+ * **editor AND at runtime** — `build()` and `update()` execute identically in
282
+ * both. `ScriptObjectSystem` loads the module, instantiates the class, calls
283
+ * `build()` to get the visual, attaches it under the entity's Object3D, and
284
+ * ticks `update()` every frame. **Do NOT gate anything on
285
+ * `world.isEditorMode()`** — running everywhere is the whole point. Use
286
+ * {@link Behaviour} for gameplay logic that must NOT run in the editor.
287
+ *
288
+ * Subclasses are resolved by the inherited static marker
289
+ * `__reluScriptObjectClass` (survives bundler identity splits). Do not override
290
+ * the marker.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * import { ScriptObject, type ColorInput } from "@relu-interactives/spatial-ecs";
295
+ * import * as THREE from "three";
296
+ *
297
+ * export default class GlowOrb extends ScriptObject {
298
+ * declare data: { color: ColorInput };
299
+ * private mat!: THREE.MeshStandardMaterial;
300
+ *
301
+ * build(): THREE.Object3D {
302
+ * this.data.color ??= { type: "color", value: "#66ccff" };
303
+ * this.mat = new THREE.MeshStandardMaterial({
304
+ * color: this.data.color.value,
305
+ * emissive: this.data.color.value,
306
+ * });
307
+ * return new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), this.mat);
308
+ * }
309
+ *
310
+ * update(dt: number): void {
311
+ * this.mat.emissiveIntensity = 0.5 + 0.5 * Math.sin(performance.now() * 0.002);
312
+ * }
313
+ *
314
+ * dispose(): void {
315
+ * this.mat.dispose();
316
+ * }
317
+ * }
318
+ * ```
319
+ *
320
+ * @category Scripting
321
+ */
322
+ declare class ScriptObject {
323
+ /**
324
+ * Static marker used by `ScriptObjectSystem` to detect `ScriptObject`
325
+ * subclasses even when a bundler ships a second copy of the package into the
326
+ * script blob (breaking `instanceof`). Do NOT override.
327
+ */
328
+ static readonly __reluScriptObjectClass: true;
329
+ /**
330
+ * The script's data payload (the `ScriptObjectComponent.data`). Declare a
331
+ * concrete shape on subclasses with `declare data: MyShape` for type safety.
332
+ * Use the same typed input envelopes as Behaviours (`ColorInput`,
333
+ * `SliderInput`, `EntityInput`, …) to get inspector fields.
334
+ */
335
+ data: Record<string, unknown>;
336
+ /** The owning ECS world. */
337
+ world: World;
338
+ /** The entity this script object is bound to. */
339
+ entityId: number;
340
+ /**
341
+ * The Object3D returned by {@link ScriptObject.build}. Assigned by
342
+ * `ScriptObjectSystem` after `build()` runs; `null` until then.
343
+ */
344
+ root: THREE.Object3D | null;
345
+ /**
346
+ * Engine-managed constructor. `ScriptObjectSystem` instantiates subclasses
347
+ * with the owning world, the entity id, and the component's `data`. User code
348
+ * should not call this directly.
349
+ */
350
+ constructor(world: World, entityId: number, data?: Record<string, unknown>);
351
+ /**
352
+ * REQUIRED — build and return the visual root. Called once by
353
+ * `ScriptObjectSystem` after construction. The returned object is attached
354
+ * under the entity's Object3D (so it inherits the entity transform, selection
355
+ * and visibility). Subclasses MUST override.
356
+ */
357
+ build(): THREE.Object3D;
358
+ /**
359
+ * OPTIONAL — per-frame hook. Runs in the editor AND at runtime (e.g. advance
360
+ * a shader time uniform, procedural animation). Keep per-frame work on local
361
+ * THREE state / uniforms — do NOT write ECS components every frame (that
362
+ * churns the editor's undo history).
363
+ */
364
+ update?(deltaTime: number): void;
365
+ /**
366
+ * OPTIONAL — free any GPU resources this script created (geometries,
367
+ * materials, textures, render targets). Called by `ScriptObjectSystem` when
368
+ * the object is removed, disabled, or reloaded. THREE `dispose()` is
369
+ * idempotent, so double-dispose is safe.
370
+ */
371
+ dispose?(): void;
372
+ }
373
+
288
374
  type ComponentValueMaps = {
289
375
  object3DRefValues: Map<number, unknown>;
290
376
  nameValues: Map<number, unknown>;
@@ -306,6 +392,7 @@ type ComponentValueMaps = {
306
392
  animationValues: Map<number, unknown>;
307
393
  imageTargetAnchorValues: Map<number, unknown>;
308
394
  scriptValues: Map<number, unknown>;
395
+ scriptObjectValues: Map<number, unknown>;
309
396
  instancedMeshValues: Map<number, unknown>;
310
397
  instanceSlotValues: Map<number, unknown>;
311
398
  prefabNodeValues: Map<number, unknown>;
@@ -390,6 +477,30 @@ declare function loadSharedGltf(url: string, load: () => Promise<GLTF>): Promise
390
477
  */
391
478
  declare function clearModelCache(url?: string): void;
392
479
 
480
+ /**
481
+ * Pure JSON-space prefab patching: apply a nested prefab's NEW definition to
482
+ * every top-level-relative instance region inside a HOST document snapshot,
483
+ * honoring the region's stored per-property overrides. This is how edits to
484
+ * a child prefab cascade through the `.prefab` files that nest it (no live
485
+ * world exists for files). Reused later by `.scene` assets.
486
+ *
487
+ * Trust model: the region's stored override list (refreshed at every host
488
+ * save with `+node` addition markers) IS the user state — no old base is
489
+ * needed. Consequently the operation is idempotent: re-applying the same
490
+ * definition yields `changed: false`.
491
+ */
492
+
493
+ type ApplyPrefabToSnapshotResult = {
494
+ snapshot: SubtreeSnapshot;
495
+ changed: boolean;
496
+ };
497
+ /**
498
+ * Patch every top-level-relative nested instance of `nestedUrl` inside
499
+ * `hostSnapshot` with `newNestedSnapshot`. Deeper nesting (B inside C inside
500
+ * A) is NOT touched here — it cascades through C's own file.
501
+ */
502
+ declare function applyPrefabToSnapshotInstances(hostSnapshot: SubtreeSnapshot, nestedUrl: string, newNestedSnapshot: SubtreeSnapshot): ApplyPrefabToSnapshotResult;
503
+
393
504
  /**
394
505
  * Collect the given roots plus every descendant, BFS parent-first, using the
395
506
  * `ParentId` component edges. Parent-first ordering matters: serialization
@@ -398,6 +509,71 @@ declare function clearModelCache(url?: string): void;
398
509
  */
399
510
  declare function collectEntitySubtree(world: World, rootIds: EntityId[]): EntityId[];
400
511
 
512
+ /**
513
+ * Direct (top-level-relative) prefab urls a document depends on — the edges
514
+ * of the nesting graph. Deeper nesting is reached transitively through each
515
+ * dependency's own file, never directly.
516
+ */
517
+ declare function collectDirectPrefabDependencies(snapshot: SubtreeSnapshot): string[];
518
+
519
+ /**
520
+ * Scoped node identity for (possibly nested) prefab documents.
521
+ *
522
+ * A nodeKey identifies an entity within one prefab document even when the
523
+ * document contains nested instances of other prefabs (whose nodeIds repeat
524
+ * per instance). Formula (parent-first walk from the scope root, inherited
525
+ * `prefix`; the scope root's prefix is "" even if it carries PrefabInstance):
526
+ *
527
+ * - entity WITHOUT PrefabInstance: key = prefix + nodeId, children inherit
528
+ * the same prefix;
529
+ * - entity WITH PrefabInstance (non-scope-root): childPrefix = prefix +
530
+ * instanceId + "/"; key = childPrefix + nodeId (self-inclusive — sibling
531
+ * instances of the SAME prefab get distinct keys); children inherit
532
+ * childPrefix.
533
+ *
534
+ * Documents with no nested instances degenerate to bare nodeIds, so v1 files
535
+ * and override lists keep their exact meaning.
536
+ */
537
+
538
+ declare const NODE_KEY_SEPARATOR = "/";
539
+ /**
540
+ * Reserved override "component" marking a node ADDED by the host document
541
+ * inside a nested instance region (`"<nodeKey>|+node|"`). File patching
542
+ * trusts stored overrides (the old base is unavailable), so without the
543
+ * marker a host-authored node inside the region would be indistinguishable
544
+ * from a node the child prefab removed — and get deleted.
545
+ */
546
+ declare const ADDED_NODE_MARKER_COMPONENT = "+node";
547
+ type NodeKeyMaps = {
548
+ idToKey: Map<number, string>;
549
+ keyToId: Map<string, number>;
550
+ /** nodeKey of the scope root (bare nodeId), or null when it has none. */
551
+ rootKey: string | null;
552
+ /** Entity ids of NESTED instance roots (non-scope-root PrefabInstance). */
553
+ boundaryIds: Map<number, {
554
+ url: string;
555
+ instanceId: string;
556
+ }>;
557
+ };
558
+ declare function buildNodeKeyMapsFromSnapshot(snapshot: SubtreeSnapshot, options?: {
559
+ scopeRootEntityId?: number;
560
+ }): NodeKeyMaps;
561
+ /** JSON-space subtree ids (root first, BFS) via ParentId edges. */
562
+ declare function collectSnapshotSubtreeIds(snapshot: SubtreeSnapshot, rootEntityId: number): number[];
563
+ type NestedRegion = {
564
+ rootEntityId: number;
565
+ url: string;
566
+ instanceId: string;
567
+ overrides: string[];
568
+ };
569
+ /**
570
+ * TOP-LEVEL-RELATIVE nested instance regions of a document: instance roots
571
+ * that are not the scope root and not themselves inside another region.
572
+ * Deeper nesting is reached by cascading through the intermediate prefab's
573
+ * own file, never directly.
574
+ */
575
+ declare function findTopLevelNestedRegions(snapshot: SubtreeSnapshot): NestedRegion[];
576
+
401
577
  type InstantiateSubtreeResult = {
402
578
  /** Live entity id of the subtree root, or null when nothing hydrated. */
403
579
  rootId: EntityId | null;
@@ -451,6 +627,8 @@ declare function indexSnapshotByEntity(snapshot: SubtreeSnapshot): Map<number, M
451
627
  * - entity-id fields → `{$node}` (internal) or raw id (external)
452
628
  * - ScriptComponent entries keep only `{className, scriptUrl, enabled, data}`
453
629
  * (entityId/runtimeState/runtimeMessage are per-instance transients)
630
+ * - ScriptObjectComponent keeps only `{scriptUrl, className, enabled, data}`
631
+ * (runtimeState/runtimeMessage are per-instance transients)
454
632
  * - Object3DRef keeps only `{visibility}` (the rest is a live object ref)
455
633
  *
456
634
  * Keep the id-bearing field list in sync with `snapshotIdUtils.ts`.
@@ -485,11 +663,20 @@ declare function collectDiffPaths(base: unknown, current: unknown, prefix?: stri
485
663
  * subtree snapshots carrying PrefabNode identity) and return the encoded
486
664
  * override list: every path where the instance deviates from the base.
487
665
  *
488
- * v1 policies: components missing on the instance produce no override
489
- * (apply re-adds them); components/nodes only on the instance are local
490
- * additions and produce no override (apply never touches them).
666
+ * Identity is nodeKey-scoped ({@link buildNodeKeyMapsFromSnapshot}), so
667
+ * nested instance regions diff correctly; for nested-free documents the
668
+ * keys degenerate to bare nodeIds (v1 behavior unchanged).
669
+ *
670
+ * Policies: components missing on the instance produce no override (apply
671
+ * re-adds them); components/nodes only on the instance are local additions
672
+ * and produce no override — EXCEPT when `emitAdditionMarkers` is set
673
+ * (file-space callers), which records `"<key>|+node|"` for instance nodes
674
+ * absent from the base so pure-JSON patching never deletes host-authored
675
+ * nodes inside nested regions.
491
676
  */
492
- declare function computeInstanceOverrides(baseSnapshot: SubtreeSnapshot, instanceSnapshot: SubtreeSnapshot): string[];
677
+ declare function computeInstanceOverrides(baseSnapshot: SubtreeSnapshot, instanceSnapshot: SubtreeSnapshot, options?: {
678
+ emitAdditionMarkers?: boolean;
679
+ }): string[];
493
680
  /**
494
681
  * Merge a component's NEW canonical file value with an instance's canonical
495
682
  * value, preserving the instance side at every overridden path. An empty
@@ -519,4 +706,4 @@ declare function serializeEntitySubtree(world: World, rootId: EntityId, options?
519
706
  maxDepth?: number;
520
707
  }): SerializeEntitySubtreeResult;
521
708
 
522
- export { Behaviour, Component, type ComponentValueMaps, EcsEntityKind, EcsSystem, type EcsWorldComponentSnapshot, type EcsWorldStateSnapshotLike, EntityId, ExternalRefWarning, HYDRATABLE_ENTITY_KINDS, type HydrateOptions, type HydrateWorldFromSnapshotResult, type InstantiateSubtreeResult, type NodeMaps, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectManagementSystem, ParentLookup, SHARED_MODEL_RESOURCE_FLAG, type SerializeEntitySubtreeResult, SpawnPrefabOptions, SpawnPrefabSource, SubtreeSnapshot, World, WorldEntityView, buildComponentValueMaps, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearModelCache, collectDiffPaths, collectEntitySubtree, computeInstanceOverrides, decanonicalizeComponentValue, encodeOverride, findSnapshotRootEntityIds, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadSharedGltf, mergeComponentValue, parseOverride, resolveDracoDecoderPath, restoreComponentState, restorePostCreateComponents, serializeEntitySubtree, setDracoDecoderPath };
709
+ export { ADDED_NODE_MARKER_COMPONENT, type ApplyPrefabToSnapshotResult, Behaviour, Component, type ComponentValueMaps, EcsEntityKind, EcsSystem, type EcsWorldComponentSnapshot, type EcsWorldStateSnapshotLike, EntityId, ExternalRefWarning, HYDRATABLE_ENTITY_KINDS, type HydrateOptions, type HydrateWorldFromSnapshotResult, type InstantiateSubtreeResult, NODE_KEY_SEPARATOR, type NestedRegion, type NodeKeyMaps, type NodeMaps, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectManagementSystem, ParentLookup, SHARED_MODEL_RESOURCE_FLAG, ScriptObject, type SerializeEntitySubtreeResult, SpawnPrefabOptions, SpawnPrefabSource, SubtreeSnapshot, World, WorldEntityView, applyPrefabToSnapshotInstances, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearModelCache, collectDiffPaths, collectDirectPrefabDependencies, collectEntitySubtree, collectSnapshotSubtreeIds, computeInstanceOverrides, decanonicalizeComponentValue, encodeOverride, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadSharedGltf, mergeComponentValue, parseOverride, resolveDracoDecoderPath, restoreComponentState, restorePostCreateComponents, serializeEntitySubtree, setDracoDecoderPath };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { k as EcsSystem, C as Component, W as WorldEntityView, T as TransformComponent, l as World, S as SpawnPrefabSource, m as SpawnPrefabOptions, n as ComponentClass, o as ParentLookup, p as EcsEntityKind, O as ObjectManagementSystem, q as EntityId, r as SubtreeSnapshot, s as ExternalRefWarning } from './System-BjwOBx81.js';
2
- export { A as AssetInput, t as CheckboxInput, u as ColorInput, v as ComponentFactory, w as ComponentInput, x as ComponentKind, y as DropdownInput, z as DropdownOption, B as EcsComponentFactoryKind, F as EcsEntityEditorKind, G as EntityInput, H as FloatInput, I as InputSystem, J as IntersectionResult, K as KeyboardKey, L as KeyboardKeys, M as MouseButton, N as PREFAB_FILE_VERSION, Q as PrefabAsset, R as RemapSnapshotOptions, U as RenderSystem, X as SelectionListener, Y as SliderInput, Z as TextInput, _ as ToggleInput, $ as TransformSyncSystem, a0 as Vec3, a1 as Vector2Input, a2 as Vector3Input, a3 as clearPrefabCache, a4 as despawnPrefab, a5 as loadPrefab, a6 as parsePrefabAsset, a7 as remapSnapshotEntityIds, a8 as setPrefabAssetUrlResolver, a9 as spawnPrefab } from './System-BjwOBx81.js';
1
+ import { k as EcsSystem, C as Component, W as WorldEntityView, T as TransformComponent, l as World, S as SpawnPrefabSource, m as SpawnPrefabOptions, n as ComponentClass, o as ParentLookup, p as EcsEntityKind, O as ObjectManagementSystem, q as SubtreeSnapshot, r as EntityId, s as ExternalRefWarning } from './System-CYWZ_Zx5.js';
2
+ export { A as AssetInput, t as CheckboxInput, u as ColorInput, v as ComponentFactory, w as ComponentInput, x as ComponentKind, y as DropdownInput, z as DropdownOption, B as EcsComponentFactoryKind, F as EcsEntityEditorKind, G as EntityInput, H as FloatInput, I as InputSystem, J as IntersectionResult, K as KeyboardKey, L as KeyboardKeys, M as MouseButton, N as PREFAB_FILE_VERSION, Q as PREFAB_FILE_VERSION_NESTED, R as PrefabAsset, U as RemapSnapshotOptions, X as RenderSystem, Y as SelectionListener, Z as SliderInput, _ as TextInput, $ as ToggleInput, a0 as TransformSyncSystem, a1 as Vec3, a2 as Vector2Input, a3 as Vector3Input, a4 as clearPrefabCache, a5 as despawnPrefab, a6 as loadPrefab, a7 as parsePrefabAsset, a8 as prefabFileVersionFor, a9 as remapSnapshotEntityIds, aa as setPrefabAssetUrlResolver, ab as spawnPrefab } from './System-CYWZ_Zx5.js';
3
3
  import * as THREE from 'three';
4
- import { O as Object3DRef } from './Video-B9z5AtIf.js';
5
- export { a3 as AnimationStateCallbacks, a4 as AnimationStateMachine, a5 as AnimationStateManager, a6 as StateMachine } from './Video-B9z5AtIf.js';
6
- export { i as CoreComponents } from './index-BBI2YdCu.js';
7
- export { AnimationSystem, AudioSystem, CameraSyncSystem, EnvironmentSyncSystem, InstancedMeshSystem, LightSyncSystem, MaterialSyncSystem, MeshGeometrySystem, ObjectVisibilitySystem, PhysicsSyncSystem, PostprocessingSyncSystem, ScriptBehaviourSystem, SelectionContext, VideoSystem } from './systems/index.js';
8
- export { createAnimationComponent, createAudioComponent, createCameraComponent, createEntityTypeComponent, createEnvironmentComponent, createImageComponent, createImageTargetAnchorComponent, createImageTargetComponent, createInstanceSlotComponent, createInstancedMeshComponent, createLightComponent, createMaterialComponent, createMeshComponent, createMeshGeometryComponent, createModelComponent, createNameComponent, createObject3DRefComponent, createParentIdComponent, createPhysicsComponent, createPostprocessingComponent, createScriptComponent, createSelectableComponent, createSelectionTransformStateComponent, createSpriteComponent, createTransformComponent, createVideoComponent, registerDefaultComponentFactories, registerDefaultFactories } from './factories/index.js';
4
+ import { O as Object3DRef } from './Video-C1IOat7L.js';
5
+ export { a6 as AnimationStateCallbacks, a7 as AnimationStateMachine, a8 as AnimationStateManager, a9 as StateMachine } from './Video-C1IOat7L.js';
6
+ export { i as CoreComponents } from './index-DdiWu1b6.js';
7
+ export { AnimationSystem, AudioSystem, CameraSyncSystem, EnvironmentSyncSystem, InstancedMeshSystem, LightSyncSystem, MaterialSyncSystem, MeshGeometrySystem, ModuleLoader, ObjectVisibilitySystem, PhysicsSyncSystem, PostprocessingSyncSystem, SCRIPT_OBJECT_ROOT_FLAG, ScriptBehaviourSystem, ScriptObjectSystem, SelectionContext, VideoSystem } from './systems/index.js';
8
+ export { createAnimationComponent, createAudioComponent, createCameraComponent, createEntityTypeComponent, createEnvironmentComponent, createImageComponent, createImageTargetAnchorComponent, createImageTargetComponent, createInstanceSlotComponent, createInstancedMeshComponent, createLightComponent, createMaterialComponent, createMeshComponent, createMeshGeometryComponent, createModelComponent, createNameComponent, createObject3DRefComponent, createParentIdComponent, createPhysicsComponent, createPostprocessingComponent, createScriptComponent, createScriptObjectComponent, createSelectableComponent, createSelectionTransformStateComponent, createSpriteComponent, createTransformComponent, createVideoComponent, registerDefaultComponentFactories, registerDefaultFactories } from './factories/index.js';
9
9
  import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
10
10
  import '@dimforge/rapier3d-compat';
11
11
 
@@ -25,15 +25,10 @@ interface CustomEcsSystem extends EcsSystem {
25
25
  * Override {@link Behaviour.onUpdate} for per-frame logic — never override
26
26
  * {@link Behaviour.update}, which is the engine-managed entry point.
27
27
  *
28
- * **Editor scripts**: By default, `Behaviour` subclasses do not run inside the
29
- * editor. Set `executeInEditor: true` on the script entry in the inspector to
30
- * enable the full lifecycle (`init` → `onUpdate` → `dispose`) while the editor
31
- * is open. This gives direct access to the Three.js scene, camera, and renderer
32
- * via {@link World.getScene}, {@link World.getCamera}, and
33
- * {@link World.getRenderer} — useful for custom shaders, procedural geometry,
34
- * grass, and any Three.js feature the editor's object palette does not expose.
35
- * Avoid mutating ECS component state every frame in editor mode, as it creates
36
- * undo history entries on every tick.
28
+ * **Editor vs runtime**: `Behaviour` subclasses run ONLY at preview/runtime —
29
+ * they never execute in the editor. For a visual that must render and animate
30
+ * inside the editor (custom shaders, procedural geometry, grass, anything the
31
+ * object palette can't produce), use {@link ScriptObject} instead.
37
32
  *
38
33
  * The runtime recognizes a script by its **default export** plus the
39
34
  * inherited static marker `__reluBehaviourClass`. Do not override the marker.
@@ -248,21 +243,13 @@ declare class Behaviour implements CustomEcsSystem {
248
243
  /**
249
244
  * Called once before the first {@link Behaviour.onUpdate}. Override to
250
245
  * default and validate {@link Behaviour.data}, cache references, or set
251
- * initial state.
252
- *
253
- * @remarks
254
- * Also called in editor mode when `executeInEditor` is `true` on the script
255
- * entry.
246
+ * initial state. Runs at preview/runtime only — never in the editor.
256
247
  */
257
248
  protected init(): void;
258
249
  /**
259
250
  * Called every frame while the script is enabled. Override with the
260
- * script's main per-frame logic.
261
- *
262
- * @remarks
263
- * Also called every frame in editor mode when `executeInEditor` is `true`.
264
- * Access the Three.js scene with `this.world.getScene()` for editor-only
265
- * rendering work such as custom shaders or procedural geometry.
251
+ * script's main per-frame logic. Runs at preview/runtime only — never in the
252
+ * editor (use {@link ScriptObject} for editor-visible visuals).
266
253
  */
267
254
  protected onUpdate(): void;
268
255
  /** Called when the editor selects this entity. Optional UX hook. */
@@ -285,6 +272,105 @@ declare class Behaviour implements CustomEcsSystem {
285
272
  onTargetLost(target: WorldEntityView): void;
286
273
  }
287
274
 
275
+ /**
276
+ * Base class for **script objects** — entities whose THREE.js visual is built
277
+ * entirely by user code (procedural geometry, shaders, particle systems,
278
+ * anything the editor can't author natively).
279
+ *
280
+ * The critical difference from {@link Behaviour}: a `ScriptObject` runs in the
281
+ * **editor AND at runtime** — `build()` and `update()` execute identically in
282
+ * both. `ScriptObjectSystem` loads the module, instantiates the class, calls
283
+ * `build()` to get the visual, attaches it under the entity's Object3D, and
284
+ * ticks `update()` every frame. **Do NOT gate anything on
285
+ * `world.isEditorMode()`** — running everywhere is the whole point. Use
286
+ * {@link Behaviour} for gameplay logic that must NOT run in the editor.
287
+ *
288
+ * Subclasses are resolved by the inherited static marker
289
+ * `__reluScriptObjectClass` (survives bundler identity splits). Do not override
290
+ * the marker.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * import { ScriptObject, type ColorInput } from "@relu-interactives/spatial-ecs";
295
+ * import * as THREE from "three";
296
+ *
297
+ * export default class GlowOrb extends ScriptObject {
298
+ * declare data: { color: ColorInput };
299
+ * private mat!: THREE.MeshStandardMaterial;
300
+ *
301
+ * build(): THREE.Object3D {
302
+ * this.data.color ??= { type: "color", value: "#66ccff" };
303
+ * this.mat = new THREE.MeshStandardMaterial({
304
+ * color: this.data.color.value,
305
+ * emissive: this.data.color.value,
306
+ * });
307
+ * return new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), this.mat);
308
+ * }
309
+ *
310
+ * update(dt: number): void {
311
+ * this.mat.emissiveIntensity = 0.5 + 0.5 * Math.sin(performance.now() * 0.002);
312
+ * }
313
+ *
314
+ * dispose(): void {
315
+ * this.mat.dispose();
316
+ * }
317
+ * }
318
+ * ```
319
+ *
320
+ * @category Scripting
321
+ */
322
+ declare class ScriptObject {
323
+ /**
324
+ * Static marker used by `ScriptObjectSystem` to detect `ScriptObject`
325
+ * subclasses even when a bundler ships a second copy of the package into the
326
+ * script blob (breaking `instanceof`). Do NOT override.
327
+ */
328
+ static readonly __reluScriptObjectClass: true;
329
+ /**
330
+ * The script's data payload (the `ScriptObjectComponent.data`). Declare a
331
+ * concrete shape on subclasses with `declare data: MyShape` for type safety.
332
+ * Use the same typed input envelopes as Behaviours (`ColorInput`,
333
+ * `SliderInput`, `EntityInput`, …) to get inspector fields.
334
+ */
335
+ data: Record<string, unknown>;
336
+ /** The owning ECS world. */
337
+ world: World;
338
+ /** The entity this script object is bound to. */
339
+ entityId: number;
340
+ /**
341
+ * The Object3D returned by {@link ScriptObject.build}. Assigned by
342
+ * `ScriptObjectSystem` after `build()` runs; `null` until then.
343
+ */
344
+ root: THREE.Object3D | null;
345
+ /**
346
+ * Engine-managed constructor. `ScriptObjectSystem` instantiates subclasses
347
+ * with the owning world, the entity id, and the component's `data`. User code
348
+ * should not call this directly.
349
+ */
350
+ constructor(world: World, entityId: number, data?: Record<string, unknown>);
351
+ /**
352
+ * REQUIRED — build and return the visual root. Called once by
353
+ * `ScriptObjectSystem` after construction. The returned object is attached
354
+ * under the entity's Object3D (so it inherits the entity transform, selection
355
+ * and visibility). Subclasses MUST override.
356
+ */
357
+ build(): THREE.Object3D;
358
+ /**
359
+ * OPTIONAL — per-frame hook. Runs in the editor AND at runtime (e.g. advance
360
+ * a shader time uniform, procedural animation). Keep per-frame work on local
361
+ * THREE state / uniforms — do NOT write ECS components every frame (that
362
+ * churns the editor's undo history).
363
+ */
364
+ update?(deltaTime: number): void;
365
+ /**
366
+ * OPTIONAL — free any GPU resources this script created (geometries,
367
+ * materials, textures, render targets). Called by `ScriptObjectSystem` when
368
+ * the object is removed, disabled, or reloaded. THREE `dispose()` is
369
+ * idempotent, so double-dispose is safe.
370
+ */
371
+ dispose?(): void;
372
+ }
373
+
288
374
  type ComponentValueMaps = {
289
375
  object3DRefValues: Map<number, unknown>;
290
376
  nameValues: Map<number, unknown>;
@@ -306,6 +392,7 @@ type ComponentValueMaps = {
306
392
  animationValues: Map<number, unknown>;
307
393
  imageTargetAnchorValues: Map<number, unknown>;
308
394
  scriptValues: Map<number, unknown>;
395
+ scriptObjectValues: Map<number, unknown>;
309
396
  instancedMeshValues: Map<number, unknown>;
310
397
  instanceSlotValues: Map<number, unknown>;
311
398
  prefabNodeValues: Map<number, unknown>;
@@ -390,6 +477,30 @@ declare function loadSharedGltf(url: string, load: () => Promise<GLTF>): Promise
390
477
  */
391
478
  declare function clearModelCache(url?: string): void;
392
479
 
480
+ /**
481
+ * Pure JSON-space prefab patching: apply a nested prefab's NEW definition to
482
+ * every top-level-relative instance region inside a HOST document snapshot,
483
+ * honoring the region's stored per-property overrides. This is how edits to
484
+ * a child prefab cascade through the `.prefab` files that nest it (no live
485
+ * world exists for files). Reused later by `.scene` assets.
486
+ *
487
+ * Trust model: the region's stored override list (refreshed at every host
488
+ * save with `+node` addition markers) IS the user state — no old base is
489
+ * needed. Consequently the operation is idempotent: re-applying the same
490
+ * definition yields `changed: false`.
491
+ */
492
+
493
+ type ApplyPrefabToSnapshotResult = {
494
+ snapshot: SubtreeSnapshot;
495
+ changed: boolean;
496
+ };
497
+ /**
498
+ * Patch every top-level-relative nested instance of `nestedUrl` inside
499
+ * `hostSnapshot` with `newNestedSnapshot`. Deeper nesting (B inside C inside
500
+ * A) is NOT touched here — it cascades through C's own file.
501
+ */
502
+ declare function applyPrefabToSnapshotInstances(hostSnapshot: SubtreeSnapshot, nestedUrl: string, newNestedSnapshot: SubtreeSnapshot): ApplyPrefabToSnapshotResult;
503
+
393
504
  /**
394
505
  * Collect the given roots plus every descendant, BFS parent-first, using the
395
506
  * `ParentId` component edges. Parent-first ordering matters: serialization
@@ -398,6 +509,71 @@ declare function clearModelCache(url?: string): void;
398
509
  */
399
510
  declare function collectEntitySubtree(world: World, rootIds: EntityId[]): EntityId[];
400
511
 
512
+ /**
513
+ * Direct (top-level-relative) prefab urls a document depends on — the edges
514
+ * of the nesting graph. Deeper nesting is reached transitively through each
515
+ * dependency's own file, never directly.
516
+ */
517
+ declare function collectDirectPrefabDependencies(snapshot: SubtreeSnapshot): string[];
518
+
519
+ /**
520
+ * Scoped node identity for (possibly nested) prefab documents.
521
+ *
522
+ * A nodeKey identifies an entity within one prefab document even when the
523
+ * document contains nested instances of other prefabs (whose nodeIds repeat
524
+ * per instance). Formula (parent-first walk from the scope root, inherited
525
+ * `prefix`; the scope root's prefix is "" even if it carries PrefabInstance):
526
+ *
527
+ * - entity WITHOUT PrefabInstance: key = prefix + nodeId, children inherit
528
+ * the same prefix;
529
+ * - entity WITH PrefabInstance (non-scope-root): childPrefix = prefix +
530
+ * instanceId + "/"; key = childPrefix + nodeId (self-inclusive — sibling
531
+ * instances of the SAME prefab get distinct keys); children inherit
532
+ * childPrefix.
533
+ *
534
+ * Documents with no nested instances degenerate to bare nodeIds, so v1 files
535
+ * and override lists keep their exact meaning.
536
+ */
537
+
538
+ declare const NODE_KEY_SEPARATOR = "/";
539
+ /**
540
+ * Reserved override "component" marking a node ADDED by the host document
541
+ * inside a nested instance region (`"<nodeKey>|+node|"`). File patching
542
+ * trusts stored overrides (the old base is unavailable), so without the
543
+ * marker a host-authored node inside the region would be indistinguishable
544
+ * from a node the child prefab removed — and get deleted.
545
+ */
546
+ declare const ADDED_NODE_MARKER_COMPONENT = "+node";
547
+ type NodeKeyMaps = {
548
+ idToKey: Map<number, string>;
549
+ keyToId: Map<string, number>;
550
+ /** nodeKey of the scope root (bare nodeId), or null when it has none. */
551
+ rootKey: string | null;
552
+ /** Entity ids of NESTED instance roots (non-scope-root PrefabInstance). */
553
+ boundaryIds: Map<number, {
554
+ url: string;
555
+ instanceId: string;
556
+ }>;
557
+ };
558
+ declare function buildNodeKeyMapsFromSnapshot(snapshot: SubtreeSnapshot, options?: {
559
+ scopeRootEntityId?: number;
560
+ }): NodeKeyMaps;
561
+ /** JSON-space subtree ids (root first, BFS) via ParentId edges. */
562
+ declare function collectSnapshotSubtreeIds(snapshot: SubtreeSnapshot, rootEntityId: number): number[];
563
+ type NestedRegion = {
564
+ rootEntityId: number;
565
+ url: string;
566
+ instanceId: string;
567
+ overrides: string[];
568
+ };
569
+ /**
570
+ * TOP-LEVEL-RELATIVE nested instance regions of a document: instance roots
571
+ * that are not the scope root and not themselves inside another region.
572
+ * Deeper nesting is reached by cascading through the intermediate prefab's
573
+ * own file, never directly.
574
+ */
575
+ declare function findTopLevelNestedRegions(snapshot: SubtreeSnapshot): NestedRegion[];
576
+
401
577
  type InstantiateSubtreeResult = {
402
578
  /** Live entity id of the subtree root, or null when nothing hydrated. */
403
579
  rootId: EntityId | null;
@@ -451,6 +627,8 @@ declare function indexSnapshotByEntity(snapshot: SubtreeSnapshot): Map<number, M
451
627
  * - entity-id fields → `{$node}` (internal) or raw id (external)
452
628
  * - ScriptComponent entries keep only `{className, scriptUrl, enabled, data}`
453
629
  * (entityId/runtimeState/runtimeMessage are per-instance transients)
630
+ * - ScriptObjectComponent keeps only `{scriptUrl, className, enabled, data}`
631
+ * (runtimeState/runtimeMessage are per-instance transients)
454
632
  * - Object3DRef keeps only `{visibility}` (the rest is a live object ref)
455
633
  *
456
634
  * Keep the id-bearing field list in sync with `snapshotIdUtils.ts`.
@@ -485,11 +663,20 @@ declare function collectDiffPaths(base: unknown, current: unknown, prefix?: stri
485
663
  * subtree snapshots carrying PrefabNode identity) and return the encoded
486
664
  * override list: every path where the instance deviates from the base.
487
665
  *
488
- * v1 policies: components missing on the instance produce no override
489
- * (apply re-adds them); components/nodes only on the instance are local
490
- * additions and produce no override (apply never touches them).
666
+ * Identity is nodeKey-scoped ({@link buildNodeKeyMapsFromSnapshot}), so
667
+ * nested instance regions diff correctly; for nested-free documents the
668
+ * keys degenerate to bare nodeIds (v1 behavior unchanged).
669
+ *
670
+ * Policies: components missing on the instance produce no override (apply
671
+ * re-adds them); components/nodes only on the instance are local additions
672
+ * and produce no override — EXCEPT when `emitAdditionMarkers` is set
673
+ * (file-space callers), which records `"<key>|+node|"` for instance nodes
674
+ * absent from the base so pure-JSON patching never deletes host-authored
675
+ * nodes inside nested regions.
491
676
  */
492
- declare function computeInstanceOverrides(baseSnapshot: SubtreeSnapshot, instanceSnapshot: SubtreeSnapshot): string[];
677
+ declare function computeInstanceOverrides(baseSnapshot: SubtreeSnapshot, instanceSnapshot: SubtreeSnapshot, options?: {
678
+ emitAdditionMarkers?: boolean;
679
+ }): string[];
493
680
  /**
494
681
  * Merge a component's NEW canonical file value with an instance's canonical
495
682
  * value, preserving the instance side at every overridden path. An empty
@@ -519,4 +706,4 @@ declare function serializeEntitySubtree(world: World, rootId: EntityId, options?
519
706
  maxDepth?: number;
520
707
  }): SerializeEntitySubtreeResult;
521
708
 
522
- export { Behaviour, Component, type ComponentValueMaps, EcsEntityKind, EcsSystem, type EcsWorldComponentSnapshot, type EcsWorldStateSnapshotLike, EntityId, ExternalRefWarning, HYDRATABLE_ENTITY_KINDS, type HydrateOptions, type HydrateWorldFromSnapshotResult, type InstantiateSubtreeResult, type NodeMaps, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectManagementSystem, ParentLookup, SHARED_MODEL_RESOURCE_FLAG, type SerializeEntitySubtreeResult, SpawnPrefabOptions, SpawnPrefabSource, SubtreeSnapshot, World, WorldEntityView, buildComponentValueMaps, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearModelCache, collectDiffPaths, collectEntitySubtree, computeInstanceOverrides, decanonicalizeComponentValue, encodeOverride, findSnapshotRootEntityIds, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadSharedGltf, mergeComponentValue, parseOverride, resolveDracoDecoderPath, restoreComponentState, restorePostCreateComponents, serializeEntitySubtree, setDracoDecoderPath };
709
+ export { ADDED_NODE_MARKER_COMPONENT, type ApplyPrefabToSnapshotResult, Behaviour, Component, type ComponentValueMaps, EcsEntityKind, EcsSystem, type EcsWorldComponentSnapshot, type EcsWorldStateSnapshotLike, EntityId, ExternalRefWarning, HYDRATABLE_ENTITY_KINDS, type HydrateOptions, type HydrateWorldFromSnapshotResult, type InstantiateSubtreeResult, NODE_KEY_SEPARATOR, type NestedRegion, type NodeKeyMaps, type NodeMaps, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectManagementSystem, ParentLookup, SHARED_MODEL_RESOURCE_FLAG, ScriptObject, type SerializeEntitySubtreeResult, SpawnPrefabOptions, SpawnPrefabSource, SubtreeSnapshot, World, WorldEntityView, applyPrefabToSnapshotInstances, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearModelCache, collectDiffPaths, collectDirectPrefabDependencies, collectEntitySubtree, collectSnapshotSubtreeIds, computeInstanceOverrides, decanonicalizeComponentValue, encodeOverride, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadSharedGltf, mergeComponentValue, parseOverride, resolveDracoDecoderPath, restoreComponentState, restorePostCreateComponents, serializeEntitySubtree, setDracoDecoderPath };