@relu-interactives/spatial-ecs 0.2.20 → 0.2.21

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 (41) hide show
  1. package/dist/{System-BjwOBx81.d.cts → System-me3iAcMF.d.cts} +5 -1
  2. package/dist/{System-BjwOBx81.d.ts → System-me3iAcMF.d.ts} +5 -1
  3. package/dist/{Video-B9z5AtIf.d.ts → Video-DHnxjGhW.d.ts} +1 -1
  4. package/dist/{Video-B8yVI1tF.d.cts → Video-U9hAnf9y.d.cts} +1 -1
  5. package/dist/{chunk-67MFQDHX.js → chunk-5U3YS4JD.js} +196 -33
  6. package/dist/chunk-5U3YS4JD.js.map +1 -0
  7. package/dist/{chunk-DY4ETMP2.js → chunk-AXRCGF63.js} +3 -3
  8. package/dist/{chunk-DY4ETMP2.js.map → chunk-AXRCGF63.js.map} +1 -1
  9. package/dist/{chunk-HUHN6IFD.js → chunk-K6KMKKJ6.js} +4 -4
  10. package/dist/{chunk-HUHN6IFD.js.map → chunk-K6KMKKJ6.js.map} +1 -1
  11. package/dist/{chunk-Z6OMYEXU.js → chunk-V7BNVTJW.js} +11 -3
  12. package/dist/chunk-V7BNVTJW.js.map +1 -0
  13. package/dist/components/index.cjs +9 -1
  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/prefabs.md +24 -5
  19. package/dist/factories/index.d.cts +2 -2
  20. package/dist/factories/index.d.ts +2 -2
  21. package/dist/factories/index.js +3 -3
  22. package/dist/{index-LHLKpJhZ.d.cts → index-CZo5I5Gs.d.cts} +29 -13
  23. package/dist/{index-BBI2YdCu.d.ts → index-D7FSg0Au.d.ts} +29 -13
  24. package/dist/index.cjs +458 -40
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +108 -10
  27. package/dist/index.d.ts +108 -10
  28. package/dist/index.js +249 -11
  29. package/dist/index.js.map +1 -1
  30. package/dist/systems/index.cjs +26 -7
  31. package/dist/systems/index.cjs.map +1 -1
  32. package/dist/systems/index.d.cts +2 -2
  33. package/dist/systems/index.d.ts +2 -2
  34. package/dist/systems/index.js +3 -3
  35. package/dist/types.d.cts +1 -1
  36. package/dist/types.d.ts +1 -1
  37. package/dist/worldSnapshotUtils.d.cts +1 -1
  38. package/dist/worldSnapshotUtils.d.ts +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-67MFQDHX.js.map +0 -1
  41. package/dist/chunk-Z6OMYEXU.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -3550,11 +3550,19 @@ __export(components_exports, {
3550
3550
  });
3551
3551
 
3552
3552
  // core/components/PrefabInstance.ts
3553
+ var generateInstanceId = /* @__PURE__ */ __name(() => {
3554
+ try {
3555
+ return crypto.randomUUID();
3556
+ } catch {
3557
+ return `i-${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
3558
+ }
3559
+ }, "generateInstanceId");
3553
3560
  var _PrefabInstance = class _PrefabInstance extends Component {
3554
- constructor(url, overrides = []) {
3561
+ constructor(url, overrides = [], instanceId) {
3555
3562
  super();
3556
3563
  this.url = url;
3557
3564
  this.overrides = overrides;
3565
+ this.instanceId = typeof instanceId === "string" && instanceId.length > 0 ? instanceId : generateInstanceId();
3558
3566
  }
3559
3567
  };
3560
3568
  __name(_PrefabInstance, "PrefabInstance");
@@ -4767,7 +4775,13 @@ function restoreComponentState(world, objectManagementSystem, targetEntityId, so
4767
4775
  ) : [];
4768
4776
  world.addComponent(
4769
4777
  targetEntityId,
4770
- new PrefabInstance(sourcePrefabInstance.url, overrides)
4778
+ new PrefabInstance(
4779
+ sourcePrefabInstance.url,
4780
+ overrides,
4781
+ // Preserve the placement identity; the constructor generates a
4782
+ // fresh one for legacy snapshots that predate instanceId.
4783
+ typeof sourcePrefabInstance.instanceId === "string" && sourcePrefabInstance.instanceId.length > 0 ? sourcePrefabInstance.instanceId : void 0
4784
+ )
4771
4785
  );
4772
4786
  }
4773
4787
  }
@@ -5014,6 +5028,148 @@ function hydrateWorldFromSnapshot(snapshot, world, objectManagementSystem) {
5014
5028
  }
5015
5029
  __name(hydrateWorldFromSnapshot, "hydrateWorldFromSnapshot");
5016
5030
 
5031
+ // core/prefab/nodeKeys.ts
5032
+ var NODE_KEY_SEPARATOR = "/";
5033
+ var ADDED_NODE_MARKER_COMPONENT = "+node";
5034
+ var asRecord4 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
5035
+ var readBlock = /* @__PURE__ */ __name((snapshot, component) => {
5036
+ const map = /* @__PURE__ */ new Map();
5037
+ const block = snapshot.components.find((b) => b.component === component);
5038
+ for (const entry of block?.entries ?? []) {
5039
+ const record = asRecord4(entry.value);
5040
+ if (record) {
5041
+ map.set(entry.entityId, record);
5042
+ }
5043
+ }
5044
+ return map;
5045
+ }, "readBlock");
5046
+ var fallbackInstanceId = /* @__PURE__ */ __name((entityId) => `e${entityId}`, "fallbackInstanceId");
5047
+ function buildNodeKeyMapsFromSnapshot(snapshot, options) {
5048
+ const parentValues = readBlock(snapshot, "ParentId");
5049
+ const nodeValues = readBlock(snapshot, "PrefabNode");
5050
+ const instanceValues = readBlock(snapshot, "PrefabInstance");
5051
+ const inDocument = new Set(snapshot.entities);
5052
+ const childrenByParent = /* @__PURE__ */ new Map();
5053
+ const rootIds = [];
5054
+ for (const entityId of snapshot.entities) {
5055
+ const parent = parentValues.get(entityId)?.value;
5056
+ if (typeof parent === "number" && inDocument.has(parent)) {
5057
+ const siblings = childrenByParent.get(parent);
5058
+ if (siblings) siblings.push(entityId);
5059
+ else childrenByParent.set(parent, [entityId]);
5060
+ } else {
5061
+ rootIds.push(entityId);
5062
+ }
5063
+ }
5064
+ const scopeRoots = options?.scopeRootEntityId !== void 0 ? [options.scopeRootEntityId] : rootIds;
5065
+ const idToKey = /* @__PURE__ */ new Map();
5066
+ const keyToId = /* @__PURE__ */ new Map();
5067
+ const boundaryIds = /* @__PURE__ */ new Map();
5068
+ const scopeRootSet = new Set(scopeRoots);
5069
+ const queue = scopeRoots.map(
5070
+ (entityId) => ({ entityId, prefix: "" })
5071
+ );
5072
+ while (queue.length > 0) {
5073
+ const { entityId, prefix } = queue.shift();
5074
+ const instance = instanceValues.get(entityId);
5075
+ const isBoundary = Boolean(
5076
+ instance && typeof instance.url === "string" && !scopeRootSet.has(entityId)
5077
+ );
5078
+ let ownPrefix = prefix;
5079
+ let childPrefix = prefix;
5080
+ if (isBoundary && instance) {
5081
+ const instanceId = typeof instance.instanceId === "string" && instance.instanceId.length > 0 ? instance.instanceId : fallbackInstanceId(entityId);
5082
+ childPrefix = `${prefix}${instanceId}${NODE_KEY_SEPARATOR}`;
5083
+ ownPrefix = childPrefix;
5084
+ boundaryIds.set(entityId, {
5085
+ url: instance.url,
5086
+ instanceId
5087
+ });
5088
+ }
5089
+ const nodeId = nodeValues.get(entityId)?.nodeId;
5090
+ if (typeof nodeId === "string" && nodeId.length > 0) {
5091
+ const key = `${ownPrefix}${nodeId}`;
5092
+ idToKey.set(entityId, key);
5093
+ keyToId.set(key, entityId);
5094
+ }
5095
+ for (const child of childrenByParent.get(entityId) ?? []) {
5096
+ queue.push({ entityId: child, prefix: childPrefix });
5097
+ }
5098
+ }
5099
+ const rootEntityId = scopeRoots[0];
5100
+ const rootNodeId = rootEntityId !== void 0 ? nodeValues.get(rootEntityId)?.nodeId : void 0;
5101
+ return {
5102
+ idToKey,
5103
+ keyToId,
5104
+ rootKey: typeof rootNodeId === "string" && rootNodeId.length > 0 ? rootNodeId : null,
5105
+ boundaryIds
5106
+ };
5107
+ }
5108
+ __name(buildNodeKeyMapsFromSnapshot, "buildNodeKeyMapsFromSnapshot");
5109
+ function collectSnapshotSubtreeIds(snapshot, rootEntityId) {
5110
+ const parentValues = readBlock(snapshot, "ParentId");
5111
+ const childrenByParent = /* @__PURE__ */ new Map();
5112
+ for (const entityId of snapshot.entities) {
5113
+ const parent = parentValues.get(entityId)?.value;
5114
+ if (typeof parent === "number") {
5115
+ const siblings = childrenByParent.get(parent);
5116
+ if (siblings) siblings.push(entityId);
5117
+ else childrenByParent.set(parent, [entityId]);
5118
+ }
5119
+ }
5120
+ const ordered = [];
5121
+ const seen = /* @__PURE__ */ new Set();
5122
+ const queue = [rootEntityId];
5123
+ while (queue.length > 0) {
5124
+ const entityId = queue.shift();
5125
+ if (seen.has(entityId)) continue;
5126
+ seen.add(entityId);
5127
+ ordered.push(entityId);
5128
+ queue.push(...childrenByParent.get(entityId) ?? []);
5129
+ }
5130
+ return ordered;
5131
+ }
5132
+ __name(collectSnapshotSubtreeIds, "collectSnapshotSubtreeIds");
5133
+ function findTopLevelNestedRegions(snapshot) {
5134
+ const parentValues = readBlock(snapshot, "ParentId");
5135
+ const instanceValues = readBlock(snapshot, "PrefabInstance");
5136
+ const inDocument = new Set(snapshot.entities);
5137
+ const roots = new Set(
5138
+ snapshot.entities.filter((entityId) => {
5139
+ const parent = parentValues.get(entityId)?.value;
5140
+ return typeof parent !== "number" || !inDocument.has(parent);
5141
+ })
5142
+ );
5143
+ const regions = [];
5144
+ for (const [entityId, instance] of instanceValues) {
5145
+ if (roots.has(entityId)) continue;
5146
+ if (typeof instance.url !== "string" || instance.url.length === 0) continue;
5147
+ let ancestor = parentValues.get(entityId)?.value;
5148
+ let nestedDeeper = false;
5149
+ let hops = 0;
5150
+ while (typeof ancestor === "number" && hops < 512) {
5151
+ if (instanceValues.has(ancestor) && !roots.has(ancestor)) {
5152
+ nestedDeeper = true;
5153
+ break;
5154
+ }
5155
+ ancestor = parentValues.get(ancestor)?.value;
5156
+ hops += 1;
5157
+ }
5158
+ if (nestedDeeper) continue;
5159
+ regions.push({
5160
+ rootEntityId: entityId,
5161
+ url: instance.url,
5162
+ instanceId: typeof instance.instanceId === "string" && instance.instanceId.length > 0 ? instance.instanceId : fallbackInstanceId(entityId),
5163
+ overrides: Array.isArray(instance.overrides) ? instance.overrides.filter(
5164
+ (entry) => typeof entry === "string"
5165
+ ) : []
5166
+ });
5167
+ }
5168
+ regions.sort((a, b) => a.rootEntityId - b.rootEntityId);
5169
+ return regions;
5170
+ }
5171
+ __name(findTopLevelNestedRegions, "findTopLevelNestedRegions");
5172
+
5017
5173
  // core/prefab/overrides.ts
5018
5174
  var OVERRIDE_DIFF_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
5019
5175
  "PrefabNode",
@@ -5027,10 +5183,10 @@ var OVERRIDE_ROOT_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
5027
5183
  "Name",
5028
5184
  "ParentId"
5029
5185
  ]);
5030
- var asRecord4 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
5186
+ var asRecord5 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
5031
5187
  var clone = /* @__PURE__ */ __name((value) => value === void 0 ? value : structuredClone(value), "clone");
5032
5188
  var isNodeRef = /* @__PURE__ */ __name((value) => {
5033
- const record = asRecord4(value);
5189
+ const record = asRecord5(value);
5034
5190
  return Boolean(
5035
5191
  record && typeof record.$node === "string" && Object.keys(record).length === 1
5036
5192
  );
@@ -5042,7 +5198,7 @@ function findSnapshotRootEntityIds(snapshot) {
5042
5198
  );
5043
5199
  const parentByEntity = /* @__PURE__ */ new Map();
5044
5200
  for (const entry of parentBlock?.entries ?? []) {
5045
- parentByEntity.set(entry.entityId, asRecord4(entry.value)?.value ?? null);
5201
+ parentByEntity.set(entry.entityId, asRecord5(entry.value)?.value ?? null);
5046
5202
  }
5047
5203
  return snapshot.entities.filter(
5048
5204
  (entityId) => typeof parentByEntity.get(entityId) !== "number"
@@ -5056,7 +5212,7 @@ function buildNodeMapsFromSnapshot(snapshot) {
5056
5212
  (block) => block.component === "PrefabNode"
5057
5213
  );
5058
5214
  for (const entry of nodeBlock?.entries ?? []) {
5059
- const nodeId = asRecord4(entry.value)?.nodeId;
5215
+ const nodeId = asRecord5(entry.value)?.nodeId;
5060
5216
  if (typeof nodeId === "string" && nodeId.length > 0) {
5061
5217
  idToNode.set(entry.entityId, nodeId);
5062
5218
  nodeToId.set(nodeId, entry.entityId);
@@ -5088,7 +5244,7 @@ var toRefOrRaw = /* @__PURE__ */ __name((id, idToNode) => {
5088
5244
  }, "toRefOrRaw");
5089
5245
  function canonicalizeComponentValue(componentName, value, idToNode) {
5090
5246
  const cloned = clone(value);
5091
- const record = asRecord4(cloned);
5247
+ const record = asRecord5(cloned);
5092
5248
  if (!record) {
5093
5249
  return cloned;
5094
5250
  }
@@ -5107,14 +5263,14 @@ function canonicalizeComponentValue(componentName, value, idToNode) {
5107
5263
  const entries = Array.isArray(record.entries) ? record.entries : [];
5108
5264
  return {
5109
5265
  entries: entries.map((rawEntry) => {
5110
- const entry = asRecord4(rawEntry);
5266
+ const entry = asRecord5(rawEntry);
5111
5267
  if (!entry) {
5112
5268
  return rawEntry;
5113
5269
  }
5114
- const data = asRecord4(entry.data) ?? {};
5270
+ const data = asRecord5(entry.data) ?? {};
5115
5271
  const canonicalData = {};
5116
5272
  for (const [key, fieldValue] of Object.entries(data)) {
5117
- const field = asRecord4(fieldValue);
5273
+ const field = asRecord5(fieldValue);
5118
5274
  if (field?.type === "entity" && typeof field.value === "number") {
5119
5275
  canonicalData[key] = {
5120
5276
  ...field,
@@ -5122,7 +5278,7 @@ function canonicalizeComponentValue(componentName, value, idToNode) {
5122
5278
  };
5123
5279
  continue;
5124
5280
  }
5125
- const componentRef = asRecord4(field?.value);
5281
+ const componentRef = asRecord5(field?.value);
5126
5282
  if (field?.type === "component" && componentRef && typeof componentRef.entityId === "number") {
5127
5283
  canonicalData[key] = {
5128
5284
  ...field,
@@ -5168,7 +5324,7 @@ function decanonicalizeComponentValue(value, nodeToId) {
5168
5324
  if (Array.isArray(value)) {
5169
5325
  return value.map((item) => decanonicalizeComponentValue(item, nodeToId));
5170
5326
  }
5171
- const record = asRecord4(value);
5327
+ const record = asRecord5(value);
5172
5328
  if (record) {
5173
5329
  const result = {};
5174
5330
  for (const [key, item] of Object.entries(record)) {
@@ -5271,15 +5427,20 @@ function collectDiffPaths(base, current, prefix = "", out = []) {
5271
5427
  return out;
5272
5428
  }
5273
5429
  __name(collectDiffPaths, "collectDiffPaths");
5274
- function computeInstanceOverrides(baseSnapshot, instanceSnapshot) {
5275
- const baseMaps = buildNodeMapsFromSnapshot(baseSnapshot);
5276
- const instanceMaps = buildNodeMapsFromSnapshot(instanceSnapshot);
5430
+ function computeInstanceOverrides(baseSnapshot, instanceSnapshot, options) {
5431
+ const baseMaps = buildNodeKeyMapsFromSnapshot(baseSnapshot);
5432
+ const instanceMaps = buildNodeKeyMapsFromSnapshot(instanceSnapshot);
5277
5433
  const baseByEntity = indexSnapshotByEntity(baseSnapshot);
5278
5434
  const instanceByEntity = indexSnapshotByEntity(instanceSnapshot);
5279
5435
  const overrides = [];
5280
- for (const [nodeId, instanceEntityId] of instanceMaps.nodeToId) {
5281
- const baseEntityId = baseMaps.nodeToId.get(nodeId);
5436
+ for (const [nodeKey, instanceEntityId] of instanceMaps.keyToId) {
5437
+ const baseEntityId = baseMaps.keyToId.get(nodeKey);
5282
5438
  if (baseEntityId === void 0) {
5439
+ if (options?.emitAdditionMarkers) {
5440
+ overrides.push(
5441
+ encodeOverride(nodeKey, ADDED_NODE_MARKER_COMPONENT, "")
5442
+ );
5443
+ }
5283
5444
  continue;
5284
5445
  }
5285
5446
  const baseComponents = baseByEntity.get(baseEntityId);
@@ -5287,7 +5448,7 @@ function computeInstanceOverrides(baseSnapshot, instanceSnapshot) {
5287
5448
  if (!baseComponents) {
5288
5449
  continue;
5289
5450
  }
5290
- const isRoot = nodeId === baseMaps.rootNodeId;
5451
+ const isRoot = nodeKey === baseMaps.rootKey;
5291
5452
  for (const [componentName, baseRaw] of baseComponents) {
5292
5453
  if (OVERRIDE_DIFF_EXCLUDED_COMPONENTS.has(componentName)) {
5293
5454
  continue;
@@ -5302,15 +5463,15 @@ function computeInstanceOverrides(baseSnapshot, instanceSnapshot) {
5302
5463
  const baseCanonical = canonicalizeComponentValue(
5303
5464
  componentName,
5304
5465
  baseRaw,
5305
- baseMaps.idToNode
5466
+ baseMaps.idToKey
5306
5467
  );
5307
5468
  const instanceCanonical = canonicalizeComponentValue(
5308
5469
  componentName,
5309
5470
  instanceRaw,
5310
- instanceMaps.idToNode
5471
+ instanceMaps.idToKey
5311
5472
  );
5312
5473
  for (const path of collectDiffPaths(baseCanonical, instanceCanonical)) {
5313
- overrides.push(encodeOverride(nodeId, componentName, path));
5474
+ overrides.push(encodeOverride(nodeKey, componentName, path));
5314
5475
  }
5315
5476
  }
5316
5477
  }
@@ -5324,7 +5485,7 @@ function getAtPath(value, tokens) {
5324
5485
  if (Array.isArray(cursor)) {
5325
5486
  cursor = cursor[Number(token)];
5326
5487
  } else {
5327
- const record = asRecord4(cursor);
5488
+ const record = asRecord5(cursor);
5328
5489
  if (!record) {
5329
5490
  return void 0;
5330
5491
  }
@@ -5344,7 +5505,7 @@ function setAtPath(target, tokens, value) {
5344
5505
  if (Array.isArray(cursor)) {
5345
5506
  cursor = cursor[Number(token)];
5346
5507
  } else {
5347
- const record = asRecord4(cursor);
5508
+ const record = asRecord5(cursor);
5348
5509
  if (!record) {
5349
5510
  return;
5350
5511
  }
@@ -5358,7 +5519,7 @@ function setAtPath(target, tokens, value) {
5358
5519
  if (Array.isArray(cursor)) {
5359
5520
  cursor[Number(last)] = value;
5360
5521
  } else {
5361
- const record = asRecord4(cursor);
5522
+ const record = asRecord5(cursor);
5362
5523
  if (record) {
5363
5524
  if (value === void 0) {
5364
5525
  delete record[last];
@@ -5390,15 +5551,20 @@ __name(mergeComponentValue, "mergeComponentValue");
5390
5551
  var INSTANTIATE_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
5391
5552
  "Environment",
5392
5553
  "Postprocessing",
5393
- "SelectionTransformState",
5394
- "PrefabInstance"
5554
+ "SelectionTransformState"
5395
5555
  ]);
5396
5556
  function instantiateSubtree(world, objectManagementSystem, snapshot) {
5557
+ const snapshotRootIds = new Set(findSnapshotRootEntityIds(snapshot));
5397
5558
  const safeSnapshot = {
5398
5559
  entities: snapshot.entities,
5399
- components: snapshot.components.filter(
5400
- (block) => !INSTANTIATE_EXCLUDED_COMPONENTS.has(block.component)
5401
- )
5560
+ components: snapshot.components.filter((block) => !INSTANTIATE_EXCLUDED_COMPONENTS.has(block.component)).map(
5561
+ (block) => block.component === "PrefabInstance" ? {
5562
+ component: block.component,
5563
+ entries: block.entries.filter(
5564
+ (entry) => !snapshotRootIds.has(entry.entityId)
5565
+ )
5566
+ } : block
5567
+ ).filter((block) => block.entries.length > 0)
5402
5568
  };
5403
5569
  const result = hydrateWorldFromSnapshotDetailed(
5404
5570
  safeSnapshot,
@@ -5418,6 +5584,11 @@ __name(instantiateSubtree, "instantiateSubtree");
5418
5584
 
5419
5585
  // core/prefab/types.ts
5420
5586
  var PREFAB_FILE_VERSION = 1;
5587
+ var PREFAB_FILE_VERSION_NESTED = 2;
5588
+ function prefabFileVersionFor(snapshot) {
5589
+ return findTopLevelNestedRegions(snapshot).length > 0 ? PREFAB_FILE_VERSION_NESTED : PREFAB_FILE_VERSION;
5590
+ }
5591
+ __name(prefabFileVersionFor, "prefabFileVersionFor");
5421
5592
  var isRecord = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
5422
5593
  function parsePrefabAsset(json) {
5423
5594
  if (!isRecord(json)) {
@@ -8360,10 +8531,10 @@ var _Behaviour = class _Behaviour {
8360
8531
  if (!this.threeRef) {
8361
8532
  return;
8362
8533
  }
8363
- const clone2 = this.threeRef.object.clone();
8534
+ const clone3 = this.threeRef.object.clone();
8364
8535
  const rotation = new THREE35__namespace.Quaternion();
8365
- clone2.lookAt(targetPosition);
8366
- clone2.getWorldQuaternion(rotation);
8536
+ clone3.lookAt(targetPosition);
8537
+ clone3.getWorldQuaternion(rotation);
8367
8538
  this.transform.setRotation(rotation);
8368
8539
  }
8369
8540
  /** Returns every component currently attached to this entity. */
@@ -10170,11 +10341,11 @@ __name(_World, "World");
10170
10341
  var World = _World;
10171
10342
 
10172
10343
  // core/prefab/snapshotIdUtils.ts
10173
- var asRecord5 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
10344
+ var asRecord6 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
10174
10345
  function remapScriptData(data, idMap, nullExternal, onExternal) {
10175
10346
  const result = {};
10176
10347
  for (const [key, value] of Object.entries(data)) {
10177
- const record = asRecord5(value);
10348
+ const record = asRecord6(value);
10178
10349
  if (!record) {
10179
10350
  result[key] = value;
10180
10351
  continue;
@@ -10189,7 +10360,7 @@ function remapScriptData(data, idMap, nullExternal, onExternal) {
10189
10360
  }
10190
10361
  continue;
10191
10362
  }
10192
- const componentRef = asRecord5(record.value);
10363
+ const componentRef = asRecord6(record.value);
10193
10364
  if (record.type === "component" && componentRef && typeof componentRef.entityId === "number") {
10194
10365
  const mapped = idMap.get(componentRef.entityId);
10195
10366
  if (mapped !== void 0) {
@@ -10227,7 +10398,7 @@ function remapSnapshotEntityIds(snapshot, idMap, options) {
10227
10398
  });
10228
10399
  }, "warn");
10229
10400
  let value = structuredClone(entry.value);
10230
- const record = asRecord5(value);
10401
+ const record = asRecord6(value);
10231
10402
  if (block.component === "ParentId" && record) {
10232
10403
  if (typeof record.value === "number") {
10233
10404
  const mapped = idMap.get(record.value);
@@ -10243,11 +10414,11 @@ function remapSnapshotEntityIds(snapshot, idMap, options) {
10243
10414
  } else if (block.component === "ScriptComponent" && record) {
10244
10415
  if (Array.isArray(record.entries)) {
10245
10416
  record.entries = record.entries.map((rawEntry, index) => {
10246
- const entryRecord = asRecord5(rawEntry);
10417
+ const entryRecord = asRecord6(rawEntry);
10247
10418
  if (!entryRecord) {
10248
10419
  return rawEntry;
10249
10420
  }
10250
- const data = asRecord5(entryRecord.data);
10421
+ const data = asRecord6(entryRecord.data);
10251
10422
  if (!data) {
10252
10423
  return rawEntry;
10253
10424
  }
@@ -10303,12 +10474,237 @@ function remapSnapshotEntityIds(snapshot, idMap, options) {
10303
10474
  }
10304
10475
  __name(remapSnapshotEntityIds, "remapSnapshotEntityIds");
10305
10476
 
10477
+ // core/prefab/applyPrefabToSnapshot.ts
10478
+ var asRecord7 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
10479
+ var clone2 = /* @__PURE__ */ __name((value) => structuredClone(value), "clone");
10480
+ var stableStringify = /* @__PURE__ */ __name((value) => {
10481
+ try {
10482
+ return JSON.stringify(value);
10483
+ } catch {
10484
+ return "";
10485
+ }
10486
+ }, "stableStringify");
10487
+ function getBlock(snapshot, component) {
10488
+ let block = snapshot.components.find((b) => b.component === component);
10489
+ if (!block) {
10490
+ block = { component, entries: [] };
10491
+ snapshot.components.push(block);
10492
+ }
10493
+ return block;
10494
+ }
10495
+ __name(getBlock, "getBlock");
10496
+ function setEntry(snapshot, component, entityId, value) {
10497
+ const block = getBlock(snapshot, component);
10498
+ const entry = block.entries.find((e) => e.entityId === entityId);
10499
+ if (entry) {
10500
+ entry.value = value;
10501
+ } else {
10502
+ block.entries.push({ entityId, value });
10503
+ block.entries.sort((a, b) => a.entityId - b.entityId);
10504
+ }
10505
+ }
10506
+ __name(setEntry, "setEntry");
10507
+ function deleteEntities(snapshot, entityIds) {
10508
+ snapshot.entities = snapshot.entities.filter((id) => !entityIds.has(id));
10509
+ snapshot.components = snapshot.components.map((block) => ({
10510
+ component: block.component,
10511
+ entries: block.entries.filter((e) => !entityIds.has(e.entityId))
10512
+ })).filter((block) => block.entries.length > 0);
10513
+ }
10514
+ __name(deleteEntities, "deleteEntities");
10515
+ function extractRegionDoc(hostSnapshot, regionIds) {
10516
+ const idSet = new Set(regionIds);
10517
+ return {
10518
+ entities: regionIds.slice(),
10519
+ components: hostSnapshot.components.map((block) => ({
10520
+ component: block.component,
10521
+ entries: block.entries.filter((e) => idSet.has(e.entityId))
10522
+ })).filter((block) => block.entries.length > 0)
10523
+ };
10524
+ }
10525
+ __name(extractRegionDoc, "extractRegionDoc");
10526
+ function applyPrefabToSnapshotInstances(hostSnapshot, nestedUrl, newNestedSnapshot) {
10527
+ const host = clone2(hostSnapshot);
10528
+ const newMaps = buildNodeKeyMapsFromSnapshot(newNestedSnapshot);
10529
+ const newByEntity = indexSnapshotByEntity(newNestedSnapshot);
10530
+ if (!newMaps.rootKey) {
10531
+ return { snapshot: host, changed: false };
10532
+ }
10533
+ let changed = false;
10534
+ const regions = findTopLevelNestedRegions(host).filter(
10535
+ (region) => region.url === nestedUrl
10536
+ );
10537
+ for (const region of regions) {
10538
+ const before = stableStringify(
10539
+ extractRegionDoc(host, collectSnapshotSubtreeIds(host, region.rootEntityId))
10540
+ );
10541
+ patchRegion(host, region.rootEntityId, region.overrides, {
10542
+ newSnapshot: newNestedSnapshot,
10543
+ newMaps,
10544
+ newByEntity
10545
+ });
10546
+ const after = stableStringify(
10547
+ extractRegionDoc(host, collectSnapshotSubtreeIds(host, region.rootEntityId))
10548
+ );
10549
+ if (before !== after) {
10550
+ changed = true;
10551
+ }
10552
+ }
10553
+ return { snapshot: host, changed };
10554
+ }
10555
+ __name(applyPrefabToSnapshotInstances, "applyPrefabToSnapshotInstances");
10556
+ function patchRegion(host, regionRootId, storedOverrides, file) {
10557
+ const { newSnapshot, newMaps, newByEntity } = file;
10558
+ const regionIds = collectSnapshotSubtreeIds(host, regionRootId);
10559
+ const regionDoc = extractRegionDoc(host, regionIds);
10560
+ const regionMaps = buildNodeKeyMapsFromSnapshot(regionDoc, {
10561
+ scopeRootEntityId: regionRootId
10562
+ });
10563
+ const regionByEntity = indexSnapshotByEntity(regionDoc);
10564
+ const grouped = groupOverridePaths(storedOverrides);
10565
+ const overridePathsFor = /* @__PURE__ */ __name((key, component) => grouped.get(key)?.get(component), "overridePathsFor");
10566
+ const isMarkedAddition = /* @__PURE__ */ __name((key) => Boolean(grouped.get(key)?.get(ADDED_NODE_MARKER_COMPONENT)), "isMarkedAddition");
10567
+ const parentBlockNew = newSnapshot.components.find(
10568
+ (b) => b.component === "ParentId"
10569
+ );
10570
+ const keyToHostId = /* @__PURE__ */ new Map();
10571
+ for (const [hostId, key] of regionMaps.idToKey) {
10572
+ keyToHostId.set(key, hostId);
10573
+ }
10574
+ const addedFileIds = [...newMaps.keyToId.entries()].filter(([key]) => !keyToHostId.has(key)).map(([, fileId]) => fileId);
10575
+ if (addedFileIds.length > 0) {
10576
+ let nextHostId = Math.max(0, ...host.entities) + 1;
10577
+ const addedSet = new Set(addedFileIds);
10578
+ const idMap = /* @__PURE__ */ new Map();
10579
+ for (const [key, fileId] of newMaps.keyToId) {
10580
+ const hostId = keyToHostId.get(key);
10581
+ if (hostId !== void 0) {
10582
+ idMap.set(fileId, hostId);
10583
+ }
10584
+ }
10585
+ for (const fileId of addedFileIds) {
10586
+ idMap.set(fileId, nextHostId);
10587
+ nextHostId += 1;
10588
+ }
10589
+ const addedSub = {
10590
+ entities: newSnapshot.entities.filter((id) => addedSet.has(id)),
10591
+ components: newSnapshot.components.map((block) => ({
10592
+ component: block.component,
10593
+ entries: block.entries.filter((e) => addedSet.has(e.entityId))
10594
+ })).filter((block) => block.entries.length > 0)
10595
+ };
10596
+ const { snapshot: remapped } = remapSnapshotEntityIds(addedSub, idMap, {
10597
+ externalRefPolicy: "null"
10598
+ });
10599
+ host.entities = [...host.entities, ...remapped.entities];
10600
+ for (const block of remapped.components) {
10601
+ for (const entry of block.entries) {
10602
+ setEntry(host, block.component, entry.entityId, entry.value);
10603
+ }
10604
+ }
10605
+ for (const fileId of addedFileIds) {
10606
+ const hostId = idMap.get(fileId);
10607
+ const parentValue = asRecord7(
10608
+ parentBlockNew?.entries.find((e) => e.entityId === fileId)?.value
10609
+ )?.value;
10610
+ if (typeof parentValue === "number" && !addedSet.has(parentValue)) {
10611
+ const parentKey = newMaps.idToKey.get(parentValue);
10612
+ const parentHost = parentKey ? keyToHostId.get(parentKey) : void 0;
10613
+ setEntry(host, "ParentId", hostId, {
10614
+ __type: "ParentId",
10615
+ value: parentHost ?? regionRootId
10616
+ });
10617
+ }
10618
+ const key = newMaps.idToKey.get(fileId);
10619
+ if (key) {
10620
+ keyToHostId.set(key, hostId);
10621
+ }
10622
+ }
10623
+ }
10624
+ for (const [key, fileId] of newMaps.keyToId) {
10625
+ const hostId = keyToHostId.get(key);
10626
+ if (hostId === void 0) continue;
10627
+ const isRegionRoot = key === newMaps.rootKey;
10628
+ const fileComponents = newByEntity.get(fileId);
10629
+ if (!fileComponents) continue;
10630
+ const regionEntityComponents = regionByEntity.get(hostId);
10631
+ for (const [componentName, fileRaw] of fileComponents) {
10632
+ if (OVERRIDE_DIFF_EXCLUDED_COMPONENTS.has(componentName)) continue;
10633
+ if (isRegionRoot && OVERRIDE_ROOT_EXCLUDED_COMPONENTS.has(componentName))
10634
+ continue;
10635
+ const fileCanonical = canonicalizeComponentValue(
10636
+ componentName,
10637
+ fileRaw,
10638
+ newMaps.idToKey
10639
+ );
10640
+ const regionRaw = regionEntityComponents?.get(componentName);
10641
+ const regionCanonical = regionRaw === void 0 ? void 0 : canonicalizeComponentValue(
10642
+ componentName,
10643
+ regionRaw,
10644
+ regionMaps.idToKey
10645
+ );
10646
+ const merged = regionCanonical === void 0 ? fileCanonical : mergeComponentValue(
10647
+ fileCanonical,
10648
+ regionCanonical,
10649
+ overridePathsFor(key, componentName)
10650
+ );
10651
+ if (regionCanonical !== void 0 && stableStringify(merged) === stableStringify(regionCanonical)) {
10652
+ continue;
10653
+ }
10654
+ const hostValue = decanonicalizeComponentValue(merged, keyToHostId);
10655
+ setEntry(host, componentName, hostId, hostValue);
10656
+ }
10657
+ }
10658
+ const removedHostIds = /* @__PURE__ */ new Set();
10659
+ for (const [key, hostId] of keyToHostId) {
10660
+ if (newMaps.keyToId.has(key)) continue;
10661
+ if (isMarkedAddition(key)) continue;
10662
+ removedHostIds.add(hostId);
10663
+ }
10664
+ if (removedHostIds.size > 0) {
10665
+ const regionIdSet = new Set(regionIds);
10666
+ const parentBlockHost = host.components.find(
10667
+ (b) => b.component === "ParentId"
10668
+ );
10669
+ for (const entry of parentBlockHost?.entries ?? []) {
10670
+ const record = asRecord7(entry.value);
10671
+ if (!removedHostIds.has(entry.entityId) && regionIdSet.has(entry.entityId) && record && typeof record.value === "number" && removedHostIds.has(record.value)) {
10672
+ record.value = regionRootId;
10673
+ }
10674
+ }
10675
+ deleteEntities(host, removedHostIds);
10676
+ }
10677
+ const refreshedRegionIds = collectSnapshotSubtreeIds(host, regionRootId);
10678
+ const refreshedDoc = extractRegionDoc(host, refreshedRegionIds);
10679
+ const refreshedOverrides = computeInstanceOverrides(
10680
+ newSnapshot,
10681
+ refreshedDoc,
10682
+ { emitAdditionMarkers: true }
10683
+ );
10684
+ const instanceEntry = asRecord7(
10685
+ host.components.find((b) => b.component === "PrefabInstance")?.entries.find((e) => e.entityId === regionRootId)?.value
10686
+ );
10687
+ if (instanceEntry) {
10688
+ instanceEntry.overrides = refreshedOverrides;
10689
+ }
10690
+ }
10691
+ __name(patchRegion, "patchRegion");
10692
+
10693
+ // core/prefab/dependencies.ts
10694
+ function collectDirectPrefabDependencies(snapshot) {
10695
+ const urls = /* @__PURE__ */ new Set();
10696
+ for (const region of findTopLevelNestedRegions(snapshot)) {
10697
+ urls.add(region.url);
10698
+ }
10699
+ return [...urls];
10700
+ }
10701
+ __name(collectDirectPrefabDependencies, "collectDirectPrefabDependencies");
10702
+
10306
10703
  // core/prefab/serializeEntitySubtree.ts
10307
10704
  var SUBTREE_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
10308
10705
  "Environment",
10309
10706
  "Postprocessing",
10310
- "SelectionTransformState",
10311
- "PrefabInstance"
10707
+ "SelectionTransformState"
10312
10708
  ]);
10313
10709
  function serializeEntitySubtree(world, rootId, options) {
10314
10710
  const orderedIds = collectEntitySubtree(world, [rootId]);
@@ -10340,6 +10736,19 @@ function serializeEntitySubtree(world, rootId, options) {
10340
10736
  if (rootParentEntry && rootParentEntry.value !== null && typeof rootParentEntry.value === "object") {
10341
10737
  rootParentEntry.value.value = null;
10342
10738
  }
10739
+ const prefabInstanceBlock = snapshot.components.find(
10740
+ (block) => block.component === "PrefabInstance"
10741
+ );
10742
+ if (prefabInstanceBlock) {
10743
+ prefabInstanceBlock.entries = prefabInstanceBlock.entries.filter(
10744
+ (entry) => entry.entityId !== rootFileId
10745
+ );
10746
+ if (prefabInstanceBlock.entries.length === 0) {
10747
+ snapshot.components = snapshot.components.filter(
10748
+ (block) => block !== prefabInstanceBlock
10749
+ );
10750
+ }
10751
+ }
10343
10752
  const filteredWarnings = warnings.filter(
10344
10753
  (warning) => !(warning.component === "ParentId" && warning.entityId === rootId)
10345
10754
  );
@@ -10347,6 +10756,7 @@ function serializeEntitySubtree(world, rootId, options) {
10347
10756
  }
10348
10757
  __name(serializeEntitySubtree, "serializeEntitySubtree");
10349
10758
 
10759
+ exports.ADDED_NODE_MARKER_COMPONENT = ADDED_NODE_MARKER_COMPONENT;
10350
10760
  exports.AnimationStateMachine = AnimationStateMachine;
10351
10761
  exports.AnimationStateManager = AnimationStateManager;
10352
10762
  exports.AnimationSystem = AnimationSystem;
@@ -10363,11 +10773,13 @@ exports.KeyboardKeys = KeyboardKeys;
10363
10773
  exports.LightSyncSystem = LightSyncSystem;
10364
10774
  exports.MaterialSyncSystem = MaterialSyncSystem;
10365
10775
  exports.MeshGeometrySystem = MeshGeometrySystem;
10776
+ exports.NODE_KEY_SEPARATOR = NODE_KEY_SEPARATOR;
10366
10777
  exports.OVERRIDE_DIFF_EXCLUDED_COMPONENTS = OVERRIDE_DIFF_EXCLUDED_COMPONENTS;
10367
10778
  exports.OVERRIDE_ROOT_EXCLUDED_COMPONENTS = OVERRIDE_ROOT_EXCLUDED_COMPONENTS;
10368
10779
  exports.ObjectManagementSystem = ObjectManagementSystem;
10369
10780
  exports.ObjectVisibilitySystem = ObjectVisibilitySystem;
10370
10781
  exports.PREFAB_FILE_VERSION = PREFAB_FILE_VERSION;
10782
+ exports.PREFAB_FILE_VERSION_NESTED = PREFAB_FILE_VERSION_NESTED;
10371
10783
  exports.PhysicsSyncSystem = PhysicsSyncSystem;
10372
10784
  exports.PostprocessingSyncSystem = PostprocessingSyncSystem;
10373
10785
  exports.RenderSystem = RenderSystem;
@@ -10376,13 +10788,17 @@ exports.ScriptBehaviourSystem = ScriptBehaviourSystem;
10376
10788
  exports.TransformSyncSystem = TransformSyncSystem;
10377
10789
  exports.VideoSystem = VideoSystem;
10378
10790
  exports.World = World;
10791
+ exports.applyPrefabToSnapshotInstances = applyPrefabToSnapshotInstances;
10379
10792
  exports.buildComponentValueMaps = buildComponentValueMaps;
10793
+ exports.buildNodeKeyMapsFromSnapshot = buildNodeKeyMapsFromSnapshot;
10380
10794
  exports.buildNodeMapsFromSnapshot = buildNodeMapsFromSnapshot;
10381
10795
  exports.canonicalizeComponentValue = canonicalizeComponentValue;
10382
10796
  exports.clearModelCache = clearModelCache;
10383
10797
  exports.clearPrefabCache = clearPrefabCache;
10384
10798
  exports.collectDiffPaths = collectDiffPaths;
10799
+ exports.collectDirectPrefabDependencies = collectDirectPrefabDependencies;
10385
10800
  exports.collectEntitySubtree = collectEntitySubtree;
10801
+ exports.collectSnapshotSubtreeIds = collectSnapshotSubtreeIds;
10386
10802
  exports.computeInstanceOverrides = computeInstanceOverrides;
10387
10803
  exports.createAnimationComponent = createAnimationComponent;
10388
10804
  exports.createAudioComponent = createAudioComponent;
@@ -10414,6 +10830,7 @@ exports.decanonicalizeComponentValue = decanonicalizeComponentValue;
10414
10830
  exports.despawnPrefab = despawnPrefab;
10415
10831
  exports.encodeOverride = encodeOverride;
10416
10832
  exports.findSnapshotRootEntityIds = findSnapshotRootEntityIds;
10833
+ exports.findTopLevelNestedRegions = findTopLevelNestedRegions;
10417
10834
  exports.groupOverridePaths = groupOverridePaths;
10418
10835
  exports.hydrateWorldFromSnapshot = hydrateWorldFromSnapshot;
10419
10836
  exports.hydrateWorldFromSnapshotDetailed = hydrateWorldFromSnapshotDetailed;
@@ -10424,6 +10841,7 @@ exports.loadSharedGltf = loadSharedGltf;
10424
10841
  exports.mergeComponentValue = mergeComponentValue;
10425
10842
  exports.parseOverride = parseOverride;
10426
10843
  exports.parsePrefabAsset = parsePrefabAsset;
10844
+ exports.prefabFileVersionFor = prefabFileVersionFor;
10427
10845
  exports.registerDefaultComponentFactories = registerDefaultComponentFactories;
10428
10846
  exports.registerDefaultFactories = registerDefaultFactories;
10429
10847
  exports.remapSnapshotEntityIds = remapSnapshotEntityIds;