@vworlds/vecs 1.0.9 → 1.0.11

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 (46) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/README.md +218 -229
  3. package/dist/command.d.ts +1 -0
  4. package/dist/command.js +2 -0
  5. package/dist/command.js.map +1 -0
  6. package/dist/component.d.ts +51 -59
  7. package/dist/component.js +31 -25
  8. package/dist/component.js.map +1 -1
  9. package/dist/dsl.d.ts +34 -26
  10. package/dist/dsl.js +46 -20
  11. package/dist/dsl.js.map +1 -1
  12. package/dist/entity.d.ts +110 -127
  13. package/dist/entity.js +323 -164
  14. package/dist/entity.js.map +1 -1
  15. package/dist/filter.d.ts +31 -23
  16. package/dist/filter.js +41 -32
  17. package/dist/filter.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/package.json +3 -1
  20. package/dist/phase.d.ts +5 -28
  21. package/dist/phase.js +11 -10
  22. package/dist/phase.js.map +1 -1
  23. package/dist/query.d.ts +128 -94
  24. package/dist/query.js +254 -145
  25. package/dist/query.js.map +1 -1
  26. package/dist/system.d.ts +64 -128
  27. package/dist/system.js +156 -149
  28. package/dist/system.js.map +1 -1
  29. package/dist/util/array_map.d.ts +4 -55
  30. package/dist/util/array_map.js +35 -37
  31. package/dist/util/array_map.js.map +1 -1
  32. package/dist/util/bitset.d.ts +40 -50
  33. package/dist/util/bitset.js +76 -62
  34. package/dist/util/bitset.js.map +1 -1
  35. package/dist/util/events.d.ts +14 -18
  36. package/dist/util/events.js +24 -3
  37. package/dist/util/events.js.map +1 -1
  38. package/dist/util/ordered_set.d.ts +1 -17
  39. package/dist/util/ordered_set.js +74 -25
  40. package/dist/util/ordered_set.js.map +1 -1
  41. package/dist/world.d.ts +222 -201
  42. package/dist/world.js +394 -323
  43. package/dist/world.js.map +1 -1
  44. package/eslint-rules/internal-underscore.js +60 -0
  45. package/eslint.config.js +5 -0
  46. package/package.json +3 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":""}
@@ -1,63 +1,68 @@
1
- import { BitPtr, Bitset } from "./util/bitset.js";
1
+ import { BitPtr } from "./util/bitset.js";
2
2
  import type { Entity } from "./entity.js";
3
- import { type World } from "./world.js";
4
3
  /**
5
- * Lifecycle hook for a component type. Obtained via {@link World.hook}.
4
+ * Lifecycle hook for a registered component class. Obtained via
5
+ * {@link World.hook}.
6
6
  *
7
- * Hooks let you react to component lifecycle events without building a full
8
- * {@link System}. Each call returns the same `Hook` so the methods can be
9
- * chained:
7
+ * Hooks are a lightweight alternative to building a {@link System} when all
8
+ * you need is a callback on add / remove / set for a single component type.
9
+ * The same `Hook` is returned on every call to `world.hook(C)`, so registration
10
+ * methods chain:
10
11
  *
11
12
  * ```ts
12
13
  * world.hook(Sprite)
13
- * .onAdd(c => initSprite(c))
14
+ * .onAdd(c => initSprite(c))
14
15
  * .onRemove(c => destroySprite(c))
15
- * .onSet(c => syncSprite(c));
16
+ * .onSet(c => syncSprite(c));
16
17
  * ```
17
18
  *
18
- * Callbacks are invoked synchronously during {@link World.runPhase} when
19
- * archetype changes are flushed.
19
+ * Callbacks fire synchronously when the corresponding entity command is
20
+ * applied: inline outside deferred mode, or while the world drains its command
21
+ * queue inside a system / `forEach` / `defer` block.
20
22
  *
21
- * @typeParam C - The `Component` subclass this hook is bound to.
23
+ * @typeParam C - Component subclass this hook is bound to.
22
24
  */
23
25
  export interface Hook<C extends Component = Component> {
24
26
  /**
25
- * Register a callback that fires when a component of this type is added to
26
- * an entity.
27
+ * Register a handler invoked when a component of this type is first attached
28
+ * to an entity (`entity.add(C)` or `entity.set(C, ...)` on an entity that
29
+ * does not yet have the component).
27
30
  *
28
- * @param handler - Receives the newly created component instance.
29
- * @returns `this` for chaining.
31
+ * @param handler - Receives the freshly created component instance.
32
+ * @returns This hook, for chaining.
30
33
  */
31
34
  onAdd(handler: (c: C) => void): Hook<C>;
32
35
  /**
33
- * Register a callback that fires when a component of this type is removed
34
- * from an entity (including when the entity is destroyed).
36
+ * Register a handler invoked when a component of this type is removed from
37
+ * an entity (explicit `entity.remove(C)` or implicit removal during
38
+ * `entity.destroy()`).
35
39
  *
36
- * @param handler - Receives the component instance being removed.
37
- * @returns `this` for chaining.
40
+ * @param handler - Receives the component instance that was removed.
41
+ * @returns This hook, for chaining.
38
42
  */
39
43
  onRemove(handler: (c: C) => void): Hook<C>;
40
44
  /**
41
- * Register a callback that fires when {@link Component.modified} is called
42
- * on a component of this type.
45
+ * Register a handler invoked when a component's data has been marked as
46
+ * changed (`component.modified()` or `entity.modified(c)`), and when
47
+ * `entity.set(C, props)` is called on an entity that already has the
48
+ * component.
43
49
  *
44
- * @param handler - Receives the component instance that changed.
45
- * @returns `this` for chaining.
50
+ * @param handler - Receives the component instance whose data changed.
51
+ * @returns This hook, for chaining.
46
52
  */
47
53
  onSet(handler: (c: C) => void): Hook<C>;
48
54
  }
49
55
  /**
50
- * Internal bookkeeping record for a registered component class.
56
+ * Bookkeeping record produced for each component class registered via
57
+ * {@link World.registerComponent}.
51
58
  *
52
- * Every component class that is passed to {@link World.registerComponent} gets
53
- * a `ComponentMeta` that maps it to a numeric type id, a string name, and a
54
- * pre-computed {@link BitPtr} used for fast archetype checks.
55
- *
56
- * `ComponentMeta` also implements {@link Hook}, so you can attach lifecycle
57
- * callbacks directly on the meta object (as `World.hook()` returns it).
59
+ * Holds the constructor, numeric type id, display name, and pre-computed
60
+ * {@link BitPtr} used by archetype checks. Implements {@link Hook}, so the
61
+ * lifecycle handlers attached via `world.hook(C)` are stored directly on the
62
+ * meta object.
58
63
  */
59
64
  export declare class ComponentMeta implements Hook<Component> {
60
- /** The component class constructor. */
65
+ /** The component class constructor this meta represents. */
61
66
  readonly Class: typeof Component;
62
67
  /** Numeric type id assigned at registration time. */
63
68
  readonly type: number;
@@ -65,15 +70,10 @@ export declare class ComponentMeta implements Hook<Component> {
65
70
  readonly componentName: string;
66
71
  /** Pre-computed bit-pointer into the entity archetype {@link Bitset}. */
67
72
  readonly bitPtr: BitPtr;
68
- /** @internal */
69
- _onAddHandler: ((c: Component) => void) | undefined;
70
- /** @internal */
71
- _onRemoveHandler: ((c: Component) => void) | undefined;
72
- /** @internal */
73
- _onSetHandler: ((c: Component) => void) | undefined;
74
73
  /**
75
- * Type ids of components that cannot coexist with this one on the same entity.
76
- * Set via {@link World.setExclusiveComponents}. `undefined` means no restrictions.
74
+ * Type ids of components that cannot coexist with this one on the same
75
+ * entity. Set via {@link World.setExclusiveComponents}; `undefined` means
76
+ * no restriction.
77
77
  */
78
78
  exclusive: number[] | undefined;
79
79
  constructor(Class: typeof Component, type: number, componentName: string);
@@ -86,12 +86,12 @@ export declare class ComponentMeta implements Hook<Component> {
86
86
  }
87
87
  /** A component class constructor or its numeric type id. */
88
88
  export type ComponentClassOrType = number | typeof Component;
89
- /** An array of component class constructors or type ids. */
90
- export type ComponentClassArray = ComponentClassOrType[];
91
89
  /**
92
90
  * Base class for all ECS components.
93
91
  *
94
- * Extend this class to define data that can be attached to an {@link Entity}:
92
+ * Subclass `Component` to declare data that can be attached to an
93
+ * {@link Entity}. Instances are constructed by the world when
94
+ * {@link Entity.add} or {@link Entity.set} runs — never instantiate manually.
95
95
  *
96
96
  * ```ts
97
97
  * class Position extends Component {
@@ -103,20 +103,18 @@ export type ComponentClassArray = ComponentClassOrType[];
103
103
  * entity.set(Position, { x: 100 });
104
104
  * ```
105
105
  *
106
- * A component instance is always bound to a single entity and is created by
107
- * the world when {@link Entity.add} is called.
106
+ * Each instance is bound to a single entity via {@link entity}; that link is
107
+ * permanent for the component's lifetime.
108
108
  */
109
109
  export declare class Component {
110
110
  /** The entity this component belongs to. */
111
111
  readonly entity: Entity;
112
- /** Registration metadata (type id, name, bit-pointer). */
112
+ /** Registration metadata (type id, display name, bit-pointer). */
113
113
  readonly meta: ComponentMeta;
114
- /** @internal */
115
- _dirty: boolean;
116
114
  constructor(
117
115
  /** The entity this component belongs to. */
118
116
  entity: Entity,
119
- /** Registration metadata (type id, name, bit-pointer). */
117
+ /** Registration metadata (type id, display name, bit-pointer). */
120
118
  meta: ComponentMeta);
121
119
  /** Numeric type id — shorthand for `this.meta.type`. */
122
120
  get type(): number;
@@ -125,18 +123,12 @@ export declare class Component {
125
123
  /**
126
124
  * Notify the world that this component's data has changed.
127
125
  *
128
- * Queues the component for delivery to all {@link System.update} callbacks
129
- * that watch this component type. Call this after mutating the component's
130
- * fields to ensure systems react to the new values.
126
+ * Queues a modified event that fires `update` callbacks on every system /
127
+ * query that watches this component type, plus the component's `onSet`
128
+ * hook. Repeated calls before the world drains its queue are coalesced
129
+ * into one delivery.
131
130
  */
132
131
  modified(): void;
133
- /** Returns the component's registered name, e.g. `"Position"`. */
132
+ /** Returns the component's registered display name (e.g. `"Position"`). */
134
133
  toString(): string;
135
134
  }
136
- /**
137
- * Compute a {@link Bitset} that has a bit set for every component class or
138
- * type id in `classes`.
139
- *
140
- * @internal Used internally to build archetype masks for system queries.
141
- */
142
- export declare function calculateComponentBitmask(classes: ComponentClassArray, world: World): Bitset;
package/dist/component.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import { BitPtr, Bitset } from "./util/bitset.js";
2
2
  /**
3
- * Internal bookkeeping record for a registered component class.
3
+ * Bookkeeping record produced for each component class registered via
4
+ * {@link World.registerComponent}.
4
5
  *
5
- * Every component class that is passed to {@link World.registerComponent} gets
6
- * a `ComponentMeta` that maps it to a numeric type id, a string name, and a
7
- * pre-computed {@link BitPtr} used for fast archetype checks.
8
- *
9
- * `ComponentMeta` also implements {@link Hook}, so you can attach lifecycle
10
- * callbacks directly on the meta object (as `World.hook()` returns it).
6
+ * Holds the constructor, numeric type id, display name, and pre-computed
7
+ * {@link BitPtr} used by archetype checks. Implements {@link Hook}, so the
8
+ * lifecycle handlers attached via `world.hook(C)` are stored directly on the
9
+ * meta object.
11
10
  */
12
11
  export class ComponentMeta {
13
12
  constructor(Class, type, componentName) {
14
13
  /**
15
- * Type ids of components that cannot coexist with this one on the same entity.
16
- * Set via {@link World.setExclusiveComponents}. `undefined` means no restrictions.
14
+ * Type ids of components that cannot coexist with this one on the same
15
+ * entity. Set via {@link World.setExclusiveComponents}; `undefined` means
16
+ * no restriction.
17
17
  */
18
18
  this.exclusive = undefined;
19
19
  this.Class = Class;
@@ -23,24 +23,26 @@ export class ComponentMeta {
23
23
  }
24
24
  /** @inheritdoc */
25
25
  onAdd(handler) {
26
- this._onAddHandler = handler;
26
+ (this._onAddHandlers ?? (this._onAddHandlers = [])).unshift(handler);
27
27
  return this;
28
28
  }
29
29
  /** @inheritdoc */
30
30
  onRemove(handler) {
31
- this._onRemoveHandler = handler;
31
+ (this._onRemoveHandlers ?? (this._onRemoveHandlers = [])).unshift(handler);
32
32
  return this;
33
33
  }
34
34
  /** @inheritdoc */
35
35
  onSet(handler) {
36
- this._onSetHandler = handler;
36
+ (this._onSetHandlers ?? (this._onSetHandlers = [])).unshift(handler);
37
37
  return this;
38
38
  }
39
39
  }
40
40
  /**
41
41
  * Base class for all ECS components.
42
42
  *
43
- * Extend this class to define data that can be attached to an {@link Entity}:
43
+ * Subclass `Component` to declare data that can be attached to an
44
+ * {@link Entity}. Instances are constructed by the world when
45
+ * {@link Entity.add} or {@link Entity.set} runs — never instantiate manually.
44
46
  *
45
47
  * ```ts
46
48
  * class Position extends Component {
@@ -52,18 +54,18 @@ export class ComponentMeta {
52
54
  * entity.set(Position, { x: 100 });
53
55
  * ```
54
56
  *
55
- * A component instance is always bound to a single entity and is created by
56
- * the world when {@link Entity.add} is called.
57
+ * Each instance is bound to a single entity via {@link entity}; that link is
58
+ * permanent for the component's lifetime.
57
59
  */
58
60
  export class Component {
59
61
  constructor(
60
62
  /** The entity this component belongs to. */
61
63
  entity,
62
- /** Registration metadata (type id, name, bit-pointer). */
64
+ /** Registration metadata (type id, display name, bit-pointer). */
63
65
  meta) {
64
66
  this.entity = entity;
65
67
  this.meta = meta;
66
- /** @internal */
68
+ /** @internal Set by {@link Entity.modified} to coalesce repeated calls until the world routes the modified command. */
67
69
  this._dirty = false;
68
70
  }
69
71
  /** Numeric type id — shorthand for `this.meta.type`. */
@@ -77,25 +79,29 @@ export class Component {
77
79
  /**
78
80
  * Notify the world that this component's data has changed.
79
81
  *
80
- * Queues the component for delivery to all {@link System.update} callbacks
81
- * that watch this component type. Call this after mutating the component's
82
- * fields to ensure systems react to the new values.
82
+ * Queues a modified event that fires `update` callbacks on every system /
83
+ * query that watches this component type, plus the component's `onSet`
84
+ * hook. Repeated calls before the world drains its queue are coalesced
85
+ * into one delivery.
83
86
  */
84
87
  modified() {
85
88
  this.entity.modified(this);
86
89
  }
87
- /** Returns the component's registered name, e.g. `"Position"`. */
90
+ /** Returns the component's registered display name (e.g. `"Position"`). */
88
91
  toString() {
89
92
  return this.meta.componentName;
90
93
  }
91
94
  }
92
95
  /**
93
- * Compute a {@link Bitset} that has a bit set for every component class or
94
- * type id in `classes`.
96
+ * Compute a {@link Bitset} with one bit set for every component class or
97
+ * numeric type id in `classes`.
98
+ *
99
+ * @internal Used to build archetype masks for `HAS` / `HAS_ONLY` queries.
95
100
  *
96
- * @internal Used internally to build archetype masks for system queries.
101
+ * @param classes - Component classes or type ids to include.
102
+ * @param world - World used to resolve classes to type ids.
97
103
  */
98
- export function calculateComponentBitmask(classes, world) {
104
+ export function _calculateComponentBitmask(classes, world) {
99
105
  const bitmask = new Bitset();
100
106
  classes.forEach((C) => {
101
107
  bitmask.add(world.getComponentType(C));
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoDlD;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAqBxB,YAAY,KAAuB,EAAE,IAAY,EAAE,aAAqB;QANxE;;;WAGG;QACI,cAAS,GAAyB,SAAS,CAAC;QAGjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAA+B;QAC7C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,SAAS;IAIpB;IACE,4CAA4C;IAC5B,MAAc;IAC9B,0DAA0D;IAC1C,IAAmB;QAFnB,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAe;QAPrC,gBAAgB;QACT,WAAM,GAAY,KAAK,CAAC;IAO5B,CAAC;IAEJ,wDAAwD;IACxD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,mEAAmE;IACnE,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACI,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,kEAAkE;IAC3D,QAAQ;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA4B,EAAE,KAAY;IAClF,MAAM,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2DlD;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IAwBxB,YAAY,KAAuB,EAAE,IAAY,EAAE,aAAqB;QAdxE;;;;WAIG;QACI,cAAS,GAAyB,SAAS,CAAC;QAUjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAA+B;QAC7C,CAAC,IAAI,CAAC,iBAAiB,KAAtB,IAAI,CAAC,iBAAiB,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,SAAS;IAIpB;IACE,4CAA4C;IAC5B,MAAc;IAC9B,kEAAkE;IAClD,IAAmB;QAFnB,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAe;QAPrC,uHAAuH;QAChH,WAAM,GAAY,KAAK,CAAC;IAO5B,CAAC;IAEJ,wDAAwD;IACxD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,mEAAmE;IACnE,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;;OAOG;IACI,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,2EAA2E;IACpE,QAAQ;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAA4B,EAAE,KAAY;IACnF,MAAM,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/dsl.d.ts CHANGED
@@ -1,30 +1,34 @@
1
1
  import { Component, ComponentClassArray, ComponentClassOrType } from "./component.js";
2
2
  import type { Entity } from "./entity.js";
3
- import type { World } from "./world.js";
4
- /** A function that tests whether a given entity belongs to a query. */
3
+ /**
4
+ * A predicate that decides whether a given entity belongs to a query.
5
+ *
6
+ * Pass one directly inside {@link QueryDSL} to express membership rules that
7
+ * the structured operators cannot reach.
8
+ */
5
9
  export type EntityTestFunc = (e: Entity) => boolean;
6
10
  /**
7
- * A composable query expression used to declare which entities a
8
- * {@link Query} or {@link System} should track.
11
+ * A composable expression describing which entities a {@link Query},
12
+ * {@link System}, or {@link Filter} should match.
9
13
  *
10
- * Queries can be nested arbitrarily:
14
+ * Operators can be nested arbitrarily:
11
15
  *
12
16
  * ```ts
13
- * // Entities that have Position AND (Sprite OR Container):
17
+ * // Position AND (Sprite OR Container):
14
18
  * world.system("render").query({
15
- * AND: [Position, { OR: [Sprite, Container] }]
19
+ * AND: [Position, { OR: [Sprite, Container] }],
16
20
  * });
17
21
  *
18
- * // Entities that have a parent with Player AND Container:
22
+ * // Parent has Player AND Container:
19
23
  * world.system("attach").query({
20
- * PARENT: { AND: [Player, Container] }
24
+ * PARENT: { AND: [Player, Container] },
21
25
  * });
22
26
  * ```
23
27
  *
24
- * Short forms:
25
- * - A single class or type id is equivalent to `{ HAS: [C] }`.
26
- * - An array `[A, B]` is equivalent to `{ HAS: [A, B] }`.
27
- * - Pass an {@link EntityTestFunc} directly for fully custom membership logic.
28
+ * Short forms recognized by `query` / `filter`:
29
+ * - A single class or numeric type id is shorthand for `{ HAS: [C] }`.
30
+ * - An array `[A, B]` is shorthand for `{ HAS: [A, B] }`.
31
+ * - An {@link EntityTestFunc} is invoked directly for fully custom logic.
28
32
  */
29
33
  export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFunc | {
30
34
  HAS: ComponentClassArray | ComponentClassOrType;
@@ -39,24 +43,30 @@ export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFu
39
43
  } | {
40
44
  PARENT: QueryDSL;
41
45
  };
42
- export declare function HAS(world: World, ...components: ComponentClassArray): EntityTestFunc;
43
46
  /**
44
- * Resolves component nullability based on what was declared in `requires` (or
45
- * the `_guaranteed` hint). Components in `R` are non-nullable; others are
46
- * `InstanceType<C> | undefined`.
47
+ * Resolve component nullability based on what was declared in `requires` (or
48
+ * the `_guaranteed` hint).
49
+ *
50
+ * Components in `R` resolve to `InstanceType<C>`; every other component class
51
+ * resolves to `InstanceType<C> | undefined`.
52
+ *
53
+ * @typeParam C - Component class being injected.
54
+ * @typeParam R - Tuple of component classes guaranteed present.
47
55
  */
48
56
  export type MaybeRequired<C, R extends (typeof Component)[]> = C extends typeof Component ? C extends R[number] ? InstanceType<C> : InstanceType<C> | undefined : never;
49
57
  /**
50
- * Statically extracts the component classes that are **guaranteed present** on
58
+ * Statically extract the component classes that are **guaranteed present** on
51
59
  * every entity matched by a {@link QueryDSL} expression.
52
60
  *
53
61
  * Rules:
54
62
  * - Plain class `C` → `[C]`
55
63
  * - Plain array `[A, B]` → `[A, B]`
56
- * - `{HAS: ...}` / `{HAS_ONLY: ...}` → recurse into the payload
57
- * - `{AND: [q1, q2, ...]}` → concatenation of each branch's extraction
58
- * - `{OR: ...}` / `{NOT: ...}` / `{PARENT: ...}` → `[]` (no guarantee)
64
+ * - `{ HAS: ... }` / `{ HAS_ONLY: ... }` → recurse into the payload
65
+ * - `{ AND: [q1, q2, ...] }` → concatenate each branch's extraction
66
+ * - `{ OR: ... }` / `{ NOT: ... }` / `{ PARENT: ... }` → `[]` (no guarantee)
59
67
  * - `EntityTestFunc` / numeric type id → `[]` (opaque)
68
+ *
69
+ * @typeParam Q - Query expression to analyse.
60
70
  */
61
71
  export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends readonly (typeof Component)[] ? Q : Q extends {
62
72
  HAS: infer H;
@@ -64,11 +74,9 @@ export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends re
64
74
  HAS_ONLY: infer H;
65
75
  } ? ExtractRequired<H> : Q extends {
66
76
  AND: infer A extends readonly QueryDSL[];
67
- } ? ExtractAndChain<A> : [];
68
- type ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [
77
+ } ? _ExtractAndChain<A> : [];
78
+ type _ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [
69
79
  infer First,
70
80
  ...infer Rest extends readonly QueryDSL[]
71
- ] ? [...ExtractRequired<First>, ...ExtractAndChain<Rest>] : [];
72
- /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
73
- export declare function buildEntityTest(world: World, q: QueryDSL): EntityTestFunc;
81
+ ] ? [...ExtractRequired<First>, ..._ExtractAndChain<Rest>] : [];
74
82
  export {};
package/dist/dsl.js CHANGED
@@ -1,56 +1,82 @@
1
- import { Component, calculateComponentBitmask, } from "./component.js";
2
- export function HAS(world, ...components) {
3
- const testBitmask = calculateComponentBitmask(components, world);
1
+ import { Component, _calculateComponentBitmask, } from "./component.js";
2
+ /**
3
+ * Build a predicate that returns `true` when an entity has every component
4
+ * type in `components` set on its archetype.
5
+ *
6
+ * @internal Factory used by {@link _buildEntityTest} and by `Query.update`'s
7
+ * watchlist auto-expansion.
8
+ *
9
+ * @param world - World used to resolve component classes to type ids.
10
+ * @param components - Component classes or numeric type ids to require.
11
+ */
12
+ export function _HAS(world, ...components) {
13
+ const testBitmask = _calculateComponentBitmask(components, world);
4
14
  return (e) => e.componentBitmask.hasBitset(testBitmask);
5
15
  }
6
- function HAS_ONLY(world, ...components) {
7
- const testBitmask = calculateComponentBitmask(components, world);
16
+ /**
17
+ * Build a predicate that returns `true` only when an entity's archetype is
18
+ * exactly the set in `components` (no other components attached).
19
+ */
20
+ function _HAS_ONLY(world, ...components) {
21
+ const testBitmask = _calculateComponentBitmask(components, world);
8
22
  return (e) => e.componentBitmask.equal(testBitmask);
9
23
  }
10
- function NOT(func) {
24
+ /** Negate a predicate. */
25
+ function _NOT(func) {
11
26
  return (e) => !func(e);
12
27
  }
13
- function AND(...funcs) {
28
+ /** Conjunction of multiple predicates. */
29
+ function _AND(...funcs) {
14
30
  return (e) => funcs.every((f) => f(e));
15
31
  }
16
- function OR(...funcs) {
32
+ /** Disjunction of multiple predicates. */
33
+ function _OR(...funcs) {
17
34
  return (e) => funcs.some((f) => f(e));
18
35
  }
19
- function PARENT(func) {
36
+ /** Lift a predicate to apply to the entity's parent (false when no parent). */
37
+ function _PARENT(func) {
20
38
  return (e) => (e.parent && func(e.parent)) || false;
21
39
  }
22
- /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
23
- export function buildEntityTest(world, q) {
40
+ /**
41
+ * Compile a {@link QueryDSL} expression into a runtime entity-test predicate.
42
+ *
43
+ * @internal Used by `Query`, `System`, and `Filter` to translate user-supplied
44
+ * DSL expressions into the predicate stored on `Query._belongs`.
45
+ *
46
+ * @param world - World used to resolve component classes to type ids.
47
+ * @param q - Query expression.
48
+ */
49
+ export function _buildEntityTest(world, q) {
24
50
  if (typeof q === "number" || (typeof q === "function" && q.prototype instanceof Component)) {
25
- return HAS(world, q);
51
+ return _HAS(world, q);
26
52
  }
27
53
  else if (typeof q === "function") {
28
54
  return q;
29
55
  }
30
56
  if (q instanceof Array) {
31
- return HAS(world, ...q);
57
+ return _HAS(world, ...q);
32
58
  }
33
59
  if ("HAS" in q) {
34
- return buildEntityTest(world, q.HAS);
60
+ return _buildEntityTest(world, q.HAS);
35
61
  }
36
62
  if ("HAS_ONLY" in q) {
37
63
  const v = q.HAS_ONLY;
38
64
  if (v instanceof Array) {
39
- return HAS_ONLY(world, ...v);
65
+ return _HAS_ONLY(world, ...v);
40
66
  }
41
- return HAS_ONLY(world, v);
67
+ return _HAS_ONLY(world, v);
42
68
  }
43
69
  if ("AND" in q) {
44
- return AND(...q.AND.map((sq) => buildEntityTest(world, sq)));
70
+ return _AND(...q.AND.map((sq) => _buildEntityTest(world, sq)));
45
71
  }
46
72
  if ("OR" in q) {
47
- return OR(...q.OR.map((sq) => buildEntityTest(world, sq)));
73
+ return _OR(...q.OR.map((sq) => _buildEntityTest(world, sq)));
48
74
  }
49
75
  if ("NOT" in q) {
50
- return NOT(buildEntityTest(world, q.NOT));
76
+ return _NOT(_buildEntityTest(world, q.NOT));
51
77
  }
52
78
  if ("PARENT" in q) {
53
- return PARENT(buildEntityTest(world, q.PARENT));
79
+ return _PARENT(_buildEntityTest(world, q.PARENT));
54
80
  }
55
81
  throw "Unrecognized query term";
56
82
  }
package/dist/dsl.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAGT,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AA+FxB;;;;;;;;;GASG;AACH,MAAM,UAAU,IAAI,CAAC,KAAY,EAAE,GAAG,UAA+B;IACnE,MAAM,WAAW,GAAG,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClE,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,KAAY,EAAE,GAAG,UAA+B;IACjE,MAAM,WAAW,GAAG,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClE,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AAC9D,CAAC;AAED,0BAA0B;AAC1B,SAAS,IAAI,CAAC,IAAoB;IAChC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,0CAA0C;AAC1C,SAAS,IAAI,CAAC,GAAG,KAAuB;IACtC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,0CAA0C;AAC1C,SAAS,GAAG,CAAC,GAAG,KAAuB;IACrC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,+EAA+E;AAC/E,SAAS,OAAO,CAAC,IAAoB;IACnC,OAAO,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAY,EAAE,CAAW;IACxD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC,SAAS,YAAY,SAAS,CAAC,EAAE;QAC1F,OAAO,IAAI,CAAC,KAAK,EAAE,CAAqB,CAAC,CAAC;KAC3C;SAAM,IAAI,OAAO,CAAC,KAAK,UAAU,EAAE;QAClC,OAAO,CAAmB,CAAC;KAC5B;IAED,IAAI,CAAC,YAAY,KAAK,EAAE;QACtB,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;KAC1B;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;KACvC;IAED,IAAI,UAAU,IAAI,CAAC,EAAE;QACnB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,IAAI,CAAC,YAAY,KAAK,EAAE;YACtB,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;SAC/B;QACD,OAAO,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;KAC5B;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;KAChE;IAED,IAAI,IAAI,IAAI,CAAC,EAAE;QACb,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;KAC9D;IAED,IAAI,KAAK,IAAI,CAAC,EAAE;QACd,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;KAC7C;IAED,IAAI,QAAQ,IAAI,CAAC,EAAE;QACjB,OAAO,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;KACnD;IAED,MAAM,yBAAyB,CAAC;AAClC,CAAC"}