@daneren2005/shared-memory-ecs 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  A small, reusable Entity / Component / System core backed by shared memory
4
4
  ([`@daneren2005/shared-memory-objects`](https://www.npmjs.com/package/@daneren2005/shared-memory-objects)).
5
- It contains only the engine-agnostic parts of the ECS - no game specific concepts (factions, terrain,
6
- fog of war, sub-classed entities, required components, etc).
5
+ It contains only engine-agnostic ECS concepts. Games supply their own components, entity classes, templates,
6
+ systems, and domain data.
7
7
 
8
8
  ## Concepts
9
9
 
@@ -23,14 +23,16 @@ fog of war, sub-classed entities, required components, etc).
23
23
  - **`ComponentRegistry<C>`** – the map of all component definitions for a game.
24
24
  - **`EntityFactory<C>`** – maps an entity `type` name to a base (template) config. Loading an entity layers
25
25
  the caller's config over its type's template, so shared static data lives in one place and a save only
26
- needs the `type` plus the entity's serialization. `BaseWorld#loadEntity` always goes through the factory.
26
+ needs the `type` plus the entity's serialization. Optionally, a template's immutable `class` selects a
27
+ game-provided `BaseEntity` subclass and restricts which components that template may load.
27
28
  - **`BaseWorld<C>`** – builds one `MemoryComponent` per registered component (attached to that component's
28
29
  definition as `world.registry[name].memoryComponent`) and runs systems. It is generic over your component
29
30
  map `C`, so `world.registry`, `entity.components`, `setComponent`, etc. are fully typed. Its entities live
30
31
  in `world.entities`, a `Map` keyed by `eid` — see [Iterating entities](#iterating-entities).
31
- - **`BaseEntity<C>`** – an `eid`, an optional `id`, and a bag of memory-backed components. It has no
32
- direct property accessors and only loads/saves component data. Every entity has an `entity` component
33
- whose `type` (a plain, worker-invisible string) records the factory template it was built from.
32
+ - **`BaseEntity<C>`** – an `eid`, its `id` alias, immutable `type`, `dead` state, and a bag of memory-backed
33
+ components. Every entity has an `entity` component whose immutable `type` and optional immutable `class`
34
+ are interned strings stored in shared memory. A game can subclass `BaseEntity` to expose direct convenience
35
+ getters for one entity class.
34
36
  - **Systems** – `System`, `IterableSystem`, `EntitySystem` (main-thread iteration over entities with a
35
37
  given set of components), `EntityWorkerSystem` (runs an update function over raw memory blocks,
36
38
  off-thread when Web Workers + `SharedArrayBuffer` are available), and `WorkerSystem` (an `EntityWorkerSystem`
@@ -132,6 +134,64 @@ and the `entity` block stores only a pointer to it at `TYPE_INDEX`. Reading `ent
132
134
  resolves that pointer back to the string through the world's cache; a worker can do the same (see
133
135
  [Reading an entity's type in a worker](#reading-an-entitys-type-in-a-worker)).
134
136
 
137
+ ### Entity classes and scoped templates
138
+
139
+ An entity `type` names one concrete template. Its optional `class` names the stable family that chooses a
140
+ wrapper constructor and the components that family permits. Callers still pass only `{ type, ...overrides }`:
141
+ the factory resolves class from the template, and neither identity can change after construction.
142
+
143
+ ```ts
144
+ import {
145
+ BaseEntity,
146
+ BaseWorld,
147
+ EntityFactory,
148
+ defineEntityClasses,
149
+ defineEntityConfigs,
150
+ } from '@daneren2005/shared-memory-ecs';
151
+ import type { ComponentsOf, EntityConfigOf, EntityInstancesOf } from '@daneren2005/shared-memory-ecs';
152
+
153
+ const registry = {
154
+ health: healthDefinition,
155
+ value: valueDefinition,
156
+ };
157
+ type Components = ComponentsOf<typeof registry>;
158
+ type Config = EntityConfigOf<typeof registry>;
159
+
160
+ class ActorEntity extends BaseEntity<Components, Config> {
161
+ get health() { return this.components.health?.health; }
162
+ }
163
+ class ItemEntity extends BaseEntity<Components, Config> {
164
+ get value() { return this.components.value?.value; }
165
+ }
166
+
167
+ const classes = defineEntityClasses(registry, {
168
+ actor: { entity: ActorEntity, components: ['health'] },
169
+ item: { entity: ItemEntity, components: ['value'] },
170
+ } as const);
171
+
172
+ const templates = defineEntityConfigs(classes, {
173
+ guardian: { type: 'guardian', class: 'actor', maxHealth: 100 },
174
+ token: { type: 'token', class: 'item', value: 5 },
175
+ } as const);
176
+
177
+ type Entities = EntityInstancesOf<typeof classes>;
178
+ const factory = new EntityFactory<Components, Config, Entities>(templates, classes);
179
+ const world = new BaseWorld<typeof registry, Components, Config, Entities>(registry, { factory });
180
+
181
+ const item = world.loadEntity({ type: 'token' });
182
+ console.log(item instanceof ItemEntity); // true
183
+ console.log(item.components.entity.class); // "item"
184
+ console.log(item.id, item.type, item.dead); // BaseEntity identity accessors
185
+ ```
186
+
187
+ `defineEntityConfigs` checks each template against its class's component definitions, so adding a health-only
188
+ property to the item template is a type error. The factory repeats the boundary at runtime: loading or attaching
189
+ a component outside the class allowlist throws, protecting dynamically loaded data too. Client/editor metadata
190
+ that no component consumes should remain in a separate authoring type and be transformed into the scoped ECS
191
+ template.
192
+
193
+ Classless factories keep the original behavior: they create `BaseEntity` and scan the complete registry.
194
+
135
195
  ## Iterating entities
136
196
 
137
197
  `world.entities` is a `Map` keyed by `eid`, not an array, and so is `entities` on `EntitySystem` and
@@ -307,7 +367,7 @@ class SpawnerSystem extends WorkerSystem<Components, SpawnerWorld> {
307
367
  ```
308
368
 
309
369
  Its worker entry uses `createSystemWorker` (the run-function counterpart of `createEntitySystemWorker`),
310
- passing the registry only when it creates entities:
370
+ passing the registry only when it creates entities or adds components:
311
371
 
312
372
  ```ts
313
373
  import { createSystemWorker } from '@daneren2005/shared-memory-ecs/worker';
@@ -324,7 +384,8 @@ barrel: importing `createEntitySystemWorker` from `@daneren2005/shared-memory-ec
324
384
  `BaseWorld`, every system, their `@daneren2005/shared-memory-objects` dependencies - into the worker, even
325
385
  though a worker never runs any of it (easily ~20kb of dead code per worker). Import worker-side helpers from
326
386
  the `@daneren2005/shared-memory-ecs/worker` subpath instead. It exposes only what runs in a worker -
327
- `createEntitySystemWorker`, `createSystemWorker`, `createEntityWorker`, `killEntityWorker`, `DEAD_INDEX`,
387
+ `createEntitySystemWorker`, `createSystemWorker`, `createEntityWorker`, `addComponentWorker`,
388
+ `removeComponentWorker`, `killEntityWorker`, `DEAD_INDEX`,
328
389
  `TYPE_INDEX` (plus the worker-relevant types) - so the bundle stays tiny:
329
390
 
330
391
  ```ts
@@ -340,10 +401,9 @@ or `killEntityWorker` should import them from `/worker` too. Type-only imports (
340
401
  `EntityWorkerSystemWorld`, ...) can come from either path since types are erased, and main-thread code
341
402
  (`EntityWorkerSystem`, `BaseWorld`, `EntityFactory`, ...) keeps importing from the package root.
342
403
 
343
- A worker that creates entities (see [Creating entities from a worker](#creating-entities-from-a-worker)) passes
344
- your component registry as the third argument - `createEntitySystemWorker(self, shipUpdate, registry)` - so it has
345
- each component's `toBlock`. Only do this in workers that actually create entities; it pulls the registry (and
346
- whatever it imports) into that worker's bundle.
404
+ A worker that creates entities or adds components passes your component registry as the third argument -
405
+ `createEntitySystemWorker(self, shipUpdate, registry)` - so it has each component's `toBlock`. Only do this in
406
+ workers that need it; it pulls the registry (and whatever it imports) into that worker's bundle.
347
407
 
348
408
  ### Reading an entity's type in a worker
349
409
 
@@ -378,7 +438,8 @@ on the main thread) resolves the same way.
378
438
  An update function runs on shared memory, so anything it writes is already visible on the main thread. What
379
439
  it cannot do from there is touch the world, so the things that have to happen back on it go through
380
440
  `callbacks`: `entityComponentChanged` (emitted on the entity as `component-property-updated`), `entityDied`
381
- (as `death`), and `createEntity` (see [Creating entities from a worker](#creating-entities-from-a-worker)).
441
+ (as `death`), `createEntity` (see [Creating entities from a worker](#creating-entities-from-a-worker)), and
442
+ component changes requested by `addComponentWorker` / `removeComponentWorker`.
382
443
  All of them are collected during the run and applied once it completes.
383
444
 
384
445
  `emitEntityEvent` is the escape hatch for an event of your own: name it whatever you like and give it
@@ -448,10 +509,12 @@ createEntityWorker(world, { type: 'ship', x: 100, y: 150 }, callbacks);
448
509
 
449
510
  `createEntityWorker` layers your overrides over the `ship` template, mints a unique id from a shared atomic
450
511
  counter (so it never collides with one the main thread or another worker hands out), and for each component
451
- the merged config triggers, pushes a block into its pool and writes the values - all off-thread. It reports
512
+ the merged config triggers, pushes a block into its pool and writes the values - all off-thread. With entity
513
+ classes registered, the worker also resolves the template's class and considers only its allowed components. It reports
452
514
  back an id-plus-block-indexes descriptor; when the run completes the main thread *adopts* it, building the
453
- always-present `entity` component there (interning the `type` is a main-thread job) and wrapping each block the
454
- worker wrote - no block is copied or re-allocated. Like every other worker report-back, the new entity first
515
+ always-present `entity` component there (interning `type` and `class` is a main-thread job), asking the factory
516
+ for the registered wrapper, and attaching each block the worker wrote - no block is copied or re-allocated. Like
517
+ every other worker report-back, the new entity first
455
518
  exists on the following frame, so the system picks it up next run.
456
519
 
457
520
  Two things make this work, both opt-in so only the workers that create entities pay for them:
@@ -483,9 +546,48 @@ const shipHealth: ComponentDefinition<HealthComponent, Int32Array, HealthConfig>
483
546
  };
484
547
  ```
485
548
 
486
- This runs identically on the main-thread fallback. Two current limits: a `loadInFinishLoading` component (one
487
- that reads other entities) is skipped, since the worker has no world to read; and an adopted entity is always
488
- the base `BaseEntity` - a factory per-type subclass is not applied to it.
549
+ This runs identically on the main-thread fallback. A `loadInFinishLoading` component (one that reads other
550
+ entities) is skipped, since the worker has no world to read. A worker does not receive constructors; it returns
551
+ the serializable class key, and the main-thread factory constructs the wrapper during adoption.
552
+
553
+ ### Adding and removing components at runtime
554
+
555
+ On the main thread, use the entity directly:
556
+
557
+ ```ts
558
+ ship.loadComponent('shield', { capacity: 100 });
559
+ ship.removeComponent('shield');
560
+ ```
561
+
562
+ Both operations notify every system. Required and excluded (`not`) queries change membership as needed. An
563
+ entity already present through other required components is re-sent to worker systems too, so their cached
564
+ component bundle gains or loses an optional component on the next run.
565
+
566
+ Inside an update function, use the worker helpers instead. They record structural changes without changing the
567
+ component snapshot currently being iterated:
568
+
569
+ ```ts
570
+ import { addComponentWorker, removeComponentWorker } from '@daneren2005/shared-memory-ecs/worker';
571
+
572
+ if(equippedShieldGenerator && !components.shield) {
573
+ addComponentWorker(world, entityId, 'shield', { capacity: 100 }, callbacks);
574
+ } else if(!equippedShieldGenerator && components.shield) {
575
+ removeComponentWorker(entityId, 'shield', callbacks);
576
+ }
577
+ ```
578
+
579
+ The main thread applies those commands after the worker run completes, emits the normal component-added or
580
+ component-removed event, and queues query deltas. The originating run therefore keeps a stable snapshot and all
581
+ systems see the new structure no later than their next run. Removing a component uses the same deferred-free
582
+ safety described below.
583
+
584
+ Worker-side addition needs two opt-ins because it allocates a block:
585
+
586
+ - Set `addsComponents: true` on the `EntityWorkerSystem`.
587
+ - Pass the component registry to that system's `createEntitySystemWorker(self, update, registry)` call.
588
+
589
+ `addComponentWorker` calls the definition's worker-safe `toBlock(config)` and cannot add the always-present
590
+ `entity` component or a `loadInFinishLoading` component. Worker-side removal needs no allocation opt-in.
489
591
 
490
592
  ### Freeing component memory safely
491
593
 
@@ -0,0 +1,3 @@
1
+ import type { ComponentMap } from '../component-definition';
2
+ import type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld } from '../systems/entity-worker-system';
3
+ export default function addComponentWorker<C extends ComponentMap, K extends keyof C>(world: EntityWorkerSystemWorld, entityId: number, componentName: K, config: Record<string, unknown>, callbacks: EntityWorkerSystemCallbacks<C>): void;
@@ -1,4 +1,5 @@
1
- import type { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedEntity } from '../systems/entity-worker-system';
1
+ import type { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedComponent, WorkerCreatedEntity } from '../systems/entity-worker-system';
2
+ import type { WorkerEntityClassRegistry } from '../entity-class';
2
3
  export interface WorkerCreatableComponent {
3
4
  loadProperties: Array<string>;
4
5
  loadInFinishLoading?: boolean;
@@ -10,4 +11,5 @@ export type WorkerCreateRegistry = {
10
11
  export type FactoryConfigs = {
11
12
  [type: string]: Record<string, unknown>;
12
13
  };
13
- export declare function buildWorkerEntity(config: WorkerCreateEntityConfig, factoryConfigs: FactoryConfigs, registry: WorkerCreateRegistry, allocator: WorkerAllocator): WorkerCreatedEntity;
14
+ export declare function buildWorkerEntity(config: WorkerCreateEntityConfig, factoryConfigs: FactoryConfigs, registry: WorkerCreateRegistry, allocator: WorkerAllocator, classes?: WorkerEntityClassRegistry): WorkerCreatedEntity;
15
+ export declare function buildWorkerComponent(name: string, config: Record<string, unknown>, registry: WorkerCreateRegistry, allocator: WorkerAllocator): WorkerCreatedComponent;
@@ -0,0 +1,3 @@
1
+ import type { ComponentMap } from '../component-definition';
2
+ import type { EntityWorkerSystemCallbacks } from '../systems/entity-worker-system';
3
+ export default function removeComponentWorker<C extends ComponentMap, K extends keyof C>(entityId: number, componentName: K, callbacks: EntityWorkerSystemCallbacks<C>): void;
@@ -88,32 +88,53 @@ function o(e, t) {
88
88
  }
89
89
  //#endregion
90
90
  //#region src/actions/build-worker-entity.ts
91
- function s(e, t, n, r) {
92
- let i = {
93
- ...t[e.type] ?? {},
91
+ function s(e, t, n, r, i) {
92
+ let a = t[e.type] ?? {};
93
+ if (i && Object.prototype.hasOwnProperty.call(e, "class")) throw Error("Entity class comes from its type template and cannot be supplied by a worker config");
94
+ let o = {
95
+ ...a,
94
96
  ...e
95
- }, a = r.allocateEid(), o = {};
96
- for (let e of Object.keys(n)) {
97
+ }, s = typeof a.class == "string" ? a.class : void 0, c = Object.keys(n);
98
+ if (i) {
99
+ if (a.type !== e.type) throw Error(`Entity template ${e.type} must declare the same type`);
100
+ let t = s ? i[s] : void 0;
101
+ if (!t) throw Error(`Entity type ${e.type} has unknown class ${s ?? ""}`);
102
+ c = t.components;
103
+ }
104
+ let l = r.allocateEid(), u = {};
105
+ for (let e of c) {
97
106
  if (e === "entity") continue;
98
107
  let t = n[e];
99
- t.loadInFinishLoading || t.loadProperties.some((e) => e in i) && (o[e] = r.allocateComponentBlock(e, t.toBlock(i)));
108
+ if (!t) throw Error(`Entity class ${s ?? ""} contains unknown component ${e}`);
109
+ t.loadInFinishLoading || t.loadProperties.some((e) => e in o) && (u[e] = r.allocateComponentBlock(e, t.toBlock(o)));
100
110
  }
101
111
  return {
102
- eid: a,
112
+ eid: l,
103
113
  type: e.type,
104
- isStatic: i.isStatic,
105
- components: o
114
+ class: s,
115
+ isStatic: o.isStatic,
116
+ components: u
117
+ };
118
+ }
119
+ function c(e, t, n, r) {
120
+ if (e === "entity") throw Error("The entity component cannot be added at runtime");
121
+ let i = n[e];
122
+ if (!i) throw Error(`Unknown component: ${e}`);
123
+ if (i.loadInFinishLoading) throw Error(`Component ${e} requires main-thread finishLoading and cannot be added in a worker`);
124
+ return {
125
+ name: e,
126
+ index: r.allocateComponentBlock(e, i.toBlock(t))
106
127
  };
107
128
  }
108
129
  //#endregion
109
130
  //#region src/component.ts
110
- var c = class {
131
+ var l = class {
111
132
  index;
112
133
  block;
113
134
  constructor(e, t) {
114
135
  this.block = e, this.index = t;
115
136
  }
116
- }, l = 0, u = 1, d = 2, f = class extends c {
137
+ }, u = 0, d = 1, f = 2, p = 3, m = class extends l {
117
138
  cache;
118
139
  constructor(e, t, n) {
119
140
  super(e, t), this.cache = n;
@@ -121,8 +142,8 @@ var c = class {
121
142
  get type() {
122
143
  return this.cache.getString(this.block[2]) ?? "";
123
144
  }
124
- set type(e) {
125
- this.block[2] = e ? this.cache.getOrCreate(e).pointer : 0;
145
+ get class() {
146
+ return this.cache.getString(this.block[3]) ?? "";
126
147
  }
127
148
  get dead() {
128
149
  return this.block[0] === 1;
@@ -136,20 +157,21 @@ var c = class {
136
157
  set isStatic(e) {
137
158
  this.block[1] = +!!e;
138
159
  }
139
- }, p = {
160
+ }, h = {
140
161
  type: Uint32Array,
141
- size: 3,
162
+ size: 4,
142
163
  loadProperties: ["type", "isStatic"],
143
164
  toBlock(e, t) {
144
- let n = t.world.constantStrings, r = e.type ? n.getOrCreate(e.type).pointer : 0;
165
+ let n = t.world.constantStrings, r = e.type ? n.getOrCreate(e.type).pointer : 0, i = e.class ? n.getOrCreate(e.class).pointer : 0;
145
166
  return [
146
167
  +!!e.dead,
147
168
  +!!e.isStatic,
148
- r
169
+ r,
170
+ i
149
171
  ];
150
172
  },
151
173
  attach(e, t, n) {
152
- return new f(t.getBlock(n), n, e.world.constantStrings);
174
+ return new m(t.getBlock(n), n, e.world.constantStrings);
153
175
  },
154
176
  save(e) {
155
177
  let t = {};
@@ -158,53 +180,64 @@ var c = class {
158
180
  };
159
181
  //#endregion
160
182
  //#region src/actions/kill-entity-worker.ts
161
- function m(e, t, n) {
183
+ function g(e, t, n) {
162
184
  let r = t.entity;
163
185
  r && (r[0] = 1), n.entityDied(e);
164
186
  }
165
187
  //#endregion
166
188
  //#region src/actions/create-entity-worker.ts
167
- function h(e, t, n) {
189
+ function _(e, t, n) {
168
190
  if (!e.buildEntityDescriptor) throw Error("createEntityWorker requires the system to be registered with createsEntities: true and the component registry passed to createEntitySystemWorker");
169
191
  n.createEntity(e.buildEntityDescriptor(t));
170
192
  }
171
193
  //#endregion
194
+ //#region src/actions/add-component-worker.ts
195
+ function v(e, t, n, r, i) {
196
+ if (!e.buildComponentDescriptor) throw Error("addComponentWorker requires addsComponents: true and the component registry passed to createEntitySystemWorker");
197
+ i.addComponent(t, e.buildComponentDescriptor(String(n), r));
198
+ }
199
+ //#endregion
200
+ //#region src/actions/remove-component-worker.ts
201
+ function y(e, t, n) {
202
+ n.removeComponent(e, t);
203
+ }
204
+ //#endregion
172
205
  //#region src/systems/workers/create-entity-system-worker.ts
173
- function g(t, n, r) {
174
- let c = [], l = {}, u, d, f, p = {}, m, h, g = (e) => f?.getString(e) ?? "";
175
- t.onmessage = function(v) {
176
- let y = v.data;
177
- if (y.type === "init") _(t, { type: "init-complete" });
178
- else if (y.type === "load") {
179
- if (y.heap && (d = new e(y.heap), f = new a(d), d.addOnGrowBufferHandlers((e) => _(t, {
206
+ function b(t, n, r) {
207
+ let l = [], u = {}, d, f, p, m = {}, h, g, _, v = !1, y = (e) => p?.getString(e) ?? "";
208
+ t.onmessage = function(b) {
209
+ let S = b.data;
210
+ if (S.type === "init") x(t, { type: "init-complete" });
211
+ else if (S.type === "load") {
212
+ if (S.heap && (f = new e(S.heap), p = new a(f), f.addOnGrowBufferHandlers((e) => x(t, {
180
213
  type: "grow-buffer-from-worker",
181
214
  buffer: e
182
- }))), d && y.sharedMemory) {
183
- p = {};
184
- for (let e of Object.keys(y.sharedMemory.components)) p[e] = i.fromSharedMemory(d, y.sharedMemory.components[e]);
185
- m = d.getSharedAlloc(y.sharedMemory.eidCounter);
215
+ }))), f && S.sharedMemory) {
216
+ m = {};
217
+ for (let e of Object.keys(S.sharedMemory.components)) m[e] = i.fromSharedMemory(f, S.sharedMemory.components[e]);
218
+ h = f.getSharedAlloc(S.sharedMemory.eidCounter);
186
219
  }
187
- h = y.factoryConfigs, u = n.init?.(y.data) ?? void 0, _(t, { type: "loaded" });
188
- } else if (y.type === "grow-buffer") d && d.buffers[y.buffer.bufferPosition] === void 0 && d.addSharedBuffer(y.buffer);
189
- else if (y.type === "reset") {
190
- c = [];
191
- for (let e of Object.keys(l)) delete l[e];
192
- u = void 0;
193
- } else if (y.type === "run") {
194
- u && Object.assign(y.world, u), y.world.getString = g, y.world.heap = d;
220
+ g = S.factoryConfigs, _ = S.factoryClasses, v = !!S.addsComponents, d = n.init?.(S.data) ?? void 0, x(t, { type: "loaded" });
221
+ } else if (S.type === "grow-buffer") f && f.buffers[S.buffer.bufferPosition] === void 0 && f.addSharedBuffer(S.buffer);
222
+ else if (S.type === "reset") {
223
+ l = [];
224
+ for (let e of Object.keys(u)) delete u[e];
225
+ d = void 0;
226
+ } else if (S.type === "run") {
227
+ d && Object.assign(S.world, d), S.world.getString = y, S.world.heap = f;
195
228
  let e = {
196
- allocateEid: () => m ? Atomics.add(m.data, 0, 1) + 1 : 0,
197
- allocateComponentBlock: (e, t) => p[e].create(t)
229
+ allocateEid: () => h ? Atomics.add(h.data, 0, 1) + 1 : 0,
230
+ allocateComponentBlock: (e, t) => m[e].create(t)
198
231
  };
199
- y.world.allocate = e, h && r && (y.world.buildEntityDescriptor = (t) => s(t, h, r, e));
200
- let i = performance.now(), a = [], f = {}, v = [], b = [];
201
- c = o(c, y.entities);
202
- let x = {};
203
- Object.entries(y.queries).forEach(([e, t]) => {
204
- let n = o(l[e] ?? [], t);
205
- l[e] = n, x[e] = n;
232
+ S.world.allocate = e, g && r && (S.world.buildEntityDescriptor = (t) => s(t, g, r, e, _)), v && r && (S.world.buildComponentDescriptor = (t, n) => c(t, n, r, e));
233
+ let i = performance.now(), a = [], p = {}, b = [], C = [], w = [];
234
+ l = o(l, S.entities);
235
+ let T = {};
236
+ Object.entries(S.queries).forEach(([e, t]) => {
237
+ let n = o(u[e] ?? [], t);
238
+ u[e] = n, T[e] = n;
206
239
  });
207
- let S = {
240
+ let E = {
208
241
  entityComponentChanged(e, t, n, r) {
209
242
  a.push({
210
243
  entityId: e,
@@ -224,7 +257,7 @@ function g(t, n, r) {
224
257
  });
225
258
  },
226
259
  emitSystemEvent(e, t) {
227
- (f[e] ?? (f[e] = [])).push(t);
260
+ (p[e] ?? (p[e] = [])).push(t);
228
261
  },
229
262
  entityDied(e) {
230
263
  a.push({
@@ -234,64 +267,79 @@ function g(t, n, r) {
234
267
  });
235
268
  },
236
269
  createEntity(e) {
237
- v.push(e);
270
+ b.push(e);
271
+ },
272
+ addComponent(e, t) {
273
+ C.push({
274
+ type: "add",
275
+ entityId: e,
276
+ component: t
277
+ });
278
+ },
279
+ removeComponent(e, t) {
280
+ C.push({
281
+ type: "remove",
282
+ entityId: e,
283
+ componentName: String(t)
284
+ });
238
285
  }
239
- }, C = !1;
286
+ }, D = !1;
240
287
  if (n.preRun) try {
241
- n.preRun(y.world, c, x, S);
288
+ n.preRun(S.world, l, T, E);
242
289
  } catch (e) {
243
- C = !0, b.push({
290
+ D = !0, w.push({
244
291
  error: e,
245
292
  phase: "preRun"
246
293
  });
247
294
  }
248
- C || c.forEach((e) => {
295
+ D || l.forEach((e) => {
249
296
  try {
250
- n(y.world, e.entityId, e.components, x, S);
297
+ n(S.world, e.entityId, e.components, T, E);
251
298
  } catch (t) {
252
- b.push({
299
+ w.push({
253
300
  error: t,
254
301
  phase: "update",
255
302
  entityId: e.entityId
256
303
  });
257
304
  }
258
- }), n.entityRemoved && y.entities.removed.forEach((e) => {
305
+ }), n.entityRemoved && S.entities.removed.forEach((e) => {
259
306
  try {
260
- n.entityRemoved(y.world, e, S);
307
+ n.entityRemoved(S.world, e, E);
261
308
  } catch (t) {
262
- b.push({
309
+ w.push({
263
310
  error: t,
264
311
  phase: "entityRemoved",
265
312
  entityId: e
266
313
  });
267
314
  }
268
315
  });
269
- let w = performance.now() - i;
270
- _(t, {
316
+ let O = performance.now() - i;
317
+ x(t, {
271
318
  type: "run-complete",
272
- generation: y.generation,
273
- runTime: w,
319
+ generation: S.generation,
320
+ runTime: O,
274
321
  events: a,
275
- systemEvents: f,
276
- created: v,
277
- errors: b
322
+ systemEvents: p,
323
+ created: b,
324
+ componentChanges: C,
325
+ errors: w
278
326
  });
279
327
  }
280
328
  };
281
329
  }
282
- function _(e, t) {
330
+ function x(e, t) {
283
331
  e.postMessage(t);
284
332
  }
285
333
  //#endregion
286
334
  //#region src/systems/workers/create-system-worker.ts
287
- function v(e) {
335
+ function S(e) {
288
336
  let t = (() => {});
289
337
  return t.preRun = (t, n, r, i) => e(t, r, i), e.init && (t.init = e.init), e.entityRemoved && (t.entityRemoved = e.entityRemoved), t;
290
338
  }
291
- function y(e, t, n) {
292
- g(e, v(t), n);
339
+ function C(e, t, n) {
340
+ b(e, S(t), n);
293
341
  }
294
342
  //#endregion
295
- export { m as a, d as c, s as d, o as f, h as i, p as l, i as m, v as n, l as o, a as p, g as r, u as s, y as t, c as u };
343
+ export { a as _, v as a, p as c, f as d, h as f, o as g, s as h, y as i, u as l, c as m, S as n, _ as o, l as p, b as r, g as s, C as t, d as u, i as v };
296
344
 
297
- //# sourceMappingURL=create-system-worker-BM5E-S92.js.map
345
+ //# sourceMappingURL=create-system-worker-DURlIMia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-system-worker-DURlIMia.js","names":[],"sources":["../src/memory-component.ts","../src/constant-string-cache.ts","../src/systems/workers/apply-query-delta.ts","../src/actions/build-worker-entity.ts","../src/component.ts","../src/entity-component.ts","../src/actions/kill-entity-worker.ts","../src/actions/create-entity-worker.ts","../src/actions/add-component-worker.ts","../src/actions/remove-component-worker.ts","../src/systems/workers/create-entity-system-worker.ts","../src/systems/workers/create-system-worker.ts"],"sourcesContent":["import SharedPool from '@daneren2005/shared-memory-objects/shared-pool';\nimport type { SharedPoolMemory } from '@daneren2005/shared-memory-objects/shared-pool';\nimport type MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\nimport type { TypedArrayConstructor } from '@daneren2005/shared-memory-objects/interfaces/typed-array-constructor';\n\nexport type ComponentTypedArray = Uint32Array | Int32Array | Float32Array | Float64Array;\n\n// Backed by a SharedPool: all bookkeeping lives in the shared heap, so a worker can reconstruct a handle over\n// the same pool (pass the owner's getSharedMemory() as `memory`) and allocate/read blocks off-thread.\nexport default class MemoryComponent<T extends ComponentTypedArray = ComponentTypedArray> {\n\theap: MemoryHeap;\n\tpool: SharedPool<T>;\n\n\tconstructor(heap: MemoryHeap, type: TypedArrayConstructor<T>, dataLength: number, memory?: SharedPoolMemory) {\n\t\tthis.heap = heap;\n\t\tthis.pool = memory\n\t\t\t? new SharedPool<T>(heap, memory)\n\t\t\t: new SharedPool<T>(heap, {\n\t\t\t\ttype,\n\t\t\t\tdataLength,\n\t\t\t});\n\t}\n\n\t// Reconstructs a handle over an existing pool (from the owner's getSharedMemory()) — used in a worker, where the\n\t// pool's type/dataLength are already recorded in the heap header, so no definition is needed.\n\tstatic fromSharedMemory<T extends ComponentTypedArray = ComponentTypedArray>(heap: MemoryHeap, memory: SharedPoolMemory): MemoryComponent<T> {\n\t\treturn new MemoryComponent<T>(heap, Uint32Array as unknown as TypedArrayConstructor<T>, 1, memory);\n\t}\n\n\tgetSharedMemory(): SharedPoolMemory {\n\t\treturn this.pool.getSharedMemory();\n\t}\n\n\tget length() {\n\t\treturn this.pool.length;\n\t}\n\tget rawLength() {\n\t\treturn this.pool.bufferLength;\n\t}\n\n\tcreate(values: Array<number>): number {\n\t\treturn this.pool.push(values);\n\t}\n\n\tgetBlock(index: number): T {\n\t\treturn this.pool.at(index);\n\t}\n\tget(index: number, dataIndex: number): number {\n\t\treturn this.pool.get(index, dataIndex);\n\t}\n\tset(index: number, dataIndex: number, value: number) {\n\t\tlet array = this.pool.at(index);\n\t\tarray[dataIndex] = value;\n\t}\n\n\tdelete(index: number) {\n\t\tthis.pool.deleteIndex(index);\n\t}\n\tclear() {\n\t\tthis.pool.clear();\n\t}\n}\n","import ConstantString from '@daneren2005/shared-memory-objects/constant-string';\nimport { getPointer } from '@daneren2005/shared-memory-objects/utils/pointer';\nimport type MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\n\n// Interns immutable strings in the heap and resolves them back from a pointer. Every distinct value is allocated\n// once as a single ConstantString shared by all users (15 \"Space Ship\" entities point at one allocation), and a\n// pointer resolves to its string through a Map hit before ever rebuilding it from memory. Both sides of the\n// worker boundary hold one: the main thread creates + interns; a worker (with a heap reconstructed from the same\n// SharedArrayBuffers) only ever resolves pointers and caches the results.\nexport default class ConstantStringCache {\n\tprivate heap: MemoryHeap;\n\t// value -> the one interned allocation. Only the creating (main) thread populates this; it owns the memory.\n\tprivate byValue = new Map<string, ConstantString>();\n\t// pointer -> value: the fast lookup every thread checks before touching memory.\n\tprivate byPointer = new Map<number, string>();\n\n\tconstructor(heap: MemoryHeap) {\n\t\tthis.heap = heap;\n\t}\n\n\t// Dedupes on value, so repeated types share a single ConstantString. Main-thread only.\n\tgetOrCreate(value: string): ConstantString {\n\t\tlet existing = this.byValue.get(value);\n\t\tif(existing) {\n\t\t\treturn existing;\n\t\t}\n\n\t\tconst string = new ConstantString(this.heap, value);\n\t\tthis.byValue.set(value, string);\n\t\tthis.byPointer.set(string.pointer, value);\n\n\t\treturn string;\n\t}\n\n\t// pointer -> string, checking the cache before rebuilding from memory. A pointer of 0 is the empty string; a\n\t// pointer into a buffer that has not synced to this thread yet returns undefined.\n\tgetString(pointer: number): string | undefined {\n\t\tif(pointer === 0) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet cached = this.byPointer.get(pointer);\n\t\tif(cached !== undefined) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst memory = getPointer(pointer);\n\t\tif(this.heap.buffers[memory.bufferPosition] === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst value = new ConstantString(this.heap, memory).value;\n\t\tthis.byPointer.set(pointer, value);\n\n\t\treturn value;\n\t}\n\n\t// Frees every allocation this cache owns and empties the lookups. A worker's cache owns nothing (it only ever\n\t// resolved views), so this just drops its pointer lookups.\n\tclear() {\n\t\tthis.byValue.forEach(string => string.free());\n\t\tthis.byValue.clear();\n\t\tthis.byPointer.clear();\n\t}\n}\n","import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../entity-worker-system';\n\n// Applies a run's delta to a query's persistent list: drop the entities that left, upsert those that joined or\n// changed. A steady-state run carries empty arrays and returns the list untouched. Returns the list (removals\n// rebuild it via filter), so callers must store the returned reference back.\nexport function applyQueryDelta<T extends EntityUpdateComponents>(\n\tlist: Array<UpdateEntityConfigObject<T>>,\n\tdelta: QueryDelta<T>,\n): Array<UpdateEntityConfigObject<T>> {\n\tif(delta.removed.length) {\n\t\tconst removedSet = new Set(delta.removed);\n\t\tlist = list.filter(entry => !removedSet.has(entry.entityId));\n\t}\n\n\tif(delta.added.length) {\n\t\t// Index existing members so a re-added entity replaces its entry in place instead of duplicating.\n\t\tconst indexByEid = new Map<number, number>();\n\t\tlist.forEach((entry, index) => indexByEid.set(entry.entityId, index));\n\n\t\tfor(let entity of delta.added) {\n\t\t\tconst existing = indexByEid.get(entity.entityId);\n\t\t\tif(existing !== undefined) {\n\t\t\t\tlist[existing] = entity;\n\t\t\t} else {\n\t\t\t\tindexByEid.set(entity.entityId, list.length);\n\t\t\t\tlist.push(entity);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn list;\n}\n","import type { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedComponent, WorkerCreatedEntity } from '../systems/entity-worker-system';\nimport type { WorkerEntityClassRegistry } from '../entity-class';\n\n// The slice of a component definition worker-side creation needs. Both the game's ComponentDefinitionMap and the\n// world's RegisteredComponentRegistry satisfy it.\nexport interface WorkerCreatableComponent {\n\tloadProperties: Array<string>\n\tloadInFinishLoading?: boolean\n\ttoBlock(config: Record<string, unknown>): Array<number>\n}\nexport type WorkerCreateRegistry = { [name: string]: WorkerCreatableComponent };\nexport type FactoryConfigs = { [type: string]: Record<string, unknown> };\n\n// Turns a factory-style config ({ type, ...overrides }) into a WorkerCreatedEntity, off-thread: layers the type's\n// factory template under the overrides, mints an id, and allocates + writes each triggered component's block via its\n// toBlock(). The always-present `entity` component is NOT built here - the main thread builds it on adopt (interning\n// the type is main-thread-only), reading `type`/`isStatic` off this descriptor. Shared by the real worker and the\n// main-thread fallback so both behave identically.\nexport function buildWorkerEntity(\n\tconfig: WorkerCreateEntityConfig,\n\tfactoryConfigs: FactoryConfigs,\n\tregistry: WorkerCreateRegistry,\n\tallocator: WorkerAllocator,\n\tclasses?: WorkerEntityClassRegistry,\n): WorkerCreatedEntity {\n\tconst template = factoryConfigs[config.type] ?? {};\n\tif(classes && Object.prototype.hasOwnProperty.call(config, 'class')) {\n\t\tthrow new Error('Entity class comes from its type template and cannot be supplied by a worker config');\n\t}\n\tconst merged: Record<string, unknown> = { ...template, ...config };\n\tconst entityClass = typeof template.class === 'string' ? template.class : undefined;\n\tlet componentNames = Object.keys(registry);\n\tif(classes) {\n\t\tif(template.type !== config.type) {\n\t\t\tthrow new Error(`Entity template ${config.type} must declare the same type`);\n\t\t}\n\t\tconst definition = entityClass ? classes[entityClass] : undefined;\n\t\tif(!definition) {\n\t\t\tthrow new Error(`Entity type ${config.type} has unknown class ${entityClass ?? ''}`);\n\t\t}\n\t\tcomponentNames = definition.components;\n\t}\n\n\tconst eid = allocator.allocateEid();\n\tconst components: { [name: string]: number } = {};\n\tfor(let name of componentNames) {\n\t\t// The entity component is always built on the main thread when the descriptor is adopted (interning the type),\n\t\t// never off-thread - skip it here whether or not the registry includes it.\n\t\tif(name === 'entity') {\n\t\t\tcontinue;\n\t\t}\n\t\tconst definition = registry[name];\n\t\tif(!definition) {\n\t\t\tthrow new Error(`Entity class ${entityClass ?? ''} contains unknown component ${name}`);\n\t\t}\n\t\t// Deferred components need the whole world (other entities) that a worker doesn't have; skip them.\n\t\tif(definition.loadInFinishLoading) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(definition.loadProperties.some(prop => prop in merged)) {\n\t\t\tcomponents[name] = allocator.allocateComponentBlock(name, definition.toBlock(merged));\n\t\t}\n\t}\n\n\treturn {\n\t\teid,\n\t\ttype: config.type,\n\t\tclass: entityClass,\n\t\tisStatic: merged.isStatic as boolean | undefined,\n\t\tcomponents,\n\t};\n}\n\nexport function buildWorkerComponent(\n\tname: string,\n\tconfig: Record<string, unknown>,\n\tregistry: WorkerCreateRegistry,\n\tallocator: WorkerAllocator,\n): WorkerCreatedComponent {\n\tif(name === 'entity') {\n\t\tthrow new Error('The entity component cannot be added at runtime');\n\t}\n\tconst definition = registry[name];\n\tif(!definition) {\n\t\tthrow new Error(`Unknown component: ${name}`);\n\t}\n\tif(definition.loadInFinishLoading) {\n\t\tthrow new Error(`Component ${name} requires main-thread finishLoading and cannot be added in a worker`);\n\t}\n\n\treturn {\n\t\tname,\n\t\tindex: allocator.allocateComponentBlock(name, definition.toBlock(config)),\n\t};\n}\n","import type { ComponentTypedArray } from './memory-component';\nimport type { BaseComponent } from './component-definition';\n\n// Base class for a memory-backed component accessor. Subclass it, declare prototype get/set accessors over\n// `this.block` indexed by the component's exported *_INDEX constants, and return `new YourComponent(block, index)`\n// from `attach`. Because the accessors live on one shared prototype - not fresh closures captured per entity -\n// reads off thousands of entities every frame stay monomorphic and inline, and constructing a component allocates\n// just the instance instead of a closure per accessor. Components that own no extra memory need nothing else; a\n// subclass that does can take more constructor args (the entity, another pool) and store them as fields.\nexport default abstract class Component<T extends ComponentTypedArray = ComponentTypedArray> implements BaseComponent {\n\treadonly index: number;\n\treadonly block: T;\n\n\tconstructor(block: T, index: number) {\n\t\tthis.block = block;\n\t\tthis.index = index;\n\t}\n}\n","import type { ComponentDefinition } from './component-definition';\nimport Component from './component';\nimport type ConstantStringCache from './constant-string-cache';\n\nexport interface EntityComponent {\n\tindex: number\n\treadonly type: string\n\treadonly class: string\n\tdead: boolean\n\tisStatic: boolean\n}\n\nexport interface EntityComponentConfig {\n\ttype: string\n\tisStatic?: boolean\n}\n\nexport interface EntityClassConfig {\n\treadonly class: string\n}\n\nexport interface EntityComponentSerialization {\n\ttype?: string\n\tdead?: boolean\n}\n\nexport const DEAD_INDEX = 0;\nexport const STATIC_INDEX = 1;\nexport const TYPE_INDEX = 2;\nexport const CLASS_INDEX = 3;\n\n// Identity strings are interned, so the accessor needs the world's cache to resolve their pointers.\nclass EntityComponentImpl extends Component<Uint32Array> implements EntityComponent {\n\tprivate cache: ConstantStringCache;\n\n\tconstructor(block: Uint32Array, index: number, cache: ConstantStringCache) {\n\t\tsuper(block, index);\n\t\tthis.cache = cache;\n\t}\n\n\tget type() {\n\t\treturn this.cache.getString(this.block[TYPE_INDEX]) ?? '';\n\t}\n\tget class() {\n\t\treturn this.cache.getString(this.block[CLASS_INDEX]) ?? '';\n\t}\n\tget dead() {\n\t\treturn this.block[DEAD_INDEX] === 1;\n\t}\n\tset dead(value: boolean) {\n\t\tthis.block[DEAD_INDEX] = value ? 1 : 0;\n\t}\n\tget isStatic() {\n\t\treturn this.block[STATIC_INDEX] === 1;\n\t}\n\tset isStatic(value: boolean) {\n\t\tthis.block[STATIC_INDEX] = value ? 1 : 0;\n\t}\n}\n\nexport const entityDefinition: ComponentDefinition<EntityComponent, Uint32Array, EntityComponentConfig & Partial<EntityClassConfig>, EntityComponentSerialization> = {\n\ttype: Uint32Array,\n\tsize: 4,\n\tloadProperties: ['type', 'isStatic'],\n\t// The only component that reads the entity (never worker-created): it interns its identity strings through the heap and\n\t// stores the pointer. `entity` is always passed here since loadComponent runs on the main thread.\n\ttoBlock(config, entity) {\n\t\tconst cache = entity!.world.constantStrings;\n\t\tconst typePointer = config.type ? cache.getOrCreate(config.type).pointer : 0;\n\t\tconst classPointer = config.class ? cache.getOrCreate(config.class).pointer : 0;\n\t\treturn [config.dead ? 1 : 0, config.isStatic ? 1 : 0, typePointer, classPointer];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new EntityComponentImpl(memory.getBlock(index), index, entity.world.constantStrings);\n\t},\n\tsave(component) {\n\t\tconst config: EntityComponentSerialization = {};\n\t\tif(component.type) {\n\t\t\tconfig.type = component.type;\n\t\t}\n\t\tif(component.dead) {\n\t\t\tconfig.dead = true;\n\t\t}\n\n\t\treturn config;\n\t},\n};\n","import type { ComponentTypedArray } from '../memory-component';\nimport type { EntityWorkerSystemCallbacks, EntityUpdateComponents } from '../systems/entity-worker-system';\nimport { DEAD_INDEX } from '../entity-component';\n\n// Worker-side killEntity: flags the entity dead in its block and reports the death back via callbacks. The\n// entity component must be in the system's query for its block to be available here.\nexport default function killEntityWorker(entityId: number, components: EntityUpdateComponents, callbacks: EntityWorkerSystemCallbacks): void {\n\tconst block = (components as { entity?: ComponentTypedArray }).entity;\n\tif(block) {\n\t\tblock[DEAD_INDEX] = 1;\n\t}\n\n\tcallbacks.entityDied(entityId);\n}\n","import type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, WorkerCreateEntityConfig } from '../systems/entity-worker-system';\n\n// Worker-side entity creation from a factory config. The worker merges the selected type template, resolves its\n// optional class, mints a unique id, and allocates + writes every allowed, triggered component block\n// directly into the shared pools (off-thread) via each component's toBlock(). It reports the descriptor back; the main\n// thread adopts it on run-complete (world.adoptEntity), building the `entity` component and class wrapper there\n// worker-written blocks, so the entity first exists on the following frame.\n//\n// Requires the system to be registered with `createsEntities: true` and its worker entry to pass the component\n// registry to createEntitySystemWorker (so the worker has each component's toBlock).\nexport default function createEntityWorker(world: EntityWorkerSystemWorld, config: WorkerCreateEntityConfig, callbacks: EntityWorkerSystemCallbacks): void {\n\tif(!world.buildEntityDescriptor) {\n\t\tthrow new Error('createEntityWorker requires the system to be registered with createsEntities: true and the component registry passed to createEntitySystemWorker');\n\t}\n\n\tcallbacks.createEntity(world.buildEntityDescriptor(config));\n}\n","import type { ComponentMap } from '../component-definition';\nimport type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld } from '../systems/entity-worker-system';\n\n// Allocates a component block in the worker and asks the main thread to attach it after the run. The entity's\n// current query snapshot is intentionally unchanged until the next run.\nexport default function addComponentWorker<C extends ComponentMap, K extends keyof C>(\n\tworld: EntityWorkerSystemWorld,\n\tentityId: number,\n\tcomponentName: K,\n\tconfig: Record<string, unknown>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n): void {\n\tif(!world.buildComponentDescriptor) {\n\t\tthrow new Error('addComponentWorker requires addsComponents: true and the component registry passed to createEntitySystemWorker');\n\t}\n\n\tcallbacks.addComponent(entityId, world.buildComponentDescriptor(String(componentName), config));\n}\n","import type { ComponentMap } from '../component-definition';\nimport type { EntityWorkerSystemCallbacks } from '../systems/entity-worker-system';\n\n// Requests a main-thread detach after the worker run. Shared memory is freed only after every system that could\n// still hold the old block has completed a run.\nexport default function removeComponentWorker<C extends ComponentMap, K extends keyof C>(\n\tentityId: number,\n\tcomponentName: K,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n): void {\n\tcallbacks.removeComponent(entityId, componentName);\n}\n","import MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\nimport type AllocatedMemory from '@daneren2005/shared-memory-objects/allocated-memory';\nimport type EntitySystemWorkerMessage from './entity-system-worker-message';\nimport type { EntityEvent, SystemEvents, WorkerRunError } from './entity-system-worker-message';\nimport ConstantStringCache from '../../constant-string-cache';\nimport MemoryComponent from '../../memory-component';\nimport type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';\nimport type {\n\tEntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents,\n\tEntityUpdateFunction, QueryDelta, UpdateEntityConfigObject, WorkerComponentChange, WorkerCreatedEntity,\n} from '../entity-worker-system';\nimport { buildWorkerComponent, buildWorkerEntity, type FactoryConfigs } from '../../actions/build-worker-entity';\nimport type { WorkerEntityClassRegistry } from '../../entity-class';\nimport { applyQueryDelta } from './apply-query-delta';\n\n// The slice of the worker global scope createEntitySystemWorker touches. Passing `self` explicitly (rather than\n// using the global) lets runners like @vitest/web-worker, which inject `self` as a module local, drive it.\nexport interface EntitySystemWorkerScope {\n\tonmessage: ((e: MessageEvent) => void) | null\n\tpostMessage(message: EntitySystemWorkerMessage): void\n}\n\nexport default function createEntitySystemWorker<\n\tC extends ComponentMap,\n\tT extends EntityUpdateComponents<C>,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(scope: EntitySystemWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>, definitions?: ComponentDefinitionMap) {\n\t// Persistent lists, carried across runs and mutated by each run's delta (see applyQueryDelta).\n\tlet entities: Array<UpdateEntityConfigObject<T>> = [];\n\tconst queryEntities: { [key: string]: Array<UpdateEntityConfigObject<T>> } = {};\n\t// What updateFunction.init returned: persistent state (e.g. a seeded RNG) merged onto `world` each run.\n\tlet worldExtension: Partial<W> | undefined;\n\tlet heap: MemoryHeap | undefined;\n\tlet stringCache: ConstantStringCache | undefined;\n\t// Reconstructed pool handles over the world's shared state, plus the factory templates - for off-thread entity\n\t// allocation. `definitions` (passed by the game's worker entry) supplies each component's toBlock/loadProperties.\n\tlet pools: { [name: string]: MemoryComponent } = {};\n\tlet eidCounter: AllocatedMemory | undefined;\n\tlet factoryConfigs: FactoryConfigs | undefined;\n\tlet factoryClasses: WorkerEntityClassRegistry | undefined;\n\tlet addsComponents = false;\n\tconst getString = (pointer: number): string => stringCache?.getString(pointer) ?? '';\n\n\tscope.onmessage = function(e) {\n\t\tconst message = e.data as EntitySystemWorkerMessage<W, D>;\n\n\t\tif(message.type === 'init') {\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'init-complete',\n\t\t\t});\n\t\t} else if(message.type === 'load') {\n\t\t\tif(message.heap) {\n\t\t\t\theap = new MemoryHeap(message.heap);\n\t\t\t\tstringCache = new ConstantStringCache(heap);\n\t\t\t\t// Report any buffer this worker grows (while allocating off-thread) back to the main thread, which adopts\n\t\t\t\t// it and fans it out to sibling workers.\n\t\t\t\theap.addOnGrowBufferHandlers(buffer => postMessageTyped(scope, { type: 'grow-buffer-from-worker', buffer }));\n\t\t\t}\n\t\t\tif(heap && message.sharedMemory) {\n\t\t\t\tpools = {};\n\t\t\t\tfor(const name of Object.keys(message.sharedMemory.components)) {\n\t\t\t\t\tpools[name] = MemoryComponent.fromSharedMemory(heap, message.sharedMemory.components[name]);\n\t\t\t\t}\n\t\t\t\teidCounter = heap.getSharedAlloc(message.sharedMemory.eidCounter);\n\t\t\t}\n\t\t\tfactoryConfigs = message.factoryConfigs;\n\t\t\tfactoryClasses = message.factoryClasses;\n\t\t\taddsComponents = !!message.addsComponents;\n\t\t\tworldExtension = updateFunction.init?.(message.data) ?? undefined;\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'loaded',\n\t\t\t});\n\t\t} else if(message.type === 'grow-buffer') {\n\t\t\t// Never replace a buffer this worker already holds: the main thread fans a worker-grown buffer back out to\n\t\t\t// every worker (including the one that grew it), and overwriting our own live MemoryBuffer would discard its\n\t\t\t// allocation bookkeeping. The SharedArrayBuffer at that position is already the same one.\n\t\t\tif(heap && heap.buffers[message.buffer.bufferPosition] === undefined) {\n\t\t\t\theap.addSharedBuffer(message.buffer);\n\t\t\t}\n\t\t} else if(message.type === 'reset') {\n\t\t\t// Drop the persistent lists so a reused world starts empty; worldExtension is refreshed by the next load.\n\t\t\tentities = [];\n\t\t\tfor(const key of Object.keys(queryEntities)) {\n\t\t\t\tdelete queryEntities[key];\n\t\t\t}\n\t\t\tworldExtension = undefined;\n\t\t} else if(message.type === 'run') {\n\t\t\tif(worldExtension) {\n\t\t\t\tObject.assign(message.world, worldExtension);\n\t\t\t}\n\t\t\tmessage.world.getString = getString;\n\t\t\tmessage.world.heap = heap;\n\t\t\tconst allocator = {\n\t\t\t\tallocateEid: () => eidCounter ? Atomics.add(eidCounter.data, 0, 1) + 1 : 0,\n\t\t\t\tallocateComponentBlock: (name: string, values: Array<number>) => pools[name].create(values),\n\t\t\t};\n\t\t\tmessage.world.allocate = allocator;\n\t\t\t// Only a system registered with createsEntities (factoryConfigs shipped) whose worker entry passed the\n\t\t\t// component definitions can create entities from a config.\n\t\t\tif(factoryConfigs && definitions) {\n\t\t\t\tmessage.world.buildEntityDescriptor = config => buildWorkerEntity(config, factoryConfigs!, definitions, allocator, factoryClasses);\n\t\t\t}\n\t\t\tif(addsComponents && definitions) {\n\t\t\t\tmessage.world.buildComponentDescriptor = (name, config) => buildWorkerComponent(name, config, definitions, allocator);\n\t\t\t}\n\t\t\tconst start = performance.now();\n\t\t\tlet entityEvents: Array<EntityEvent> = [];\n\t\t\tlet systemEvents: SystemEvents = {};\n\t\t\tlet createdEntities: Array<WorkerCreatedEntity> = [];\n\t\t\tlet componentChanges: Array<WorkerComponentChange> = [];\n\t\t\tlet errors: Array<WorkerRunError> = [];\n\n\t\t\tentities = applyQueryDelta(entities, message.entities as QueryDelta<T>);\n\n\t\t\tlet queries: EntityQueryComponents<C> = {};\n\t\t\tObject.entries(message.queries).forEach(([queryKey, delta]) => {\n\t\t\t\tconst list = applyQueryDelta(queryEntities[queryKey] ?? [], delta as QueryDelta<T>);\n\t\t\t\tqueryEntities[queryKey] = list;\n\t\t\t\tqueries[queryKey] = list;\n\t\t\t});\n\n\t\t\tlet callbacks: EntityWorkerSystemCallbacks<C> = {\n\t\t\t\tentityComponentChanged<K extends keyof C, P extends keyof C[K]>(entityId: number, componentName: K, prop: P, value: C[K][P]) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent: 'component-property-updated',\n\t\t\t\t\t\targs: [componentName, prop, value],\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\temitEntityEvent(entityId: number, event: string, ...args: Array<unknown>) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\targs,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\temitSystemEvent(event: string, entityId: number) {\n\t\t\t\t\t(systemEvents[event] ?? (systemEvents[event] = [])).push(entityId);\n\t\t\t\t},\n\t\t\t\tentityDied(entityId: number) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent: 'death',\n\t\t\t\t\t\targs: [],\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tcreateEntity(entity: WorkerCreatedEntity) {\n\t\t\t\t\tcreatedEntities.push(entity);\n\t\t\t\t},\n\t\t\t\taddComponent(entityId, component) {\n\t\t\t\t\tcomponentChanges.push({ type: 'add', entityId, component });\n\t\t\t\t},\n\t\t\t\tremoveComponent(entityId, componentName) {\n\t\t\t\t\tcomponentChanges.push({ type: 'remove', entityId, componentName: String(componentName) });\n\t\t\t\t},\n\t\t\t};\n\t\t\tlet preRunFailed = false;\n\t\t\tif(updateFunction.preRun) {\n\t\t\t\ttry {\n\t\t\t\t\tupdateFunction.preRun(message.world, entities, queries, callbacks);\n\t\t\t\t} catch(err) {\n\t\t\t\t\tpreRunFailed = true;\n\t\t\t\t\terrors.push({ error: err as Error, phase: 'preRun' });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// preRun sets up the run's state; if it threw, skip the entity loop rather than run over half-prepared data.\n\t\t\tif(!preRunFailed) {\n\t\t\t\tentities.forEach(entity => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tupdateFunction(message.world, entity.entityId, entity.components, queries, callbacks);\n\t\t\t\t\t} catch(err) {\n\t\t\t\t\t\t// One entity failing must not stop the rest of the run.\n\t\t\t\t\t\terrors.push({ error: err as Error, phase: 'update', entityId: entity.entityId });\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif(updateFunction.entityRemoved) {\n\t\t\t\tmessage.entities.removed.forEach(entityId => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tupdateFunction.entityRemoved!(message.world, entityId, callbacks);\n\t\t\t\t\t} catch(err) {\n\t\t\t\t\t\terrors.push({ error: err as Error, phase: 'entityRemoved', entityId });\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst runTime = performance.now() - start;\n\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'run-complete',\n\t\t\t\tgeneration: message.generation,\n\t\t\t\trunTime,\n\t\t\t\tevents: entityEvents,\n\t\t\t\tsystemEvents,\n\t\t\t\tcreated: createdEntities,\n\t\t\t\tcomponentChanges,\n\t\t\t\terrors,\n\t\t\t});\n\t\t}\n\t};\n}\n\nfunction postMessageTyped(scope: EntitySystemWorkerScope, message: EntitySystemWorkerMessage) {\n\tscope.postMessage(message);\n}\n","import createEntitySystemWorker, { type EntitySystemWorkerScope } from './create-entity-system-worker';\nimport type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';\nimport type {\n\tEntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents,\n\tEntityUpdateFunction, EntityUpdateInitFunction, EntityRemovedFunction,\n} from '../entity-worker-system';\n\n// A WorkerSystem's single per-run function: called once per run over the named sub-queries, not once per entity.\n// Optional `init`/`entityRemoved` mirror EntityUpdateFunction's hooks.\nexport type WorkerSystemRunFunction<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n> = ((world: W, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => void) & {\n\tinit?: EntityUpdateInitFunction<W, D>\n\tentityRemoved?: EntityRemovedFunction<C, W>\n};\n\n// A WorkerSystem reuses the EntityWorkerSystem worker machinery: the single run function becomes `preRun` (which runs\n// once with query access) and the per-entity body is a no-op, since a WorkerSystem never populates its main query.\nexport function toEntityUpdateFunction<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(run: WorkerSystemRunFunction<C, W, D>): EntityUpdateFunction<C, EntityUpdateComponents<C>, W, D> {\n\tconst updateFunction = (() => {}) as EntityUpdateFunction<C, EntityUpdateComponents<C>, W, D>;\n\tupdateFunction.preRun = (world, _entities, queries, callbacks) => run(world, queries, callbacks);\n\tif(run.init) {\n\t\tupdateFunction.init = run.init;\n\t}\n\tif(run.entityRemoved) {\n\t\tupdateFunction.entityRemoved = run.entityRemoved;\n\t}\n\n\treturn updateFunction;\n}\n\n// Worker entry helper for a WorkerSystem (mirrors createEntitySystemWorker). Pass the component registry only for a\n// worker that creates entities.\nexport default function createSystemWorker<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(scope: EntitySystemWorkerScope, runFunction: WorkerSystemRunFunction<C, W, D>, definitions?: ComponentDefinitionMap) {\n\tcreateEntitySystemWorker(scope, toEntityUpdateFunction(runFunction), definitions);\n}\n"],"mappings":";;;;;AASA,IAAqB,IAArB,MAAqB,EAAqE;CACzF;CACA;CAEA,YAAY,GAAkB,GAAgC,GAAoB,GAA2B;EAE5G,AADA,KAAK,OAAO,GACZ,KAAK,OAAO,IACT,IAAI,EAAc,GAAM,CAAM,IAC9B,IAAI,EAAc,GAAM;GACzB;GACA;EACD,CAAC;CACH;CAIA,OAAO,iBAAsE,GAAkB,GAA8C;EAC5I,OAAO,IAAI,EAAmB,GAAM,aAAoD,GAAG,CAAM;CAClG;CAEA,kBAAoC;EACnC,OAAO,KAAK,KAAK,gBAAgB;CAClC;CAEA,IAAI,SAAS;EACZ,OAAO,KAAK,KAAK;CAClB;CACA,IAAI,YAAY;EACf,OAAO,KAAK,KAAK;CAClB;CAEA,OAAO,GAA+B;EACrC,OAAO,KAAK,KAAK,KAAK,CAAM;CAC7B;CAEA,SAAS,GAAkB;EAC1B,OAAO,KAAK,KAAK,GAAG,CAAK;CAC1B;CACA,IAAI,GAAe,GAA2B;EAC7C,OAAO,KAAK,KAAK,IAAI,GAAO,CAAS;CACtC;CACA,IAAI,GAAe,GAAmB,GAAe;EACpD,IAAI,IAAQ,KAAK,KAAK,GAAG,CAAK;EAC9B,EAAM,KAAa;CACpB;CAEA,OAAO,GAAe;EACrB,KAAK,KAAK,YAAY,CAAK;CAC5B;CACA,QAAQ;EACP,KAAK,KAAK,MAAM;CACjB;AACD,GCpDqB,IAArB,MAAyC;CACxC;CAEA,0BAAkB,IAAI,IAA4B;CAElD,4BAAoB,IAAI,IAAoB;CAE5C,YAAY,GAAkB;EAC7B,KAAK,OAAO;CACb;CAGA,YAAY,GAA+B;EAC1C,IAAI,IAAW,KAAK,QAAQ,IAAI,CAAK;EACrC,IAAG,GACF,OAAO;EAGR,IAAM,IAAS,IAAI,EAAe,KAAK,MAAM,CAAK;EAIlD,OAHA,KAAK,QAAQ,IAAI,GAAO,CAAM,GAC9B,KAAK,UAAU,IAAI,EAAO,SAAS,CAAK,GAEjC;CACR;CAIA,UAAU,GAAqC;EAC9C,IAAG,MAAY,GACd,OAAO;EAGR,IAAI,IAAS,KAAK,UAAU,IAAI,CAAO;EACvC,IAAG,MAAW,KAAA,GACb,OAAO;EAGR,IAAM,IAAS,EAAW,CAAO;EACjC,IAAG,KAAK,KAAK,QAAQ,EAAO,oBAAoB,KAAA,GAC/C;EAGD,IAAM,IAAQ,IAAI,EAAe,KAAK,MAAM,CAAM,CAAC,CAAC;EAGpD,OAFA,KAAK,UAAU,IAAI,GAAS,CAAK,GAE1B;CACR;CAIA,QAAQ;EAGP,AAFA,KAAK,QAAQ,SAAQ,MAAU,EAAO,KAAK,CAAC,GAC5C,KAAK,QAAQ,MAAM,GACnB,KAAK,UAAU,MAAM;CACtB;AACD;;;AC3DA,SAAgB,EACf,GACA,GACqC;CACrC,IAAG,EAAM,QAAQ,QAAQ;EACxB,IAAM,IAAa,IAAI,IAAI,EAAM,OAAO;EACxC,IAAO,EAAK,QAAO,MAAS,CAAC,EAAW,IAAI,EAAM,QAAQ,CAAC;CAC5D;CAEA,IAAG,EAAM,MAAM,QAAQ;EAEtB,IAAM,oBAAa,IAAI,IAAoB;EAC3C,EAAK,SAAS,GAAO,MAAU,EAAW,IAAI,EAAM,UAAU,CAAK,CAAC;EAEpE,KAAI,IAAI,KAAU,EAAM,OAAO;GAC9B,IAAM,IAAW,EAAW,IAAI,EAAO,QAAQ;GAC/C,AAAG,MAAa,KAAA,KAGf,EAAW,IAAI,EAAO,UAAU,EAAK,MAAM,GAC3C,EAAK,KAAK,CAAM,KAHhB,EAAK,KAAY;EAKnB;CACD;CAEA,OAAO;AACR;;;ACbA,SAAgB,EACf,GACA,GACA,GACA,GACA,GACsB;CACtB,IAAM,IAAW,EAAe,EAAO,SAAS,CAAC;CACjD,IAAG,KAAW,OAAO,UAAU,eAAe,KAAK,GAAQ,OAAO,GACjE,MAAU,MAAM,qFAAqF;CAEtG,IAAM,IAAkC;EAAE,GAAG;EAAU,GAAG;CAAO,GAC3D,IAAc,OAAO,EAAS,SAAU,WAAW,EAAS,QAAQ,KAAA,GACtE,IAAiB,OAAO,KAAK,CAAQ;CACzC,IAAG,GAAS;EACX,IAAG,EAAS,SAAS,EAAO,MAC3B,MAAU,MAAM,mBAAmB,EAAO,KAAK,4BAA4B;EAE5E,IAAM,IAAa,IAAc,EAAQ,KAAe,KAAA;EACxD,IAAG,CAAC,GACH,MAAU,MAAM,eAAe,EAAO,KAAK,qBAAqB,KAAe,IAAI;EAEpF,IAAiB,EAAW;CAC7B;CAEA,IAAM,IAAM,EAAU,YAAY,GAC5B,IAAyC,CAAC;CAChD,KAAI,IAAI,KAAQ,GAAgB;EAG/B,IAAG,MAAS,UACX;EAED,IAAM,IAAa,EAAS;EAC5B,IAAG,CAAC,GACH,MAAU,MAAM,gBAAgB,KAAe,GAAG,8BAA8B,GAAM;EAGpF,EAAW,uBAGX,EAAW,eAAe,MAAK,MAAQ,KAAQ,CAAM,MACvD,EAAW,KAAQ,EAAU,uBAAuB,GAAM,EAAW,QAAQ,CAAM,CAAC;CAEtF;CAEA,OAAO;EACN;EACA,MAAM,EAAO;EACb,OAAO;EACP,UAAU,EAAO;EACjB;CACD;AACD;AAEA,SAAgB,EACf,GACA,GACA,GACA,GACyB;CACzB,IAAG,MAAS,UACX,MAAU,MAAM,iDAAiD;CAElE,IAAM,IAAa,EAAS;CAC5B,IAAG,CAAC,GACH,MAAU,MAAM,sBAAsB,GAAM;CAE7C,IAAG,EAAW,qBACb,MAAU,MAAM,aAAa,EAAK,oEAAoE;CAGvG,OAAO;EACN;EACA,OAAO,EAAU,uBAAuB,GAAM,EAAW,QAAQ,CAAM,CAAC;CACzE;AACD;;;ACrFA,IAA8B,IAA9B,MAAsH;CACrH;CACA;CAEA,YAAY,GAAU,GAAe;EAEpC,AADA,KAAK,QAAQ,GACb,KAAK,QAAQ;CACd;AACD,GCSa,IAAa,GACb,IAAe,GACf,IAAa,GACb,IAAc,GAGrB,IAAN,cAAkC,EAAkD;CACnF;CAEA,YAAY,GAAoB,GAAe,GAA4B;EAE1E,AADA,MAAM,GAAO,CAAK,GAClB,KAAK,QAAQ;CACd;CAEA,IAAI,OAAO;EACV,OAAO,KAAK,MAAM,UAAU,KAAK,MAAA,EAAiB,KAAK;CACxD;CACA,IAAI,QAAQ;EACX,OAAO,KAAK,MAAM,UAAU,KAAK,MAAA,EAAkB,KAAK;CACzD;CACA,IAAI,OAAO;EACV,OAAO,KAAK,MAAA,OAAsB;CACnC;CACA,IAAI,KAAK,GAAgB;EACxB,KAAK,MAAA,KAAoB;CAC1B;CACA,IAAI,WAAW;EACd,OAAO,KAAK,MAAA,OAAwB;CACrC;CACA,IAAI,SAAS,GAAgB;EAC5B,KAAK,MAAA,KAAsB;CAC5B;AACD,GAEa,IAAwJ;CACpK,MAAM;CACN,MAAM;CACN,gBAAgB,CAAC,QAAQ,UAAU;CAGnC,QAAQ,GAAQ,GAAQ;EACvB,IAAM,IAAQ,EAAQ,MAAM,iBACtB,IAAc,EAAO,OAAO,EAAM,YAAY,EAAO,IAAI,CAAC,CAAC,UAAU,GACrE,IAAe,EAAO,QAAQ,EAAM,YAAY,EAAO,KAAK,CAAC,CAAC,UAAU;EAC9E,OAAO;GAAC,KAAO;GAAc,KAAO;GAAkB;GAAa;EAAY;CAChF;CACA,OAAO,GAAQ,GAAQ,GAAO;EAC7B,OAAO,IAAI,EAAoB,EAAO,SAAS,CAAK,GAAG,GAAO,EAAO,MAAM,eAAe;CAC3F;CACA,KAAK,GAAW;EACf,IAAM,IAAuC,CAAC;EAQ9C,OAPG,EAAU,SACZ,EAAO,OAAO,EAAU,OAEtB,EAAU,SACZ,EAAO,OAAO,KAGR;CACR;AACD;;;AChFA,SAAwB,EAAiB,GAAkB,GAAoC,GAA8C;CAC5I,IAAM,IAAS,EAAgD;CAK/D,AAJG,MACF,EAAA,KAAoB,IAGrB,EAAU,WAAW,CAAQ;AAC9B;;;ACHA,SAAwB,EAAmB,GAAgC,GAAkC,GAA8C;CAC1J,IAAG,CAAC,EAAM,uBACT,MAAU,MAAM,kJAAkJ;CAGnK,EAAU,aAAa,EAAM,sBAAsB,CAAM,CAAC;AAC3D;;;ACXA,SAAwB,EACvB,GACA,GACA,GACA,GACA,GACO;CACP,IAAG,CAAC,EAAM,0BACT,MAAU,MAAM,gHAAgH;CAGjI,EAAU,aAAa,GAAU,EAAM,yBAAyB,OAAO,CAAa,GAAG,CAAM,CAAC;AAC/F;;;ACZA,SAAwB,EACvB,GACA,GACA,GACO;CACP,EAAU,gBAAgB,GAAU,CAAa;AAClD;;;ACWA,SAAwB,EAKtB,GAAgC,GAAkD,GAAsC;CAEzH,IAAI,IAA+C,CAAC,GAC9C,IAAuE,CAAC,GAE1E,GACA,GACA,GAGA,IAA6C,CAAC,GAC9C,GACA,GACA,GACA,IAAiB,IACf,KAAa,MAA4B,GAAa,UAAU,CAAO,KAAK;CAElF,EAAM,YAAY,SAAS,GAAG;EAC7B,IAAM,IAAU,EAAE;EAElB,IAAG,EAAQ,SAAS,QACnB,EAAiB,GAAO,EACvB,MAAM,gBACP,CAAC;OACK,IAAG,EAAQ,SAAS,QAAQ;GAQlC,IAPG,EAAQ,SACV,IAAO,IAAI,EAAW,EAAQ,IAAI,GAClC,IAAc,IAAI,EAAoB,CAAI,GAG1C,EAAK,yBAAwB,MAAU,EAAiB,GAAO;IAAE,MAAM;IAA2B;GAAO,CAAC,CAAC,IAEzG,KAAQ,EAAQ,cAAc;IAChC,IAAQ,CAAC;IACT,KAAI,IAAM,KAAQ,OAAO,KAAK,EAAQ,aAAa,UAAU,GAC5D,EAAM,KAAQ,EAAgB,iBAAiB,GAAM,EAAQ,aAAa,WAAW,EAAK;IAE3F,IAAa,EAAK,eAAe,EAAQ,aAAa,UAAU;GACjE;GAKA,AAJA,IAAiB,EAAQ,gBACzB,IAAiB,EAAQ,gBACzB,IAAiB,CAAC,CAAC,EAAQ,gBAC3B,IAAiB,EAAe,OAAO,EAAQ,IAAI,KAAK,KAAA,GACxD,EAAiB,GAAO,EACvB,MAAM,SACP,CAAC;EACF,OAAO,IAAG,EAAQ,SAAS,eAIvB,KAAQ,EAAK,QAAQ,EAAQ,OAAO,oBAAoB,KAAA,KAC1D,EAAK,gBAAgB,EAAQ,MAAM;OAE9B,IAAG,EAAQ,SAAS,SAAS;GAEnC,IAAW,CAAC;GACZ,KAAI,IAAM,KAAO,OAAO,KAAK,CAAa,GACzC,OAAO,EAAc;GAEtB,IAAiB,KAAA;EAClB,OAAO,IAAG,EAAQ,SAAS,OAAO;GAKjC,AAJG,KACF,OAAO,OAAO,EAAQ,OAAO,CAAc,GAE5C,EAAQ,MAAM,YAAY,GAC1B,EAAQ,MAAM,OAAO;GACrB,IAAM,IAAY;IACjB,mBAAmB,IAAa,QAAQ,IAAI,EAAW,MAAM,GAAG,CAAC,IAAI,IAAI;IACzE,yBAAyB,GAAc,MAA0B,EAAM,EAAK,CAAC,OAAO,CAAM;GAC3F;GAOA,AANA,EAAQ,MAAM,WAAW,GAGtB,KAAkB,MACpB,EAAQ,MAAM,yBAAwB,MAAU,EAAkB,GAAQ,GAAiB,GAAa,GAAW,CAAc,IAE/H,KAAkB,MACpB,EAAQ,MAAM,4BAA4B,GAAM,MAAW,EAAqB,GAAM,GAAQ,GAAa,CAAS;GAErH,IAAM,IAAQ,YAAY,IAAI,GAC1B,IAAmC,CAAC,GACpC,IAA6B,CAAC,GAC9B,IAA8C,CAAC,GAC/C,IAAiD,CAAC,GAClD,IAAgC,CAAC;GAErC,IAAW,EAAgB,GAAU,EAAQ,QAAyB;GAEtE,IAAI,IAAoC,CAAC;GACzC,OAAO,QAAQ,EAAQ,OAAO,CAAC,CAAC,SAAS,CAAC,GAAU,OAAW;IAC9D,IAAM,IAAO,EAAgB,EAAc,MAAa,CAAC,GAAG,CAAsB;IAElF,AADA,EAAc,KAAY,GAC1B,EAAQ,KAAY;GACrB,CAAC;GAED,IAAI,IAA4C;IAC/C,uBAAgE,GAAkB,GAAkB,GAAS,GAAgB;KAC5H,EAAa,KAAK;MACjB;MACA,OAAO;MACP,MAAM;OAAC;OAAe;OAAM;MAAK;KAClC,CAAC;IACF;IACA,gBAAgB,GAAkB,GAAe,GAAG,GAAsB;KACzE,EAAa,KAAK;MACjB;MACA;MACA;KACD,CAAC;IACF;IACA,gBAAgB,GAAe,GAAkB;KAChD,CAAC,EAAa,OAAW,EAAa,KAAS,CAAC,GAAA,CAAI,KAAK,CAAQ;IAClE;IACA,WAAW,GAAkB;KAC5B,EAAa,KAAK;MACjB;MACA,OAAO;MACP,MAAM,CAAC;KACR,CAAC;IACF;IACA,aAAa,GAA6B;KACzC,EAAgB,KAAK,CAAM;IAC5B;IACA,aAAa,GAAU,GAAW;KACjC,EAAiB,KAAK;MAAE,MAAM;MAAO;MAAU;KAAU,CAAC;IAC3D;IACA,gBAAgB,GAAU,GAAe;KACxC,EAAiB,KAAK;MAAE,MAAM;MAAU;MAAU,eAAe,OAAO,CAAa;KAAE,CAAC;IACzF;GACD,GACI,IAAe;GACnB,IAAG,EAAe,QACjB,IAAI;IACH,EAAe,OAAO,EAAQ,OAAO,GAAU,GAAS,CAAS;GAClE,SAAQ,GAAK;IAEZ,AADA,IAAe,IACf,EAAO,KAAK;KAAE,OAAO;KAAc,OAAO;IAAS,CAAC;GACrD;GAeD,AAXI,KACH,EAAS,SAAQ,MAAU;IAC1B,IAAI;KACH,EAAe,EAAQ,OAAO,EAAO,UAAU,EAAO,YAAY,GAAS,CAAS;IACrF,SAAQ,GAAK;KAEZ,EAAO,KAAK;MAAE,OAAO;MAAc,OAAO;MAAU,UAAU,EAAO;KAAS,CAAC;IAChF;GACD,CAAC,GAGC,EAAe,iBACjB,EAAQ,SAAS,QAAQ,SAAQ,MAAY;IAC5C,IAAI;KACH,EAAe,cAAe,EAAQ,OAAO,GAAU,CAAS;IACjE,SAAQ,GAAK;KACZ,EAAO,KAAK;MAAE,OAAO;MAAc,OAAO;MAAiB;KAAS,CAAC;IACtE;GACD,CAAC;GAEF,IAAM,IAAU,YAAY,IAAI,IAAI;GAEpC,EAAiB,GAAO;IACvB,MAAM;IACN,YAAY,EAAQ;IACpB;IACA,QAAQ;IACR;IACA,SAAS;IACT;IACA;GACD,CAAC;EACF;CACD;AACD;AAEA,SAAS,EAAiB,GAAgC,GAAoC;CAC7F,EAAM,YAAY,CAAO;AAC1B;;;AC1LA,SAAgB,EAId,GAAiG;CAClG,IAAM,WAAwB,CAAC;CAS/B,OARA,EAAe,UAAU,GAAO,GAAW,GAAS,MAAc,EAAI,GAAO,GAAS,CAAS,GAC5F,EAAI,SACN,EAAe,OAAO,EAAI,OAExB,EAAI,kBACN,EAAe,gBAAgB,EAAI,gBAG7B;AACR;AAIA,SAAwB,EAItB,GAAgC,GAA+C,GAAsC;CACtH,EAAyB,GAAO,EAAuB,CAAW,GAAG,CAAW;AACjF"}