@forgeax/engine-scene 0.0.0-dev.8d955ade1c79

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 (84) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +55 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/asset-owner.integration.test.d.ts +2 -0
  5. package/dist/__tests__/asset-owner.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/components.unit.test.d.ts +2 -0
  7. package/dist/__tests__/components.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/diagnostic-type-surface.test-d.d.ts +3 -0
  9. package/dist/__tests__/diagnostic-type-surface.test-d.d.ts.map +1 -0
  10. package/dist/__tests__/direct-child-of-import.unit.test.d.ts +2 -0
  11. package/dist/__tests__/direct-child-of-import.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts +8 -0
  13. package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts.map +1 -0
  14. package/dist/__tests__/hierarchy-projection-contract.unit.test.d.ts +2 -0
  15. package/dist/__tests__/hierarchy-projection-contract.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/hierarchy-projection.unit.test.d.ts +2 -0
  17. package/dist/__tests__/hierarchy-projection.unit.test.d.ts.map +1 -0
  18. package/dist/__tests__/plugin.integration.test.d.ts +2 -0
  19. package/dist/__tests__/plugin.integration.test.d.ts.map +1 -0
  20. package/dist/__tests__/propagation.unit.test.d.ts +2 -0
  21. package/dist/__tests__/propagation.unit.test.d.ts.map +1 -0
  22. package/dist/__tests__/transform-change-journal.unit.test.d.ts +2 -0
  23. package/dist/__tests__/transform-change-journal.unit.test.d.ts.map +1 -0
  24. package/dist/assets/scene-decoder.d.ts +8 -0
  25. package/dist/assets/scene-decoder.d.ts.map +1 -0
  26. package/dist/collect-subtree.d.ts +4 -0
  27. package/dist/collect-subtree.d.ts.map +1 -0
  28. package/dist/components/child-of.d.ts +31 -0
  29. package/dist/components/child-of.d.ts.map +1 -0
  30. package/dist/components/children.d.ts +48 -0
  31. package/dist/components/children.d.ts.map +1 -0
  32. package/dist/components/morph-weights.d.ts +5 -0
  33. package/dist/components/morph-weights.d.ts.map +1 -0
  34. package/dist/components/name.d.ts +4 -0
  35. package/dist/components/name.d.ts.map +1 -0
  36. package/dist/components/transform.d.ts +52 -0
  37. package/dist/components/transform.d.ts.map +1 -0
  38. package/dist/errors.d.ts +22 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/index.d.ts +15 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.mjs +1731 -0
  43. package/dist/index.mjs.map +1 -0
  44. package/dist/instances/collect-profile.d.ts +7 -0
  45. package/dist/instances/collect-profile.d.ts.map +1 -0
  46. package/dist/instances/externalization.d.ts +14 -0
  47. package/dist/instances/externalization.d.ts.map +1 -0
  48. package/dist/instances/scene-instances.d.ts +350 -0
  49. package/dist/instances/scene-instances.d.ts.map +1 -0
  50. package/dist/plugin.d.ts +3 -0
  51. package/dist/plugin.d.ts.map +1 -0
  52. package/dist/systems/hierarchy-projection.d.ts +16 -0
  53. package/dist/systems/hierarchy-projection.d.ts.map +1 -0
  54. package/dist/systems/index.d.ts +3 -0
  55. package/dist/systems/index.d.ts.map +1 -0
  56. package/dist/systems/propagate-transforms.d.ts +15 -0
  57. package/dist/systems/propagate-transforms.d.ts.map +1 -0
  58. package/package.json +58 -0
  59. package/src/__tests__/asset-owner.integration.test.ts +57 -0
  60. package/src/__tests__/components.unit.test.ts +26 -0
  61. package/src/__tests__/diagnostic-type-surface.test-d.ts +11 -0
  62. package/src/__tests__/direct-child-of-import.unit.test.ts +13 -0
  63. package/src/__tests__/fixtures/malformed-hierarchy-edge.ts +15 -0
  64. package/src/__tests__/hierarchy-projection-contract.unit.test.ts +45 -0
  65. package/src/__tests__/hierarchy-projection.unit.test.ts +92 -0
  66. package/src/__tests__/plugin.integration.test.ts +23 -0
  67. package/src/__tests__/propagation.unit.test.ts +163 -0
  68. package/src/__tests__/transform-change-journal.unit.test.ts +105 -0
  69. package/src/assets/scene-decoder.ts +153 -0
  70. package/src/collect-subtree.ts +30 -0
  71. package/src/components/child-of.ts +90 -0
  72. package/src/components/children.ts +113 -0
  73. package/src/components/morph-weights.ts +6 -0
  74. package/src/components/name.ts +22 -0
  75. package/src/components/transform.ts +89 -0
  76. package/src/errors.ts +34 -0
  77. package/src/index.ts +65 -0
  78. package/src/instances/collect-profile.ts +10 -0
  79. package/src/instances/externalization.ts +137 -0
  80. package/src/instances/scene-instances.ts +1564 -0
  81. package/src/plugin.ts +28 -0
  82. package/src/systems/hierarchy-projection.ts +134 -0
  83. package/src/systems/index.ts +11 -0
  84. package/src/systems/propagate-transforms.ts +376 -0
@@ -0,0 +1,7 @@
1
+ /** Immutable Scene collection policy shared by runtime collectors. */
2
+ export interface SceneCollectProfile {
3
+ readonly includeComponent: (componentName: string, transient: boolean) => boolean;
4
+ readonly includeField: (componentName: string, fieldName: string, transient: boolean) => boolean;
5
+ }
6
+ export declare const SCENE_COLLECT_PROFILE: SceneCollectProfile;
7
+ //# sourceMappingURL=collect-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-profile.d.ts","sourceRoot":"","sources":["../../src/instances/collect-profile.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC;IAClF,QAAQ,CAAC,YAAY,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC;CAClG;AAED,eAAO,MAAM,qBAAqB,EAAE,mBAGlC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { AssetRef, SceneAsset } from '@forgeax/engine-types';
2
+ import { type Result } from '@forgeax/engine-types';
3
+ export type SceneComponentSchemaResolver = (componentName: string) => Readonly<Record<string, string>> | undefined;
4
+ export interface SceneExternalizationError {
5
+ readonly field: string;
6
+ readonly value: unknown;
7
+ }
8
+ export interface ExternalizedSceneAsset {
9
+ readonly payload: Record<string, unknown>;
10
+ readonly refs: readonly AssetRef[];
11
+ }
12
+ /** Project a SceneAsset's shared asset fields into a payload plus indexed refs. */
13
+ export declare function externalizeSceneAsset(scene: SceneAsset, resolveSchema: SceneComponentSchemaResolver): Result<ExternalizedSceneAsset, SceneExternalizationError>;
14
+ //# sourceMappingURL=externalization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"externalization.d.ts","sourceRoot":"","sources":["../../src/instances/externalization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAiB,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,4BAA4B,GAAG,CACzC,aAAa,EAAE,MAAM,KAClB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AA+BD,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,4BAA4B,GAC1C,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAsF3D"}
@@ -0,0 +1,350 @@
1
+ import { type Component, type ComponentData, type ComponentSchema, type EcsError, type EntityHandle, type ShapeOf, type World } from '@forgeax/engine-ecs';
2
+ import type { Handle, LocalEntityId, MountOverride, SceneAsset, SceneInstanceMount } from '@forgeax/engine-types';
3
+ import { type Result } from '@forgeax/engine-types';
4
+ /**
5
+ * C-R2 (feat-20260622-s5 / studio-issues): one structured, non-fatal record of
6
+ * a SceneAsset payload field that did NOT match the target component's schema.
7
+ *
8
+ * Scene data is loader-fed and may carry a stale / deprecated / typo'd field
9
+ * (an editor renames a field, an old `.pack.json` lags). `worldInstantiateScene`
10
+ * does NOT blank the whole scene over one such field (#478 lesson: a
11
+ * prod-silent strip re-introduced an invisible-entity class) and does NOT abort
12
+ * fatally. Instead it skips the unknown key (no write, no input mutation) and
13
+ * surfaces this record on the success value's `diagnostics[]` — observable in
14
+ * production (NOT NODE_ENV-gated), consumed by property access (no string parse):
15
+ *
16
+ * const r = worldInstantiateScene(world, handle);
17
+ * if (r.ok) for (const d of r.value.diagnostics)
18
+ * console.warn(`unknown field ${d.component}.${d.field} on localId ${d.localId}`);
19
+ *
20
+ * Direct `world.spawn` / `world.addComponent` / `Commands.spawn` stay fail-fast
21
+ * with `SpawnDataUnknownFieldError` — those are explicit API calls where a typo
22
+ * is a programming error, not loader-fed data.
23
+ */
24
+ export type SceneInstantiateDiagnostic = {
25
+ /** Component name (schema key) the unknown field appeared under. */
26
+ readonly component: string;
27
+ /** The offending field name not declared in the component schema. */
28
+ readonly field: string;
29
+ /** LocalEntityId (within its owning SceneAsset) of the carrying entity. */
30
+ readonly localId: number;
31
+ };
32
+ /**
33
+ * Success value of `worldInstantiateScene`. `root` is the synthetic scene-root
34
+ * EntityHandle (carries `SceneInstance`); `diagnostics` is the (possibly empty)
35
+ * list of non-fatal unknown-field records aggregated across this scene and every
36
+ * recursively mounted sub-scene (C-R2). Empty array = no diagnostics.
37
+ */
38
+ export type SceneInstantiateOk = {
39
+ readonly root: EntityHandle;
40
+ readonly diagnostics: readonly SceneInstantiateDiagnostic[];
41
+ };
42
+ /**
43
+ * Success value of `worldInstantiateSceneFlat` — the "edit the scene itself"
44
+ * primitive. Unlike `instantiateScene`, NO synthetic SceneInstance root is
45
+ * minted and NO `ChildOf` is forced onto top-level members: the scene's own
46
+ * entities become plain top-level world entities whose hierarchy is exactly
47
+ * their authored `ChildOf` (an entity with no `ChildOf` stays a root). `roots`
48
+ * is the set of those top-level handles (own rootless entities + top-level
49
+ * mount carriers). Nested prefabs inside the scene STILL materialise as their
50
+ * own SceneInstance anchors (charter P4: instance == entity-with-SceneInstance)
51
+ * — only THIS scene is flat.
52
+ */
53
+ export type SceneInstantiateFlatOk = {
54
+ readonly roots: EntityHandle[];
55
+ /**
56
+ * All mount carrier entities spawned while flattening this scene. These are
57
+ * separate from `roots`: carriers with an authored parent are not roots,
58
+ * but still delimit a nested prefab subtree for post-spawn hooks.
59
+ */
60
+ readonly mountEntities: EntityHandle[];
61
+ readonly diagnostics: readonly SceneInstantiateDiagnostic[];
62
+ };
63
+ /**
64
+ * @internal Intermediate produced by `_spawnSceneMembers` and consumed by both
65
+ * the anchor finisher (`_instantiateSceneAsset`) and the flat finisher
66
+ * (`_instantiateSceneAssetFlat`). Holds everything the shared member-spawn
67
+ * (mounts recursion + own-entity spawn + deferred owned-parent wiring) computes,
68
+ * before either finisher decides whether to wrap the members in a synthetic
69
+ * SceneInstance root.
70
+ */
71
+ export interface SceneMembersSpawn {
72
+ /** LocalEntityId → live Entity u32 (ENTITY_NULL_RAW for unspawned slots). */
73
+ readonly mapping: Uint32Array;
74
+ /** Reverse map live Entity → LocalEntityId for override / detach bookkeeping. */
75
+ readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
76
+ /** Own entities that carried no `ChildOf` — the scene's authored top-level roots. */
77
+ readonly rootEntities: EntityHandle[];
78
+ /** Mount carriers whose `mount.parent === undefined` (default-parented). */
79
+ readonly mountEntitiesNeedingRootParent: EntityHandle[];
80
+ /** Every mount carrier spawned by this scene, including explicitly parented carriers. */
81
+ readonly mountEntities: EntityHandle[];
82
+ /**
83
+ * The child anchor and mapping for each mount. Flat scene opening has no
84
+ * outer SceneInstance state to own parent-namespace mount overrides, so it
85
+ * records those overrides on this child anchor after the shared spawn pass.
86
+ */
87
+ readonly mountInstances: readonly {
88
+ readonly mount: SceneInstanceMount;
89
+ readonly root: EntityHandle;
90
+ readonly mapping: Uint32Array;
91
+ }[];
92
+ /** `entities.length + mounts + Σ memberCount`, captured at instantiate-time. */
93
+ readonly totalSlots: number;
94
+ }
95
+ export type SceneAssetResolver = (source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>) => Result<Handle<'SceneAsset', 'shared'>, unknown>;
96
+ /** @internal */
97
+ export declare function worldSetSceneAssetResolver(world: World, resolver: SceneAssetResolver): void;
98
+ /** @internal */
99
+ export declare function worldGetSceneAssetResolver(world: World): SceneAssetResolver | null;
100
+ /**
101
+ * Materialise a SceneAsset (and any nested SceneAsset references via
102
+ * `mounts[]`) into live entities. Returns the synthetic root Entity that
103
+ * carries the `SceneInstance` ECS component (charter P4: instance ==
104
+ * entity-with-SceneInstance).
105
+ *
106
+ * Recursion path is closed inside `_instantiateSceneRec(handle, parent,
107
+ * stack)` (D-3); cycle detection is fail-fast `pack-cyclic-reference +
108
+ * detail.kind:'mount-asset'` (D-1 mirror, plan-strategy §D-3). The
109
+ * caller-supplied `parent` flows to the synthetic root's `ChildOf` so the
110
+ * full sub-tree attaches under the AI user's host entity.
111
+ *
112
+ * @example
113
+ * const r = worldInstantiateScene(world, handle);
114
+ * if (!r.ok) return r;
115
+ * const { root, diagnostics } = r.value;
116
+ * for (const d of diagnostics) // C-R2: unknown-field records, non-fatal
117
+ * console.warn(`unknown field ${d.component}.${d.field} on localId ${d.localId}`);
118
+ * const inst = world.get(root, SceneInstance).value;
119
+ * const member = inst.mapping[0]; // first member entity
120
+ */
121
+ export declare function worldInstantiateScene(world: World, handle: Handle<'SceneAsset', 'shared'>, parent?: EntityHandle): Result<SceneInstantiateOk, EcsError>;
122
+ /**
123
+ * Materialise a projected `SceneAsset` payload without exposing the temporary
124
+ * shared-ref handle to the caller. The World remains the owner of both the
125
+ * handle and the instantiated SceneInstance: the producer grant is released
126
+ * after the SceneInstance retains its source, including when instantiation
127
+ * fails. This is the payload-shaped counterpart to `worldInstantiateScene` for
128
+ * hosts that load a SceneAsset directly from the Engine AssetRegistry.
129
+ */
130
+ export declare function worldInstantiateScenePayload(world: World, asset: SceneAsset, parent?: EntityHandle): Result<SceneInstantiateOk, EcsError>;
131
+ /**
132
+ * Materialise a SceneAsset FLAT — the "edit the scene itself" primitive.
133
+ * Unlike `instantiateScene`, this mints NO synthetic SceneInstance root and
134
+ * forces NO `ChildOf` onto top-level members: the scene's own entities become
135
+ * plain top-level world entities whose hierarchy is exactly their authored
136
+ * `ChildOf` (an entity with no `ChildOf` is a root). Use this to OPEN a scene
137
+ * for editing; use `instantiateScene` (anchor) at runtime / for nested
138
+ * prefabs where an instance boundary + override isolation is wanted.
139
+ *
140
+ * Nested prefabs referenced via `mounts[]` STILL materialise as their own
141
+ * SceneInstance anchors (charter P4 preserved) — only THIS top scene is flat.
142
+ *
143
+ * @example
144
+ * const r = worldInstantiateSceneFlat(world, handle);
145
+ * if (!r.ok) return r;
146
+ * const { roots, diagnostics } = r.value; // roots = top-level handles
147
+ */
148
+ export declare function worldInstantiateSceneFlat(world: World, handle: Handle<'SceneAsset', 'shared'>): Result<SceneInstantiateFlatOk, EcsError>;
149
+ /**
150
+ * @internal Recursive helper carrying the cycle-detection stack. Sugar /
151
+ * other public callers must not see this mechanic — use `instantiateScene`
152
+ * (D-3 / charter P1).
153
+ */
154
+ export declare function worldInstantiateSceneRec(world: World, handle: Handle<'SceneAsset', 'shared'>, parent: EntityHandle | undefined, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
155
+ /**
156
+ * @internal Resolve a SceneAsset handle through the SharedRefStore.
157
+ * The handle u32 is the SharedRefStore slot id (`world.allocSharedRef
158
+ * ('SceneAsset', asset)` is the producer; rc starts at 1, the SceneInstance
159
+ * spawn retains to rc=2 in M4 / w13). Errors propagate as EcsError so the
160
+ * instantiateScene chain returns a single closed union.
161
+ */
162
+ export declare function worldResolveSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>): Result<SceneAsset, EcsError>;
163
+ /**
164
+ * @internal Spawn one SceneAsset's members — the shared body of both scene
165
+ * finishers. Recurses into `mounts[]` (each nested prefab becomes its own
166
+ * SceneInstance anchor), spawns `entities[]` honouring their authored
167
+ * `ChildOf`, and wires deferred owned-parent mount edges. Does NOT create a
168
+ * synthetic root or force any `ChildOf` — that is the caller's (finisher's)
169
+ * job. `_instantiateSceneRec` owns cycle bookkeeping.
170
+ */
171
+ export declare function worldSpawnSceneMembers(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<SceneMembersSpawn, EcsError>;
172
+ /**
173
+ * @internal Spawn one SceneAsset's entities + apply mounts recursively, then
174
+ * wrap them in a synthetic SceneInstance root (the anchor). This is the
175
+ * runtime / Play / nested-mount finisher (charter P4: instance ==
176
+ * entity-with-SceneInstance). Caller (`_instantiateSceneRec`) owns cycle
177
+ * bookkeeping.
178
+ */
179
+ export declare function worldInstantiateSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, parent: EntityHandle | undefined, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
180
+ /**
181
+ * @internal Flat finisher — spawn one SceneAsset's members WITHOUT wrapping
182
+ * them in a synthetic SceneInstance root and WITHOUT forcing `ChildOf` onto
183
+ * top-level members. Used for "opening a scene to edit": the scene's own
184
+ * entities become plain top-level world entities whose hierarchy is exactly
185
+ * their authored `ChildOf`. Nested prefabs inside still materialise as their
186
+ * own SceneInstance anchors (the mount recursion in `_spawnSceneMembers` is
187
+ * always anchored). Returns the top-level handles (own rootless entities +
188
+ * top-level mount carriers).
189
+ */
190
+ export declare function worldInstantiateSceneAssetFlat(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<{
191
+ roots: EntityHandle[];
192
+ mountEntities: EntityHandle[];
193
+ }, EcsError>;
194
+ /** @internal Build ComponentData[] for one SceneEntity, remapping localIds.
195
+ *
196
+ * C-R2 (feat-20260622-s5 M6): unknown fields on a SceneAsset payload are NOT
197
+ * fatal. Unlike `world.spawn` (an explicit API call where a typo is a
198
+ * programming error -> `SpawnDataUnknownFieldError`), scene data is loader-fed
199
+ * and may carry a stale / deprecated / typo'd field. The remap below builds a
200
+ * fresh `remappedRaw` and simply SKIPS keys absent from the schema (no input
201
+ * mutation — the source `raw` is never deleted-from), recording each skipped
202
+ * key as a non-fatal `SceneInstantiateDiagnostic` into the passed accumulator.
203
+ * All known fields still write through, so one bad field cannot blank the
204
+ * entity or the scene (C-AC-02/03/04).
205
+ */
206
+ export declare function worldBuildSceneEntityComponentDatas(world: World, node: import('@forgeax/engine-types').SceneEntity, mapping: Uint32Array, diagnostics: SceneInstantiateDiagnostic[]): Result<ComponentData[], EcsError>;
207
+ /**
208
+ * @internal feat-20260713 M2 / w8: apply one MountOverride to a live member
209
+ * entity column. The `field?` shape is the add-or-patch discriminant:
210
+ *
211
+ * - `field` present -> PATCH one field: `world.set(member, comp, {[field]:
212
+ * value})`. Omitted fields keep their authored / existing values.
213
+ * - `field` absent -> ADD/UPSERT the whole component: `value` is the
214
+ * per-field value map for `comp`. When the member already carries `comp`
215
+ * it is upserted (set-over each supplied field + schema defaults for the
216
+ * omitted ones — the whole component is rewritten from the value map +
217
+ * defaults, never a `component-already-present` error). When absent it is
218
+ * added fresh via `addComponent` (fillComponentDefaults fills omitted
219
+ * fields). The value-map is fed through `fillComponentDefaults` so the
220
+ * add and upsert paths write byte-identical rows.
221
+ *
222
+ * Component registration + value-key validation happened at
223
+ * `_validateMountOverrides` (fail-fast before any spawn); by this point the
224
+ * comp resolves through the World-local catalog and the value keys are schema-valid.
225
+ * still guards defensively (an unregistered comp is a no-op skip, matching
226
+ * the prior field-patch behaviour). Returns the underlying set / addComponent
227
+ * Result so a shared-field value gate (D-4) or any other write error
228
+ * propagates unchanged.
229
+ */
230
+ export declare function worldApplyMountOverride(world: World, member: EntityHandle, ov: MountOverride): Result<void, EcsError>;
231
+ /**
232
+ * @internal R2/B-3 + R2/B-4: validate `mount.overrides[]` BEFORE any
233
+ * spawn so a malformed override fails fast with no observable side
234
+ * effects (charter P3 explicit-failure). Two checks:
235
+ *
236
+ * 1. `override.localId` must address a slot inside the parent-namespace
237
+ * member window `[memberFirst, memberFirst + memberCount)` (AC-06).
238
+ * 2. `override.field` must exist in the resolved component schema
239
+ * (AC-07). When the component is unregistered we cannot validate the
240
+ * field shape; let the existing fall-through path proceed (the
241
+ * catalog guard inside the override-application loop
242
+ * will skip the write).
243
+ */
244
+ export declare function worldValidateMountOverrides(world: World, mount: SceneInstanceMount): Result<void, EcsError>;
245
+ /** @internal Spawn the mount-entity slot carrying mount.components (if any).
246
+ *
247
+ * R2/B-1: the mount entity is a structural intermediate in the ChildOf
248
+ * chain `cube -> innerSyntheticRoot -> mountEntity -> outerSyntheticRoot`,
249
+ * so it MUST carry Transform whenever Transform is registered (mirrors
250
+ * the D-V-0 synthetic-root invariant). Otherwise propagateTransforms
251
+ * walking the chain hits a Transform-less parent and emits per-frame
252
+ * `RhiError(hierarchy-broken)` (verify R1 root cause of the
253
+ * hello-scene-nesting demo black frames).
254
+ */
255
+ export declare function worldSpawnMountEntity(world: World, mount: SceneInstanceMount, mapping: Uint32Array, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
256
+ /** @internal Resolve mount.source through the wired SceneAssetResolver. */
257
+ export declare function worldResolveMountSource(world: World, source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>): Result<Handle<'SceneAsset', 'shared'>, EcsError>;
258
+ /** @internal Convert mount.overrides Map shape to the SceneInstanceState shape.
259
+ *
260
+ * feat-20260713 M1 / w4: `field` is optional (add-or-patch discriminant). In
261
+ * M1 only the field-patch form reaches this builder (the component-add form
262
+ * fails fast in the apply loops); the record type stays `field?: string` so
263
+ * the M2 add path can flow through untouched. `exactOptionalPropertyTypes`
264
+ * forbids writing an explicit `field: undefined`, so omit the key when absent.
265
+ */
266
+ export declare function worldMountOverridesToStateMap(src: Map<LocalEntityId, Map<string, MountOverride>>): Map<LocalEntityId, Map<string, {
267
+ comp: string;
268
+ field?: string;
269
+ value: unknown;
270
+ }>>;
271
+ /** @internal Set the payload of an already-allocated SceneInstance state ref. */
272
+ export declare function worldSetUniqueRefPayload<T>(world: World, handle: Handle<string, 'unique'>, payload: T): void;
273
+ /**
274
+ * @internal Resolve the SceneInstanceState payload behind the
275
+ * `SceneInstance.state` ref column on `root`. Returns Err when `root`
276
+ * does not carry SceneInstance or the ref slot is dead.
277
+ */
278
+ export declare function worldResolveSceneInstanceStatePayload(world: World, root: EntityHandle): Result<SceneInstanceStatePayload, EcsError>;
279
+ /**
280
+ * Public sugar — get the SceneInstanceState payload (Map / Set view) for
281
+ * `root`. Equivalent to `world.get(root, SceneInstance)` followed by a
282
+ * managed-ref resolution; provided so AI users do not have to learn the
283
+ * `ref<T>` slot resolution mechanic for the common read path.
284
+ */
285
+ export declare function worldGetSceneInstanceState(world: World, root: EntityHandle): Result<SceneInstanceStatePayload, EcsError>;
286
+ /**
287
+ * Despawn a SceneInstance root + all its members. `opts.keepDetached`
288
+ * preserves members marked via `worldDetachSceneMember` (plan-strategy
289
+ * §D-5). Returns the count of entities actually despawned (root + each
290
+ * non-detached member).
291
+ *
292
+ * For a plain entity (no SceneInstance), behaviour matches
293
+ * `world.despawn(entity)` followed by `despawnDescendants(entity)` — i.e.
294
+ * `keepDetached` is a no-op.
295
+ */
296
+ export declare function worldDespawnScene(world: World, root: EntityHandle, opts?: {
297
+ keepDetached?: boolean;
298
+ }): Result<number, EcsError>;
299
+ /**
300
+ * Despawn every descendant of `root` reachable through Children mirror /
301
+ * SceneInstance.mapping. `opts.keepDetached` is honoured only when `root`
302
+ * carries a SceneInstance (otherwise the option is ignored — there is no
303
+ * detached set on a plain entity).
304
+ *
305
+ * Returns the count of entities despawned. The `root` itself is NOT
306
+ * despawned (that is `despawnScene`'s extra step).
307
+ */
308
+ export declare function worldDespawnDescendants(world: World, root: EntityHandle, opts?: {
309
+ keepDetached?: boolean;
310
+ }): Result<number, EcsError>;
311
+ /**
312
+ * Write a runtime override to a member entity belonging to `root`. Routes
313
+ * through `world.set(member, comp, { [field]: value })` after an entity-
314
+ * scope guard so cross-instance writes fail-fast. Type-mismatch surfaces
315
+ * `EcsErrorCode = 'scene-override-type-mismatch'` (D-9).
316
+ */
317
+ export declare function worldSetSceneOverride<S extends ComponentSchema>(world: World, root: EntityHandle, member: EntityHandle, component: Component<string, S>, field: keyof ShapeOf<S> & string, value: unknown): Result<void, EcsError>;
318
+ /**
319
+ * Drop a runtime override (and any mount-time override for the same
320
+ * (member, comp, field) triple); roll the live column value back to the
321
+ * source SceneAsset's layer-1 explicit value (M2 v1 — M3+ widens to layer
322
+ * 2/3 defaults via fillComponentDefaults).
323
+ */
324
+ export declare function worldRemoveSceneOverride<S extends ComponentSchema>(world: World, root: EntityHandle, member: EntityHandle, component: Component<string, S>, field: keyof ShapeOf<S> & string): Result<void, EcsError>;
325
+ /** Mark a member entity detached. Idempotent (set semantics). */
326
+ export declare function worldDetachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result<void, EcsError>;
327
+ /** Clear a detached mark. Idempotent (set semantics). */
328
+ export declare function worldReattachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result<void, EcsError>;
329
+ /**
330
+ * Get the SceneAsset handle a SceneInstance root was instantiated from.
331
+ * Returns Err on a plain entity (no SceneInstance component).
332
+ */
333
+ export declare function worldGetSceneAssetForInstance(world: World, root: EntityHandle): Result<Handle<'SceneAsset', 'shared'>, EcsError>;
334
+ /** @internal Structural payload behind `SceneInstance.state` ref column. */
335
+ export interface SceneInstanceStatePayload {
336
+ readonly source: Handle<'SceneAsset', 'shared'>;
337
+ readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
338
+ readonly detachedLocalIds: Set<LocalEntityId>;
339
+ readonly overrides: Map<LocalEntityId, Map<string, {
340
+ readonly comp: string;
341
+ readonly field?: string;
342
+ readonly value: unknown;
343
+ }>>;
344
+ readonly rootEntities: EntityHandle[];
345
+ /** Synthetic roots of recursively mounted SceneAssets owned by this instance. */
346
+ readonly mountRoots: EntityHandle[];
347
+ readonly totalSlots: number;
348
+ readonly mountTimeOverrides: readonly MountOverride[];
349
+ }
350
+ //# sourceMappingURL=scene-instances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-instances.d.ts","sourceRoot":"","sources":["../../src/instances/scene-instances.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '@forgeax/engine-plugin';
2
+ export declare function scenePlugin(): Plugin;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAiBrD,wBAAgB,WAAW,IAAI,MAAM,CASpC"}
@@ -0,0 +1,16 @@
1
+ import { type EntityHandle, type World } from '@forgeax/engine-ecs';
2
+ import type { SceneErrorCode, SceneErrorDetail } from '../errors';
3
+ export interface SceneHierarchyDiagnostic {
4
+ readonly code: SceneErrorCode;
5
+ readonly expected: string;
6
+ readonly hint: string;
7
+ readonly detail: SceneErrorDetail;
8
+ }
9
+ export interface SceneHierarchySnapshot {
10
+ readonly parentOf: ReadonlyMap<EntityHandle, EntityHandle>;
11
+ readonly diagnostics: readonly SceneHierarchyDiagnostic[];
12
+ getParent(entity: EntityHandle): EntityHandle | undefined;
13
+ }
14
+ /** Build the only World-local projection of ChildOf parent facts. */
15
+ export declare function projectHierarchy(world: World): SceneHierarchySnapshot;
16
+ //# sourceMappingURL=hierarchy-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchy-projection.d.ts","sourceRoot":"","sources":["../../src/systems/hierarchy-projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,YAAY,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC3D,QAAQ,CAAC,WAAW,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC1D,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;CAC3D;AAqCD,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,sBAAsB,CA+ErE"}
@@ -0,0 +1,3 @@
1
+ export { projectHierarchy, type SceneHierarchyDiagnostic, type SceneHierarchySnapshot, } from './hierarchy-projection';
2
+ export { PROPAGATE_TRANSFORMS_SYSTEM, propagateTransforms, registerPropagateTransforms, TransformSet, } from './propagate-transforms';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/systems/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,2BAA2B,EAC3B,YAAY,GACb,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type SystemHandle, type World } from '@forgeax/engine-ecs';
2
+ import { type Result } from '@forgeax/engine-types';
3
+ import { SceneError } from '../errors';
4
+ import { type SceneHierarchySnapshot } from './hierarchy-projection';
5
+ export declare const PROPAGATE_TRANSFORMS_SYSTEM: "propagateTransforms";
6
+ export declare const PROPAGATE_TRANSFORMS_FIXED_SYSTEM: "propagateTransformsFixed";
7
+ export declare const TransformSet: import("@forgeax/engine-ecs").SystemSet;
8
+ export declare const TransformFixedSet: import("@forgeax/engine-ecs").SystemSet;
9
+ export declare function propagateTransforms(world: World, _hierarchy?: SceneHierarchySnapshot): Result<void, SceneError>;
10
+ export declare const PropagateTransforms: SystemHandle<readonly []>;
11
+ export declare const PropagateTransformsFixed: SystemHandle<readonly []>;
12
+ export declare function registerPropagateTransforms(world: World, options?: {
13
+ beforeSystemName?: string;
14
+ }): () => void;
15
+ //# sourceMappingURL=propagate-transforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propagate-transforms.d.ts","sourceRoot":"","sources":["../../src/systems/propagate-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,KAAK,EACX,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,eAAO,MAAM,2BAA2B,EAAG,qBAA8B,CAAC;AAC1E,eAAO,MAAM,iCAAiC,EAAG,0BAAmC,CAAC;AACrF,eAAO,MAAM,YAAY,yCAAyC,CAAC;AACnE,eAAO,MAAM,iBAAiB,yCAA+C,CAAC;AAgM9E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,UAAU,CAAC,EAAE,sBAAsB,GAClC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,CA2F1B;AAED,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,SAAS,EAAE,CAOxD,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,YAAY,CAAC,SAAS,EAAE,CAI7D,CAAC;AAEH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,MAAM,IAAI,CA6CZ"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@forgeax/engine-scene",
3
+ "version": "0.0.0-dev.8d955ade1c79",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "sideEffects": false,
8
+ "description": "Scene identity, hierarchy, and world transform propagation.",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "main": "./dist/index.mjs",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "dist",
20
+ "src",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "dependencies": {
25
+ "@forgeax/engine-ecs": "0.0.0-dev.8d955ade1c79",
26
+ "@forgeax/engine-math": "0.0.0-dev.8d955ade1c79",
27
+ "@forgeax/engine-plugin": "0.0.0-dev.8d955ade1c79"
28
+ },
29
+ "forgeax": {
30
+ "metrics": {
31
+ "bundle-size": {
32
+ "enabled": true,
33
+ "path": "dist/index.mjs",
34
+ "compression": "gzip"
35
+ },
36
+ "fps": {
37
+ "enabled": false,
38
+ "reason": "headless scene package"
39
+ },
40
+ "bench": {
41
+ "enabled": false,
42
+ "reason": "scene propagation covered by unit tests"
43
+ },
44
+ "gate": {
45
+ "enabled": false,
46
+ "reason": "no package binary gate"
47
+ },
48
+ "spike-report": {
49
+ "enabled": false,
50
+ "reason": "not a spike package"
51
+ }
52
+ }
53
+ },
54
+ "scripts": {
55
+ "build": "tsup",
56
+ "test": "vitest run"
57
+ }
58
+ }
@@ -0,0 +1,57 @@
1
+ import { defineComponent, World } from '@forgeax/engine-ecs';
2
+ import type { SceneAsset } from '@forgeax/engine-types';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { ChildOf, Children, worldDespawnScene, worldInstantiateScenePayload } from '../index';
5
+
6
+ const SceneInstance = defineComponent('SceneInstance', {
7
+ source: { type: 'shared<SceneAsset>' },
8
+ mapping: { type: 'array<entity>' },
9
+ state: { type: 'unique<SceneInstanceState>' },
10
+ });
11
+ const SceneMarker = defineComponent('ScenePayloadMarker', { value: 'i32' });
12
+
13
+ function registerSceneWorld(world: World): void {
14
+ for (const component of [SceneInstance, ChildOf, Children, SceneMarker]) {
15
+ world.components.register(component).unwrap();
16
+ }
17
+ }
18
+
19
+ function scene(): SceneAsset {
20
+ return {
21
+ kind: 'scene',
22
+ entities: [
23
+ {
24
+ localId: 0 as never,
25
+ components: { ScenePayloadMarker: { value: 7 } },
26
+ },
27
+ ],
28
+ };
29
+ }
30
+
31
+ describe('scene payload ownership', () => {
32
+ it('retains the payload for the mounted instance and releases it on despawn', () => {
33
+ const world = new World();
34
+ registerSceneWorld(world);
35
+
36
+ const result = worldInstantiateScenePayload(world, scene());
37
+
38
+ expect(result.ok).toBe(true);
39
+ expect(world.sharedRefs._liveCount()).toBe(1);
40
+ if (!result.ok) return;
41
+ expect(worldDespawnScene(world, result.value.root).ok).toBe(true);
42
+ expect(world.sharedRefs._liveCount()).toBe(0);
43
+ });
44
+
45
+ it('releases the temporary producer grant when materialisation fails', () => {
46
+ const world = new World();
47
+ registerSceneWorld(world);
48
+
49
+ const result = worldInstantiateScenePayload(world, {
50
+ kind: 'scene',
51
+ entities: [{ localId: 0 as never, components: { MissingComponent: { value: 1 } } }],
52
+ } as SceneAsset);
53
+
54
+ expect(result.ok).toBe(false);
55
+ expect(world.sharedRefs._liveCount()).toBe(0);
56
+ });
57
+ });
@@ -0,0 +1,26 @@
1
+ import { World } from '@forgeax/engine-ecs';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { ChildOf, Children, Name, Transform } from '../index';
4
+
5
+ describe('scene component roster', () => {
6
+ it('exports exactly the four scene components', () => {
7
+ expect(Object.keys({ Name, Transform, ChildOf, Children }).sort()).toEqual([
8
+ 'ChildOf',
9
+ 'Children',
10
+ 'Name',
11
+ 'Transform',
12
+ ]);
13
+ });
14
+
15
+ it('registers and spawns in a headless world', () => {
16
+ const world = new World();
17
+ const entity = world
18
+ .spawn(
19
+ { component: Name, data: { value: 'root' } },
20
+ { component: Transform, data: {} },
21
+ { component: Children, data: {} },
22
+ )
23
+ .unwrap();
24
+ expect(world.get(entity, Name).unwrap().value).toBe('root');
25
+ });
26
+ });
@@ -0,0 +1,11 @@
1
+ import type * as SceneSurface from '../index';
2
+
3
+ type DiagnosticSurface = Pick<SceneSurface.SceneHierarchyDiagnostic, 'code' | 'detail'>;
4
+ const diagnosticSurface: DiagnosticSurface | undefined = undefined;
5
+ void diagnosticSurface;
6
+
7
+ // These names were duplicate projections of the SceneError code/detail owners.
8
+ // @ts-expect-error hierarchy diagnostics use SceneErrorCode directly.
9
+ export type RemovedDiagnosticCode = SceneSurface['SceneHierarchyDiagnosticCode'];
10
+ // @ts-expect-error hierarchy diagnostics use SceneErrorDetail directly.
11
+ export type RemovedDiagnosticDetail = SceneSurface['SceneHierarchyDiagnosticDetail'];
@@ -0,0 +1,13 @@
1
+ import { componentSchema } from '@forgeax/engine-ecs/internal';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ describe('scene ChildOf module', () => {
5
+ it('registers its Children mirror when imported directly', async () => {
6
+ vi.resetModules();
7
+ const { ChildOf } = await import('../components/child-of');
8
+ const { Children } = await import('../components/children');
9
+
10
+ expect(ChildOf.name).toBe('ChildOf');
11
+ expect(componentSchema(Children).entities).toBe('array<entity>');
12
+ });
13
+ });
@@ -0,0 +1,15 @@
1
+ import type { EntityHandle, World } from '@forgeax/engine-ecs';
2
+ import { ChildOf } from '../../index';
3
+
4
+ /**
5
+ * Scene-owned malformed-edge fixture. The public relationship write accepts
6
+ * stale handles as data; the projection then owns the liveness diagnostic.
7
+ * Keeping this fixture on `world.set` avoids a private ECS graph seam.
8
+ */
9
+ export function setMalformedParentEdge(
10
+ world: World,
11
+ child: EntityHandle,
12
+ parent: EntityHandle,
13
+ ): void {
14
+ world.set(child, ChildOf, { parent }).unwrap();
15
+ }