@forgeax/engine-scene 0.1.21 → 0.1.24

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 (87) hide show
  1. package/README.md +82 -11
  2. package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts +5 -3
  3. package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts.map +1 -1
  4. package/dist/__tests__/flat-propagation.contract.test.d.ts +2 -0
  5. package/dist/__tests__/flat-propagation.contract.test.d.ts.map +1 -0
  6. package/dist/__tests__/flat-propagation.derived-writer.contract.test.d.ts +2 -0
  7. package/dist/__tests__/flat-propagation.derived-writer.contract.test.d.ts.map +1 -0
  8. package/dist/__tests__/flat-propagation.perf.test.d.ts +2 -0
  9. package/dist/__tests__/flat-propagation.perf.test.d.ts.map +1 -0
  10. package/dist/__tests__/hierarchy-propagation.perf.test.d.ts +2 -0
  11. package/dist/__tests__/hierarchy-propagation.perf.test.d.ts.map +1 -0
  12. package/dist/__tests__/scene-binding.integration.test.d.ts +2 -0
  13. package/dist/__tests__/scene-binding.integration.test.d.ts.map +1 -0
  14. package/dist/__tests__/schedule-order.contract.test.d.ts +2 -0
  15. package/dist/__tests__/schedule-order.contract.test.d.ts.map +1 -0
  16. package/dist/__tests__/structural.test.d.ts +2 -0
  17. package/dist/__tests__/structural.test.d.ts.map +1 -0
  18. package/dist/__tests__/transform-carrier-consumers.test-d.d.ts +2 -0
  19. package/dist/__tests__/transform-carrier-consumers.test-d.d.ts.map +1 -0
  20. package/dist/__tests__/transform-carrier-prototype.bench.d.ts +39 -0
  21. package/dist/__tests__/transform-carrier-prototype.bench.d.ts.map +1 -0
  22. package/dist/__tests__/transform-carrier-prototype.test-d.d.ts +2 -0
  23. package/dist/__tests__/transform-carrier-prototype.test-d.d.ts.map +1 -0
  24. package/dist/__tests__/transform-carrier-prototype.test.d.ts +2 -0
  25. package/dist/__tests__/transform-carrier-prototype.test.d.ts.map +1 -0
  26. package/dist/__tests__/transform-component-version.unit.test.d.ts +2 -0
  27. package/dist/__tests__/transform-component-version.unit.test.d.ts.map +1 -0
  28. package/dist/assets/scene-decoder.d.ts.map +1 -1
  29. package/dist/components/child-of.d.ts +23 -14
  30. package/dist/components/child-of.d.ts.map +1 -1
  31. package/dist/components/children.d.ts +14 -13
  32. package/dist/components/children.d.ts.map +1 -1
  33. package/dist/components/transform.d.ts +7 -43
  34. package/dist/components/transform.d.ts.map +1 -1
  35. package/dist/errors.d.ts +23 -2
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/index.d.ts +3 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.mjs +1122 -358
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/instances/binding.d.ts +29 -0
  42. package/dist/instances/binding.d.ts.map +1 -0
  43. package/dist/instances/externalization.d.ts.map +1 -1
  44. package/dist/instances/scene-instances.d.ts +6 -18
  45. package/dist/instances/scene-instances.d.ts.map +1 -1
  46. package/dist/instances/state.d.ts +28 -0
  47. package/dist/instances/state.d.ts.map +1 -0
  48. package/dist/plugin.d.ts.map +1 -1
  49. package/dist/systems/hierarchy-projection.d.ts.map +1 -1
  50. package/dist/systems/index.d.ts +1 -1
  51. package/dist/systems/index.d.ts.map +1 -1
  52. package/dist/systems/propagate-transforms.d.ts +22 -2
  53. package/dist/systems/propagate-transforms.d.ts.map +1 -1
  54. package/package.json +5 -5
  55. package/src/__tests__/components.unit.test.ts +34 -2
  56. package/src/__tests__/fixtures/malformed-hierarchy-edge.ts +26 -4
  57. package/src/__tests__/flat-propagation.contract.test.ts +128 -0
  58. package/src/__tests__/flat-propagation.derived-writer.contract.test.ts +39 -0
  59. package/src/__tests__/flat-propagation.perf.test.ts +336 -0
  60. package/src/__tests__/hierarchy-propagation.perf.test.ts +149 -0
  61. package/src/__tests__/plugin.integration.test.ts +2 -2
  62. package/src/__tests__/propagation.unit.test.ts +271 -91
  63. package/src/__tests__/scene-binding.integration.test.ts +124 -0
  64. package/src/__tests__/schedule-order.contract.test.ts +43 -0
  65. package/src/__tests__/structural.test.ts +23 -0
  66. package/src/__tests__/transform-carrier-consumers.test-d.ts +156 -0
  67. package/src/__tests__/transform-carrier-prototype.bench.ts +168 -0
  68. package/src/__tests__/transform-carrier-prototype.test-d.ts +71 -0
  69. package/src/__tests__/transform-carrier-prototype.test.ts +170 -0
  70. package/src/__tests__/{transform-change-journal.unit.test.ts → transform-component-version.unit.test.ts} +32 -39
  71. package/src/assets/scene-decoder.ts +6 -1
  72. package/src/components/child-of.ts +38 -27
  73. package/src/components/children.ts +36 -37
  74. package/src/components/transform.ts +23 -81
  75. package/src/errors.ts +26 -2
  76. package/src/index.ts +10 -1
  77. package/src/instances/binding.ts +77 -0
  78. package/src/instances/externalization.ts +7 -1
  79. package/src/instances/scene-instances.ts +51 -80
  80. package/src/instances/state.ts +62 -0
  81. package/src/plugin.ts +9 -2
  82. package/src/systems/hierarchy-projection.ts +25 -8
  83. package/src/systems/index.ts +3 -0
  84. package/src/systems/propagate-transforms.ts +1142 -240
  85. package/dist/.tsbuildinfo +0 -1
  86. package/dist/__tests__/transform-change-journal.unit.test.d.ts +0 -2
  87. package/dist/__tests__/transform-change-journal.unit.test.d.ts.map +0 -1
@@ -21,6 +21,7 @@ import type {
21
21
  PackErrorCode,
22
22
  PackErrorDetail,
23
23
  SceneAsset,
24
+ SceneEntityRef,
24
25
  SceneInstanceMount,
25
26
  } from '@forgeax/engine-types';
26
27
  import {
@@ -32,6 +33,16 @@ import {
32
33
  unwrapHandle,
33
34
  } from '@forgeax/engine-types';
34
35
  import { ComponentNotDefinedError } from '../errors';
36
+ import { resolveSceneEntity, validateSceneBindings } from './binding.js';
37
+ import {
38
+ isPrimitiveScalarFieldType,
39
+ mountOverrideStateKey,
40
+ primitiveJsType,
41
+ type SceneInstanceStatePayload,
42
+ sceneWorldState,
43
+ } from './state.js';
44
+
45
+ export type { SceneInstanceStatePayload } from './state.js';
35
46
 
36
47
  const entityIndex = (entity: EntityHandle): number => (entity as number) & 0x00ffffff;
37
48
  const entityGeneration = (entity: EntityHandle): number => ((entity as number) >>> 24) & 0xff;
@@ -136,21 +147,6 @@ export type SceneAssetResolver = (
136
147
  parentHandle: Handle<'SceneAsset', 'shared'>,
137
148
  ) => Result<Handle<'SceneAsset', 'shared'>, unknown>;
138
149
 
139
- interface SceneWorldState {
140
- resolver: SceneAssetResolver | null;
141
- readonly statePayloads: Map<number, unknown>;
142
- }
143
-
144
- const sceneWorldStates = new WeakMap<World, SceneWorldState>();
145
-
146
- function sceneWorldState(world: World): SceneWorldState {
147
- const current = sceneWorldStates.get(world);
148
- if (current !== undefined) return current;
149
- const created = { resolver: null, statePayloads: new Map<number, unknown>() };
150
- sceneWorldStates.set(world, created);
151
- return created;
152
- }
153
-
154
150
  /** @internal */
155
151
  export function worldSetSceneAssetResolver(world: World, resolver: SceneAssetResolver): void {
156
152
  sceneWorldState(world).resolver = resolver;
@@ -158,7 +154,7 @@ export function worldSetSceneAssetResolver(world: World, resolver: SceneAssetRes
158
154
 
159
155
  /** @internal */
160
156
  export function worldGetSceneAssetResolver(world: World): SceneAssetResolver | null {
161
- return sceneWorldState(world).resolver;
157
+ return sceneWorldState(world).resolver as SceneAssetResolver | null;
162
158
  }
163
159
 
164
160
  /**
@@ -339,6 +335,15 @@ export function worldSpawnSceneMembers(
339
335
 
340
336
  const ownEntities = asset.entities;
341
337
  const ownMounts = asset.mounts ?? [];
338
+ const bindingKeys = ownEntities.flatMap((entity) =>
339
+ entity.bindingKey === undefined ? [] : [entity.bindingKey],
340
+ );
341
+ if (bindingKeys.length > 0) {
342
+ const bindingCheck = validateSceneBindings(asset.sourceKey ?? '', bindingKeys);
343
+ if (!bindingCheck.ok) {
344
+ return err(bindingCheck.error as unknown as EcsError);
345
+ }
346
+ }
342
347
  const memberSum = ownMounts.reduce((s, m) => s + m.memberCount, 0);
343
348
  const countBaseline = ownEntities.length + ownMounts.length + memberSum;
344
349
  // C-R1 (studio-issues #6): mapping table must be sized to maxLocalId+1,
@@ -641,8 +646,8 @@ export function worldInstantiateSceneAsset(
641
646
  const mappingPlain: number[] = Array.from(mapping);
642
647
  // The synthetic root is the ChildOf parent of every owned root entity
643
648
  // (step 5 below) and may itself become a ChildOf parent of a caller-
644
- // supplied `parent` chain. propagateTransforms walks ChildOf parents
645
- // through the Transform liveMap and treats a parent missing Transform
649
+ // supplied `parent` chain. propagateTransforms expands the ECS-maintained
650
+ // Children lists parent-first and treats a parent missing Transform
646
651
  // as `hierarchy-broken`, so the synthetic root must carry Transform
647
652
  // (identity TRS via layer-2 defaults) when Transform is defined.
648
653
  const rootComponents: ComponentData[] = [
@@ -701,8 +706,18 @@ export function worldInstantiateSceneAsset(
701
706
  }
702
707
 
703
708
  const detached = new Set<LocalEntityId>();
709
+ const bindings = new Map<string, EntityHandle>();
710
+ for (const entity of asset.entities) {
711
+ if (entity.bindingKey === undefined) continue;
712
+ const live = mapping[entity.localId as unknown as number];
713
+ if (live !== undefined && live !== ENTITY_NULL_RAW) {
714
+ bindings.set(entity.bindingKey, live as unknown as EntityHandle);
715
+ }
716
+ }
704
717
  const state: Record<string, unknown> = {
705
718
  source: handle,
719
+ sceneSourceKey: asset.sourceKey,
720
+ bindings,
706
721
  entityToLocalId,
707
722
  detachedLocalIds: detached,
708
723
  // Convert overrides Map<LocalEntityId, Map<string, MountOverride>>
@@ -1033,7 +1048,7 @@ export function worldValidateMountOverrides(
1033
1048
  * chain `cube -> innerSyntheticRoot -> mountEntity -> outerSyntheticRoot`,
1034
1049
  * so it MUST carry Transform whenever Transform is registered (mirrors
1035
1050
  * the D-V-0 synthetic-root invariant). Otherwise propagateTransforms
1036
- * walking the chain hits a Transform-less parent and emits per-frame
1051
+ * expanding the chain hits a Transform-less parent and emits per-frame
1037
1052
  * `RhiError(hierarchy-broken)` (verify R1 root cause of the
1038
1053
  * hello-scene-nesting demo black frames).
1039
1054
  */
@@ -1049,7 +1064,7 @@ export function worldSpawnMountEntity(
1049
1064
  };
1050
1065
  const cdRes = worldBuildSceneEntityComponentDatas(world, fakeNode, mapping, diagnostics);
1051
1066
  if (!cdRes.ok) return cdRes;
1052
- // R2/B-1: ensure Transform is attached so propagateTransforms can walk
1067
+ // R2/B-1: ensure Transform is attached so propagateTransforms can expand
1053
1068
  // through this entity. Layer-2 defaults supply identity TRS; the
1054
1069
  // mount.components overlay (when present and including Transform) takes
1055
1070
  // precedence and is already in cdRes.value.
@@ -1179,6 +1194,22 @@ export function worldGetSceneInstanceState(
1179
1194
  ): Result<SceneInstanceStatePayload, EcsError> {
1180
1195
  return worldResolveSceneInstanceStatePayload(world, root);
1181
1196
  }
1197
+
1198
+ /** Resolve a generated SceneEntityRef against one concrete SceneInstance. */
1199
+ export function worldResolveSceneEntity(
1200
+ world: World,
1201
+ root: EntityHandle,
1202
+ ref: SceneEntityRef,
1203
+ ): Result<EntityHandle, EcsError> {
1204
+ const state = worldResolveSceneInstanceStatePayload(world, root);
1205
+ if (!state.ok) return state;
1206
+ const resolved = resolveSceneEntity(ref, {
1207
+ sceneSourceKey: state.value.sceneSourceKey ?? ref.sceneSourceKey,
1208
+ bindings: state.value.bindings,
1209
+ });
1210
+ if (!resolved.ok) return err(resolved.error as unknown as EcsError);
1211
+ return ok(resolved.value as EntityHandle);
1212
+ }
1182
1213
  /**
1183
1214
  * Despawn a SceneInstance root + all its members. `opts.keepDetached`
1184
1215
  * preserves members marked via `worldDetachSceneMember` (plan-strategy
@@ -1452,27 +1483,6 @@ export function worldGetSceneAssetForInstance(
1452
1483
  return ok(stateRes.value.source);
1453
1484
  }
1454
1485
 
1455
- // SceneInstanceStatePayload — internal echo of the runtime
1456
- // `SceneInstanceState` interface for ECS-side consumption (engine-ecs cannot
1457
- // value-import engine-runtime by AC-29; structural shape only).
1458
- // ────────────────────────────────────────────────────────────────────────────
1459
-
1460
- /** @internal Structural payload behind `SceneInstance.state` ref column. */
1461
- export interface SceneInstanceStatePayload {
1462
- readonly source: Handle<'SceneAsset', 'shared'>;
1463
- readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
1464
- readonly detachedLocalIds: Set<LocalEntityId>;
1465
- readonly overrides: Map<
1466
- LocalEntityId,
1467
- Map<string, { readonly comp: string; readonly field?: string; readonly value: unknown }>
1468
- >;
1469
- readonly rootEntities: EntityHandle[];
1470
- /** Synthetic roots of recursively mounted SceneAssets owned by this instance. */
1471
- readonly mountRoots: EntityHandle[];
1472
- readonly totalSlots: number;
1473
- readonly mountTimeOverrides: readonly MountOverride[];
1474
- }
1475
-
1476
1486
  /**
1477
1487
  * Topological sort over the implicit ChildOf graph (parents before children).
1478
1488
  * Cycle-free input always covers all n nodes; cyclic input emits whatever was
@@ -1523,42 +1533,3 @@ function sceneTopoSort(
1523
1533
  }
1524
1534
  return order;
1525
1535
  }
1526
-
1527
- /**
1528
- * @internal feat-20260713 M2 / w8: SceneInstanceState map key for a
1529
- * MountOverride. Field-patch form keys by `comp:field` (one entry per patched
1530
- * field); component-add form keys by `comp` (one entry per added component). The
1531
- * two key shapes cannot collide because a field-patch always carries a `:field`
1532
- * suffix. Later array entries for the same key overwrite earlier ones, matching
1533
- * the array-order apply semantics.
1534
- */
1535
- function mountOverrideStateKey(ov: MountOverride): string {
1536
- return ov.field !== undefined ? `${ov.comp}:${ov.field}` : ov.comp;
1537
- }
1538
-
1539
- /** Schema field types that are JS primitives (typeof checkable). */
1540
- function isPrimitiveScalarFieldType(fieldType: string): boolean {
1541
- if (
1542
- fieldType === 'f32' ||
1543
- fieldType === 'f64' ||
1544
- fieldType === 'u32' ||
1545
- fieldType === 'i32' ||
1546
- fieldType === 'u8' ||
1547
- fieldType === 'i8' ||
1548
- fieldType === 'u16' ||
1549
- fieldType === 'i16' ||
1550
- fieldType === 'bool' ||
1551
- fieldType === 'string'
1552
- ) {
1553
- return true;
1554
- }
1555
- if (fieldType.startsWith('enum<')) return true;
1556
- return false;
1557
- }
1558
-
1559
- /** Map a primitive scalar field type to the runtime `typeof` it should narrow to. */
1560
- function primitiveJsType(fieldType: string): string {
1561
- if (fieldType === 'bool') return 'boolean';
1562
- if (fieldType === 'string') return 'string';
1563
- return 'number';
1564
- }
@@ -0,0 +1,62 @@
1
+ import type { EntityHandle, World } from '@forgeax/engine-ecs';
2
+ import type { Handle, LocalEntityId, MountOverride } from '@forgeax/engine-types';
3
+
4
+ /** Internal state retained by a SceneInstance root. */
5
+ export interface SceneInstanceStatePayload {
6
+ readonly source: Handle<'SceneAsset', 'shared'>;
7
+ readonly sceneSourceKey?: string;
8
+ readonly bindings: Map<string, EntityHandle>;
9
+ readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
10
+ readonly detachedLocalIds: Set<LocalEntityId>;
11
+ readonly overrides: Map<
12
+ LocalEntityId,
13
+ Map<string, { readonly comp: string; readonly field?: string; readonly value: unknown }>
14
+ >;
15
+ readonly rootEntities: EntityHandle[];
16
+ readonly mountRoots: EntityHandle[];
17
+ readonly totalSlots: number;
18
+ readonly mountTimeOverrides: readonly MountOverride[];
19
+ }
20
+
21
+ export interface SceneWorldState {
22
+ resolver: unknown;
23
+ readonly statePayloads: Map<number, unknown>;
24
+ }
25
+
26
+ const sceneWorldStates = new WeakMap<World, SceneWorldState>();
27
+
28
+ export function sceneWorldState(world: World): SceneWorldState {
29
+ const current = sceneWorldStates.get(world);
30
+ if (current !== undefined) return current;
31
+ const created: SceneWorldState = { resolver: null, statePayloads: new Map<number, unknown>() };
32
+ sceneWorldStates.set(world, created);
33
+ return created;
34
+ }
35
+
36
+ export function mountOverrideStateKey(ov: MountOverride): string {
37
+ return ov.field !== undefined ? `${ov.comp}:${ov.field}` : ov.comp;
38
+ }
39
+
40
+ export function isPrimitiveScalarFieldType(fieldType: string): boolean {
41
+ if (
42
+ fieldType === 'f32' ||
43
+ fieldType === 'f64' ||
44
+ fieldType === 'u32' ||
45
+ fieldType === 'i32' ||
46
+ fieldType === 'u8' ||
47
+ fieldType === 'i8' ||
48
+ fieldType === 'u16' ||
49
+ fieldType === 'i16' ||
50
+ fieldType === 'bool' ||
51
+ fieldType === 'string'
52
+ ) {
53
+ return true;
54
+ }
55
+ return fieldType.startsWith('enum<');
56
+ }
57
+
58
+ export function primitiveJsType(fieldType: string): string {
59
+ if (fieldType === 'bool') return 'boolean';
60
+ if (fieldType === 'string') return 'string';
61
+ return 'number';
62
+ }
package/src/plugin.ts CHANGED
@@ -4,10 +4,17 @@ import { ChildOf } from './components/child-of';
4
4
  import { Children } from './components/children';
5
5
  import { MorphWeights } from './components/morph-weights';
6
6
  import { Name } from './components/name';
7
- import { Transform } from './components/transform';
7
+ import { GlobalTransform, Transform } from './components/transform';
8
8
  import { registerPropagateTransforms } from './systems/propagate-transforms';
9
9
 
10
- const SCENE_COMPONENTS: readonly Component[] = [ChildOf, Children, MorphWeights, Name, Transform];
10
+ const SCENE_COMPONENTS: readonly Component[] = [
11
+ ChildOf,
12
+ Children,
13
+ MorphWeights,
14
+ Name,
15
+ Transform,
16
+ GlobalTransform,
17
+ ];
11
18
 
12
19
  function registerSceneComponents(world: World): () => void {
13
20
  const leases = SCENE_COMPONENTS.map((component) => world.components.register(component).unwrap());
@@ -1,5 +1,4 @@
1
- import { Entity, type EntityHandle, type World } from '@forgeax/engine-ecs';
2
- import { createWorldProjection } from '@forgeax/engine-ecs/projection';
1
+ import { Entity, type EntityHandle, type Query, type World } from '@forgeax/engine-ecs';
3
2
  import { ChildOf } from '../components/child-of';
4
3
  import type { SceneErrorCode, SceneErrorDetail } from '../errors';
5
4
 
@@ -17,7 +16,8 @@ export interface SceneHierarchySnapshot {
17
16
  }
18
17
 
19
18
  interface HierarchyProjectionCacheEntry {
20
- readonly changes: ReturnType<typeof createWorldProjection>;
19
+ readonly structureEpoch: number;
20
+ readonly childOfChanges: Query;
21
21
  readonly snapshot: SceneHierarchySnapshot;
22
22
  }
23
23
 
@@ -30,6 +30,18 @@ interface HierarchyProjectionCacheEntry {
30
30
  // writes are internal-only and must not mutate authored hierarchy state.
31
31
  const HIERARCHY_PROJECTION_CACHE = new WeakMap<World, HierarchyProjectionCacheEntry>();
32
32
 
33
+ function createChildOfChangeQuery(world: World): Query {
34
+ const result = world.query({ changed: [ChildOf] });
35
+ if (!result.ok) throw result.error;
36
+ return result.value;
37
+ }
38
+
39
+ function drainChanges(query: Query): boolean {
40
+ let changed = false;
41
+ for (const span of query.spans().unwrap()) changed ||= span.length > 0;
42
+ return changed;
43
+ }
44
+
33
45
  function diagnostic(
34
46
  code: SceneErrorCode,
35
47
  entity: EntityHandle,
@@ -54,10 +66,12 @@ function diagnostic(
54
66
  /** Build the only World-local projection of ChildOf parent facts. */
55
67
  export function projectHierarchy(world: World): SceneHierarchySnapshot {
56
68
  const cached = HIERARCHY_PROJECTION_CACHE.get(world);
57
- const changes = cached?.changes ?? createWorldProjection(world, { components: [ChildOf] });
58
- if (cached !== undefined) {
59
- const evidence = changes.poll();
60
- if (evidence.status === 'delta' && evidence.changes.length === 0) return cached.snapshot;
69
+ if (
70
+ cached !== undefined &&
71
+ cached.structureEpoch === world.getStructureEpoch() &&
72
+ !drainChanges(cached.childOfChanges)
73
+ ) {
74
+ return cached.snapshot;
61
75
  }
62
76
  const liveEntities = new Set<EntityHandle>();
63
77
  const authoredParents = new Map<EntityHandle, EntityHandle>();
@@ -126,8 +140,11 @@ export function projectHierarchy(world: World): SceneHierarchySnapshot {
126
140
  return stableParentOf.get(entity);
127
141
  },
128
142
  };
143
+ const childOfChanges = createChildOfChangeQuery(world);
144
+ drainChanges(childOfChanges);
129
145
  HIERARCHY_PROJECTION_CACHE.set(world, {
130
- changes,
146
+ structureEpoch: world.getStructureEpoch(),
147
+ childOfChanges,
131
148
  snapshot,
132
149
  });
133
150
  return snapshot;
@@ -4,8 +4,11 @@ export {
4
4
  type SceneHierarchySnapshot,
5
5
  } from './hierarchy-projection';
6
6
  export {
7
+ PROPAGATE_TRANSFORMS_FIXED_SYSTEM,
7
8
  PROPAGATE_TRANSFORMS_SYSTEM,
9
+ PropagateTransformsFixed,
8
10
  propagateTransforms,
9
11
  registerPropagateTransforms,
12
+ TransformFixedSet,
10
13
  TransformSet,
11
14
  } from './propagate-transforms';