@daneren2005/shared-memory-ecs 1.2.2 → 1.4.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
@@ -10,10 +10,11 @@ fog of war, sub-classed entities, required components, etc).
10
10
  - **Component** – a plain object with an `index` (its block inside a shared-memory pool) plus getters/
11
11
  setters over that memory. Games decide what components exist.
12
12
  - **`ComponentDefinition`** – describes a component: its typed array `type`, block `size`, the config keys
13
- that trigger loading (`loadProperties`), a `load(entity, memory, config)` and an optional `save(component)`.
14
- A component's data splits into `Config` (defining props supplied up front, e.g. `maxHealth`) and
15
- `Serialization` (runtime-derived state, e.g. current `health`); `load` sees `Config & Serialization` while
16
- `save` returns only the `Serialization` slice.
13
+ that trigger loading (`loadProperties`), a `load(entity, memory, config)`, an optional `save(component)`,
14
+ and an optional `free(component)`. A component's data splits into `Config` (defining props supplied up
15
+ front, e.g. `maxHealth`) and `Serialization` (runtime-derived state, e.g. current `health`); `load` sees
16
+ `Config & Serialization` while `save` returns only the `Serialization` slice. `free` runs when the
17
+ component is torn down (see [Freeing extra resources](#freeing-extra-resources)).
17
18
  - **`ComponentRegistry<C>`** – the map of all component definitions for a game.
18
19
  - **`EntityFactory<C>`** – maps an entity `type` name to a base (template) config. Loading an entity layers
19
20
  the caller's config over its type's template, so shared static data lives in one place and a save only
@@ -190,6 +191,67 @@ class DamageSystem extends ComponentSystem<Components, { health: Int32Array }, D
190
191
  }
191
192
  ```
192
193
 
194
+ ### Update-function hooks: `init` and `preRun`
195
+
196
+ `addDataToWorld` runs on the main thread and re-sends its data every run. When the data instead needs to
197
+ *live in the worker* — computed once, or too big to ship each frame — attach hooks to the update function
198
+ itself. Both are optional properties on the `EntityUpdateFunction`.
199
+
200
+ **`init`** runs on `system.finishLoading()` — once at startup (`world.init()` calls it), and again on each
201
+ `world.load()` so a reused world re-seeds its workers. It receives whatever the system's `getInitData()` returned
202
+ (structured-cloned across the boundary) and returns a `Partial<W>` that is merged onto `world` on every
203
+ subsequent run. Use it for state that must be seeded from the main thread but then persist inside the worker
204
+ — a seeded RNG, a lookup table, a config object — without paying to re-send it each frame. Type the init
205
+ data with the `D` type parameter (the fourth on `EntityUpdateFunction` / `ComponentSystem`) so `getInitData`
206
+ and the `init` hook agree on its shape; it defaults to `unknown` when a system has no init data:
207
+
208
+ ```ts
209
+ interface DamageWorld extends ComponentSystemWorld {
210
+ damage: number
211
+ }
212
+ interface DamageInitData {
213
+ baseDamage: number
214
+ }
215
+
216
+ const damageUpdate: EntityUpdateFunction<Components, { health: Int32Array }, DamageWorld, DamageInitData> =
217
+ (world, entityId, components) => { components.health[0] -= world.damage; };
218
+
219
+ // Runs once in the worker; its return is merged onto `world` before every run. `data` is typed as
220
+ // `DamageInitData | undefined` (undefined when the system supplies no getInitData).
221
+ damageUpdate.init = (data) => ({ damage: data?.baseDamage ?? 1 });
222
+
223
+ class DamageSystem extends ComponentSystem<Components, { health: Int32Array }, DamageWorld, DamageInitData> {
224
+ constructor(world: BaseWorld<Components>) {
225
+ super(world, {
226
+ name: 'DamageSystem',
227
+ required: ['health'],
228
+ updateFunction: damageUpdate,
229
+ getWorker: () => new Worker(/* your worker entry */),
230
+ // Runs on the main thread; its result is structured-cloned to the worker's `init`.
231
+ getInitData: () => ({ baseDamage: 5 }),
232
+ });
233
+ }
234
+ }
235
+ ```
236
+
237
+ Unlike `addDataToWorld` (an overridable method on the system), `getInitData` is a config option — it lives in
238
+ the options passed to the `ComponentSystem` constructor, so a system used without a subclass can supply it
239
+ there directly.
240
+
241
+ **`preRun`** runs once per run, before any entity is updated, with the run's `world`, the full entity list,
242
+ the query results, and the same `callbacks`. Use it for setup that spans the whole batch — seeding a
243
+ spatial index, resetting an accumulator, or emitting a run-level event — that would be wasteful or wrong to
244
+ repeat inside the per-entity loop:
245
+
246
+ ```ts
247
+ damageUpdate.preRun = (world, entities, queries, callbacks) => {
248
+ // Runs before the per-entity pass; `entities` is everything this run will touch.
249
+ };
250
+ ```
251
+
252
+ (An `entityRemoved(world, entityId, callbacks)` hook completes the set — it fires once per entity that left
253
+ the system this run, so a worker can release any per-entity state it was holding.)
254
+
193
255
  ### Importing in workers
194
256
 
195
257
  Each worker entry file is bundled on its own, and a single-entry bundle cannot tree-shake this package's
@@ -273,6 +335,56 @@ thread already holds the values - sending them along would only pay to copy what
273
335
  System events are dispatched before the per-entity events of the same run, so an entity a run both moved and
274
336
  killed is still in the world when its move is reported.
275
337
 
338
+ ### Freeing component memory safely
339
+
340
+ Component blocks live in a shared pool, so a freed block gets handed straight back out to the next entity that
341
+ needs one. That is a problem across workers: if one system kills an entity, a second reuses its freed block for
342
+ a brand-new entity, and a third is still mid-run over what it thinks is the old entity, the third system writes
343
+ into the new entity's memory.
344
+
345
+ So the library never frees a block the instant it is orphaned. When an entity dies or you call
346
+ `removeComponent`, the block is *deferred* — the component is gone from the entity immediately, but the memory is
347
+ held until every system that could be mid-run over it has finished a run. Only then is the block returned to the
348
+ pool for reuse. This is automatic; there is nothing to call. The one visible effect is that
349
+ `memoryComponent.length` can briefly sit one higher than the number of live components, until the next update
350
+ lets the holding systems finish. If a system stays stuck (never completes a run) for more than ten seconds of
351
+ unscaled time, the world logs a warning naming that system and frees the blocks anyway rather than leak them —
352
+ a stuck system there is a bug worth chasing down.
353
+
354
+ ### Freeing extra resources
355
+
356
+ Everything above frees a component's own block. A component that allocates something *else* in `load` —
357
+ another heap structure (a `SharedList`, a `SharedString`) or child entities it owns — needs to release that
358
+ too, and the block-level deferred free won't do it. Give the definition an optional `free(component)`:
359
+
360
+ ```ts
361
+ const cargoDefinition: ComponentDefinition<Cargo, Uint32Array, CargoConfig> = {
362
+ type: Uint32Array,
363
+ size: 3,
364
+ loadProperties: ['cargoSpace'],
365
+ load(entity, memory, config) {
366
+ /* allocate a SharedList in the heap, stash its pointer in the block, return accessors */
367
+ },
368
+ free(component) {
369
+ component.items.free(); // release the SharedList + the item entities it holds
370
+ },
371
+ };
372
+ ```
373
+
374
+ `free` runs exactly once per component teardown: on `removeComponent`, and on every path that removes an
375
+ entity — `removeEntity`, a `death` (from `killEntity`), a `load` that drops the old batch, and `clear`.
376
+ It is deferred to the same safe point as the component's own block (see above), not fired the instant the
377
+ entity dies — so a system still mid-run over that memory can't see your resource released early. Reload calls
378
+ it without any `death` event, so it is the reliable place to avoid leaks when a world is reused.
379
+
380
+ ### Reusing a world: `load` and `clear`
381
+
382
+ A world is meant to be reused rather than rebuilt. `world.load(config)` swaps in a fresh scenario — it removes
383
+ the old entities, clears every system, and loads the new batch (deferred frees from the old contents drain over
384
+ the following updates, as above). `await world.clear()` instead tears the world all the way back down to an
385
+ empty, reusable state: it waits for every system's in-flight worker run to finish so nothing is still reading
386
+ the memory, frees the held blocks immediately, and resolves once the world is ready to load into again.
387
+
276
388
  ## Measuring performance
277
389
 
278
390
  `PerformanceTiming` watches a world and reports what running it costs. Hand it the world and it hooks itself
@@ -14,6 +14,7 @@ export interface ComponentDefinition<Component extends BaseComponent, T extends
14
14
  loadInFinishLoading?: boolean;
15
15
  load(entity: BaseEntity, memory: MemoryComponent<T>, config: Config & Serialization): Component;
16
16
  save?(component: Component): Serialization;
17
+ free?(component: Component): void;
17
18
  }
18
19
  export type RegisteredComponentDefinition<Component extends BaseComponent, T extends ComponentTypedArray = ComponentTypedArray, Config = any, Serialization = object> = ComponentDefinition<Component, T, Config, Serialization> & {
19
20
  memoryComponent: MemoryComponent<T>;
@@ -58,21 +58,27 @@ function a(e, t) {
58
58
  //#endregion
59
59
  //#region src/systems/workers/create-component-worker.ts
60
60
  function o(t, n) {
61
- let r = [], i = {};
62
- t.onmessage = function(a) {
63
- let o = a.data;
64
- if (o.type === "init") s(t, { type: "loaded" });
65
- else if (o.type === "run") {
66
- let a = performance.now(), c = [], l = {}, u = [];
67
- r = e(r, o.entities);
68
- let d = {};
69
- Object.entries(o.queries).forEach(([t, n]) => {
61
+ let r = [], i = {}, a;
62
+ t.onmessage = function(o) {
63
+ let c = o.data;
64
+ if (c.type === "init") s(t, { type: "init-complete" });
65
+ else if (c.type === "load") a = n.init?.(c.data) ?? void 0, s(t, { type: "loaded" });
66
+ else if (c.type === "reset") {
67
+ r = [];
68
+ for (let e of Object.keys(i)) delete i[e];
69
+ a = void 0;
70
+ } else if (c.type === "run") {
71
+ a && Object.assign(c.world, a);
72
+ let o = performance.now(), l = [], u = {}, d = [];
73
+ r = e(r, c.entities);
74
+ let f = {};
75
+ Object.entries(c.queries).forEach(([t, n]) => {
70
76
  let r = e(i[t] ?? [], n);
71
- i[t] = r, d[t] = r;
77
+ i[t] = r, f[t] = r;
72
78
  });
73
- let f = {
79
+ let p = {
74
80
  entityComponentChanged(e, t, n, r) {
75
- c.push({
81
+ l.push({
76
82
  entityId: e,
77
83
  event: "component-property-updated",
78
84
  args: [
@@ -83,36 +89,39 @@ function o(t, n) {
83
89
  });
84
90
  },
85
91
  emitEntityEvent(e, t, ...n) {
86
- c.push({
92
+ l.push({
87
93
  entityId: e,
88
94
  event: t,
89
95
  args: n
90
96
  });
91
97
  },
92
98
  emitSystemEvent(e, t) {
93
- (l[e] ?? (l[e] = [])).push(t);
99
+ (u[e] ?? (u[e] = [])).push(t);
94
100
  },
95
101
  entityDied(e) {
96
- c.push({
102
+ l.push({
97
103
  entityId: e,
98
104
  event: "death",
99
105
  args: []
100
106
  });
101
107
  },
102
108
  createEntity(e) {
103
- u.push(e);
109
+ d.push(e);
104
110
  }
105
111
  };
106
- n.preRun && n.preRun(o.world, r, d, f), r.forEach((e) => {
107
- n(o.world, e.entityId, e.components, d, f);
108
- }), n.entityRemoved && o.entities.removed.forEach((e) => {
109
- n.entityRemoved(o.world, e, f);
110
- }), s(t, {
112
+ n.preRun && n.preRun(c.world, r, f, p), r.forEach((e) => {
113
+ n(c.world, e.entityId, e.components, f, p);
114
+ }), n.entityRemoved && c.entities.removed.forEach((e) => {
115
+ n.entityRemoved(c.world, e, p);
116
+ });
117
+ let m = performance.now() - o;
118
+ s(t, {
111
119
  type: "run-complete",
112
- runTime: performance.now() - a,
113
- events: c,
114
- systemEvents: l,
115
- created: u
120
+ generation: c.generation,
121
+ runTime: m,
122
+ events: l,
123
+ systemEvents: u,
124
+ created: d
116
125
  });
117
126
  }
118
127
  };
@@ -123,4 +132,4 @@ function s(e, t) {
123
132
  //#endregion
124
133
  export { n as a, t as i, a as n, r as o, i as r, e as s, o as t };
125
134
 
126
- //# sourceMappingURL=create-component-worker-Bd1BTUkq.js.map
135
+ //# sourceMappingURL=create-component-worker-vbX3vEia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-component-worker-vbX3vEia.js","names":[],"sources":["../src/systems/workers/apply-query-delta.ts","../src/entity-component.ts","../src/actions/kill-entity-worker.ts","../src/actions/create-entity-worker.ts","../src/systems/workers/create-component-worker.ts"],"sourcesContent":["import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../component-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 { ComponentDefinition } from './component-definition';\n\n// dead/isStatic live in the shared block (worker-visible); type is main-thread only.\nexport interface EntityComponent {\n\tindex: number\n\ttype: string\n\tdead: boolean\n\tisStatic: boolean\n}\n\nexport interface EntityComponentConfig {\n\ttype: string\n\tisStatic?: boolean\n}\n\nexport interface EntityComponentSerialization {\n\ttype?: string\n\tdead?: boolean\n}\n\nexport const DEAD_INDEX = 0;\nexport const STATIC_INDEX = 1;\n\nexport const entityDefinition: ComponentDefinition<EntityComponent, Uint32Array, EntityComponentConfig, EntityComponentSerialization> = {\n\ttype: Uint32Array,\n\tsize: 2,\n\tloadProperties: ['type', 'isStatic'],\n\tload(entity, memory, config) {\n\t\tconst index = memory.create([config.dead ? 1 : 0, config.isStatic ? 1 : 0]);\n\t\tconst block = memory.getBlock(index);\n\n\t\treturn {\n\t\t\tindex,\n\t\t\ttype: config.type,\n\t\t\tget dead() {\n\t\t\t\treturn block[DEAD_INDEX] === 1;\n\t\t\t},\n\t\t\tset dead(value: boolean) {\n\t\t\t\tblock[DEAD_INDEX] = value ? 1 : 0;\n\t\t\t},\n\t\t\tget isStatic() {\n\t\t\t\treturn block[STATIC_INDEX] === 1;\n\t\t\t},\n\t\t\tset isStatic(value: boolean) {\n\t\t\t\tblock[STATIC_INDEX] = value ? 1 : 0;\n\t\t\t},\n\t\t};\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 { ComponentSystemCallbacks, EntityUpdateComponents } from '../systems/component-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: ComponentSystemCallbacks): 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 { ComponentSystemCallbacks, CreateEntityConfig } from '../systems/component-system';\n\n// Worker-side loadEntity: buffers the config through callbacks. The main thread builds it via world.loadEntity\n// once the run completes, so it first exists on the following frame.\nexport default function createEntityWorker(config: CreateEntityConfig, callbacks: ComponentSystemCallbacks): void {\n\tcallbacks.createEntity(config);\n}\n","import type ComponentWorkerMessage from './component-worker-message';\nimport type { EntityEvent, SystemEvents } from './component-worker-message';\nimport type { ComponentMap } from '../../component-definition';\nimport type { ComponentSystemCallbacks, ComponentSystemWorld, EntityQueryComponents, EntityUpdateComponents, EntityUpdateFunction, QueryDelta, UpdateEntityConfigObject } from '../component-system';\nimport { applyQueryDelta } from './apply-query-delta';\n\n// The slice of the worker global scope createComponentWorker 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 ComponentWorkerScope {\n\tonmessage: ((e: MessageEvent) => void) | null\n\tpostMessage(message: ComponentWorkerMessage): void\n}\n\nexport default function createComponentWorker<\n\tC extends ComponentMap,\n\tT extends EntityUpdateComponents<C>,\n\tW extends ComponentSystemWorld = ComponentSystemWorld,\n\tD = unknown,\n>(scope: ComponentWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>) {\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\n\tscope.onmessage = function(e) {\n\t\tconst message = e.data as ComponentWorkerMessage<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\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 === '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\tconst start = performance.now();\n\t\t\tlet entityEvents: Array<EntityEvent> = [];\n\t\t\tlet systemEvents: SystemEvents = {};\n\t\t\tlet createdEntities: Array<Record<string, unknown>> = [];\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: ComponentSystemCallbacks<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(config: Record<string, unknown>) {\n\t\t\t\t\tcreatedEntities.push(config);\n\t\t\t\t},\n\t\t\t};\n\t\t\tif(updateFunction.preRun) {\n\t\t\t\tupdateFunction.preRun(message.world, entities, queries, callbacks);\n\t\t\t}\n\n\t\t\tentities.forEach(entity => {\n\t\t\t\tupdateFunction(message.world, entity.entityId, entity.components, queries, callbacks);\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\tupdateFunction.entityRemoved!(message.world, entityId, callbacks);\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});\n\t\t}\n\t};\n}\n\nfunction postMessageTyped(scope: ComponentWorkerScope, message: ComponentWorkerMessage) {\n\tscope.postMessage(message);\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ export type { IterableSystemConfig } from './systems/iterable-system';
19
19
  export { default as EntitySystem } from './systems/entity-system';
20
20
  export type { EntitySystemConfig } from './systems/entity-system';
21
21
  export { default as ComponentSystem } from './systems/component-system';
22
- export type { ComponentSystemConfig, ComponentSystemQuery, ComponentSystemWorld, ComponentSystemCallbacks, CreateEntityConfig, EntityUpdateComponents, EntityQueryComponents, EntityUpdateFunction, EntityUpdatePreRunFunction, EntityRemovedFunction, UpdateEntityConfig, UpdateEntityConfigObject, } from './systems/component-system';
22
+ export type { ComponentSystemConfig, ComponentSystemQuery, ComponentSystemWorld, ComponentSystemCallbacks, CreateEntityConfig, EntityUpdateComponents, EntityQueryComponents, EntityUpdateFunction, EntityUpdateInitFunction, EntityUpdatePreRunFunction, EntityRemovedFunction, UpdateEntityConfig, UpdateEntityConfigObject, } from './systems/component-system';
23
23
  export { default as WebWorker } from './systems/workers/web-worker';
24
24
  export { default as ComponentWebWorker } from './systems/workers/component-web-worker';
25
25
  export { default as createComponentWorker } from './systems/workers/create-component-worker';