@vworlds/vecs 1.0.40 → 1.0.42

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/dist/filter.d.ts CHANGED
@@ -70,12 +70,12 @@ export declare class Filter<R extends ComponentType[] = []> {
70
70
  * @param callback - Receives the entity and a tuple of resolved component
71
71
  * instances.
72
72
  */
73
- forEach<J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
73
+ forEach<const J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
74
74
  /**
75
75
  * As above, but pass `{ cursor: Entity, inject: [...] }` to make the
76
76
  * entity-first signature explicit. Identical behavior to the array form.
77
77
  */
78
- forEach<J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
78
+ forEach<const J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
79
79
  /**
80
80
  * As above, but pass `{ cursor: Iter, inject: [...] }` to receive a reused
81
81
  * {@link Iter} cursor instead of the bare entity. The cursor exposes the
@@ -83,6 +83,6 @@ export declare class Filter<R extends ComponentType[] = []> {
83
83
  * resolved component was read from (the visited entity for direct components,
84
84
  * the relationship's target entity for ones injected via `target`).
85
85
  */
86
- forEach<J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
86
+ forEach<const J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
87
87
  private _forEachMatch;
88
88
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vworlds/vecs",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -11,8 +11,8 @@ export declare class Group<K = unknown, R extends ComponentType[] = []> {
11
11
  get count(): number;
12
12
  [Symbol.iterator](): IterableIterator<Entity>;
13
13
  forEach(callback: (e: Entity) => void): this;
14
- forEach<J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
15
- forEach<J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
16
- forEach<J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
14
+ forEach<const J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
15
+ forEach<const J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
16
+ forEach<const J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
17
17
  has(e: Entity): boolean;
18
18
  }
@@ -48,22 +48,22 @@ export declare class Query<R extends ComponentType[] = []> implements TermConsum
48
48
  belongs(e: Entity): boolean;
49
49
  has(e: Entity): boolean;
50
50
  forEach(callback: (e: Entity) => void): this;
51
- forEach<J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
52
- forEach<J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
53
- forEach<J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
51
+ forEach<const J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
52
+ forEach<const J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
53
+ forEach<const J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
54
54
  toString(): string;
55
55
  with<const S extends readonly QuerySpec[]>(...specs: [...S]): Query<[...R, ...ExtractSpecsRequired<S>]>;
56
56
  without(dsl: QueryDSL): this;
57
57
  track(): this;
58
- orderBy<J extends InjectTuple>(components: readonly [...J], compare: (entityA: Entity, a: MaybeResolvedInjectTuple<J, R>, entityB: Entity, b: MaybeResolvedInjectTuple<J, R>) => number): this;
59
- orderBy<J extends InjectTuple>(options: EntityCursorOption & OrderByOptions<J>, compare: (entityA: Entity, a: MaybeResolvedInjectTuple<J, R>, entityB: Entity, b: MaybeResolvedInjectTuple<J, R>) => number): this;
60
- orderBy<J extends InjectTuple>(options: IterCursorOption & OrderByOptions<J>, compare: (a: Iter, aResolved: MaybeResolvedInjectTuple<J, R>, b: Iter, bResolved: MaybeResolvedInjectTuple<J, R>) => number): this;
58
+ orderBy<const J extends InjectTuple>(components: readonly [...J], compare: (entityA: Entity, a: MaybeResolvedInjectTuple<J, R>, entityB: Entity, b: MaybeResolvedInjectTuple<J, R>) => number): this;
59
+ orderBy<const J extends InjectTuple>(options: EntityCursorOption & OrderByOptions<J>, compare: (entityA: Entity, a: MaybeResolvedInjectTuple<J, R>, entityB: Entity, b: MaybeResolvedInjectTuple<J, R>) => number): this;
60
+ orderBy<const J extends InjectTuple>(options: IterCursorOption & OrderByOptions<J>, compare: (a: Iter, aResolved: MaybeResolvedInjectTuple<J, R>, b: Iter, bResolved: MaybeResolvedInjectTuple<J, R>) => number): this;
61
61
  cascade(rel: ComponentRef): this;
62
62
  cascade(options: CascadeOptions): this;
63
63
  cascade(rel: ComponentRef, compare: (a: Entity, b: Entity) => number): this;
64
64
  cascade(options: CascadeOptions, compare: (a: Entity, b: Entity) => number): this;
65
- groupBy<K, J extends FanInjectTuple>(components: readonly [...J], key: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => K): GroupedQuery<K, R>;
66
- groupBy<K, J extends FanInjectTuple>(options: GroupByOptions<J>, key: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => K): GroupedQuery<K, R>;
65
+ groupBy<K, const J extends FanInjectTuple>(components: readonly [...J], key: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => K): GroupedQuery<K, R>;
66
+ groupBy<K, const J extends FanInjectTuple>(options: GroupByOptions<J>, key: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => K): GroupedQuery<K, R>;
67
67
  groupBy(relationship: ComponentRef): GroupedQuery<Entity, R>;
68
68
  groupBy(options: {
69
69
  relationship: ComponentRef;
@@ -78,13 +78,13 @@ export declare class Query<R extends ComponentType[] = []> implements TermConsum
78
78
  set<C extends ComponentClass>(Class: C, props: Partial<InstanceType<C>>): this;
79
79
  set(ceid: EID, props: Partial<ComponentInstance>): this;
80
80
  set(entity: Entity, props: Partial<ComponentInstance>): this;
81
- enter<J extends InjectTuple>(inject: readonly [...J], callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
82
- enter<J extends InjectTuple>(options: EntityCursorOption & InjectCallbackOptions<J>, callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
83
- enter<J extends InjectTuple>(options: IterCursorOption & InjectCallbackOptions<J>, callback: (it: Iter, injected: RequiredResolvedInjectTuple<J>) => void): this;
81
+ enter<const J extends InjectTuple>(inject: readonly [...J], callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
82
+ enter<const J extends InjectTuple>(options: EntityCursorOption & InjectCallbackOptions<J>, callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
83
+ enter<const J extends InjectTuple>(options: IterCursorOption & InjectCallbackOptions<J>, callback: (it: Iter, injected: RequiredResolvedInjectTuple<J>) => void): this;
84
84
  enter(callback: (e: Entity) => void): this;
85
- exit<J extends InjectTuple>(inject: readonly [...J], callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
86
- exit<J extends InjectTuple>(options: EntityCursorOption & InjectCallbackOptions<J>, callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
87
- exit<J extends InjectTuple>(options: IterCursorOption & InjectCallbackOptions<J>, callback: (it: Iter, injected: RequiredResolvedInjectTuple<J>) => void): this;
85
+ exit<const J extends InjectTuple>(inject: readonly [...J], callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
86
+ exit<const J extends InjectTuple>(options: EntityCursorOption & InjectCallbackOptions<J>, callback: (e: Entity, injected: RequiredResolvedInjectTuple<J>) => void): this;
87
+ exit<const J extends InjectTuple>(options: IterCursorOption & InjectCallbackOptions<J>, callback: (it: Iter, injected: RequiredResolvedInjectTuple<J>) => void): this;
88
88
  exit(callback: (e: Entity) => void): this;
89
89
  /**
90
90
  * Register one update callback for a watched component.
@@ -105,10 +105,10 @@ export declare class Query<R extends ComponentType[] = []> implements TermConsum
105
105
  * already has an update callback on this query.
106
106
  */
107
107
  update<C extends ComponentType>(ComponentClass: C, callback: (e: Entity, c: InstanceType<C>) => void): this;
108
- update<C extends ComponentType, J extends InjectTuple>(options: EntityCursorOption & UpdateCallbackOptions<C, J> & {
108
+ update<C extends ComponentType, const J extends InjectTuple>(options: EntityCursorOption & UpdateCallbackOptions<C, J> & {
109
109
  inject: readonly [...J];
110
110
  }, callback: (e: Entity, c: InstanceType<C>, injected: MaybeResolvedInjectTuple<J, R>) => void): this;
111
- update<C extends ComponentType, J extends InjectTuple>(options: IterCursorOption & UpdateCallbackOptions<C, J> & {
111
+ update<C extends ComponentType, const J extends InjectTuple>(options: IterCursorOption & UpdateCallbackOptions<C, J> & {
112
112
  inject: readonly [...J];
113
113
  }, callback: (it: Iter, c: InstanceType<C>, injected: MaybeResolvedInjectTuple<J, R>) => void): this;
114
114
  update<C extends ComponentType>(options: EntityCursorOption & UpdateCallbackOptions<C, []>, callback: (e: Entity, c: InstanceType<C>) => void): this;
package/dist/system.d.ts CHANGED
@@ -165,12 +165,12 @@ export declare class System<R extends ComponentType[] = []> extends Query<R> imp
165
165
  * });
166
166
  * ```
167
167
  */
168
- each<J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
168
+ each<const J extends FanInjectTuple>(components: readonly [...J], callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
169
169
  /**
170
170
  * As above, but pass `{ cursor: Entity, inject: [...] }` to make the
171
171
  * entity-first signature explicit. Identical behavior to the array form.
172
172
  */
173
- each<J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
173
+ each<const J extends FanInjectTuple>(options: EntityCursorOption & FanInjectCallbackOptions<J>, callback: (e: Entity, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
174
174
  /**
175
175
  * As above, but pass `{ cursor: Iter, inject: [...] }` to receive a reused
176
176
  * {@link Iter} cursor instead of the bare entity. The cursor exposes the
@@ -178,7 +178,7 @@ export declare class System<R extends ComponentType[] = []> extends Query<R> imp
178
178
  * resolved component was read from (the visited entity for direct components,
179
179
  * the relationship target for `target`-injected ones).
180
180
  */
181
- each<J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
181
+ each<const J extends FanInjectTuple>(options: IterCursorOption & FanInjectCallbackOptions<J>, callback: (it: Iter, resolved: MaybeResolvedInjectTuple<J, R>) => void): this;
182
182
  /**
183
183
  * Add entity-membership predicates using {@link QuerySpec} expressions.
184
184
  *
package/docs/README.md CHANGED
@@ -46,5 +46,5 @@ Keep the [Glossary](./glossary.md) at hand for terminology.
46
46
  | [utilities.md](./utilities.md) | Public helper classes: `Events`, `ArrayMap`, `IterableArrayMap`, plus `Bitset` cross-ref. |
47
47
  | [glossary.md](./glossary.md) | ECS and vecs terminology. |
48
48
 
49
- Networking (wire encoding with `@type`, protocol component ordering, replication) lives in the
49
+ Networking (wire encoding with `@wireType`, protocol component ordering, replication) lives in the
50
50
  `@vworlds/vecs-wire` and `@vworlds/vecs-protocol` packages and is only cross-referenced here.
@@ -102,9 +102,8 @@ export class Rotation {
102
102
 
103
103
  ### Networked components
104
104
 
105
- Mark wire fields with `@type` from `@vworlds/vecs-wire` (conventionally imported as
106
- `import { type as wireType }`). The
107
- **order** you pass components in `networkComponents` _is the wire protocol_ (type id = index + 1).
105
+ Mark wire fields with `@wireType` from `@vworlds/vecs-wire` (`import { wireType }`). The
106
+ **order** you pass components in `networkComponents` _is the wire protocol_ (wire type id = index + 1).
108
107
  Reordering is a breaking protocol change — snapshot-test the order.
109
108
 
110
109
  ```ts
@@ -114,7 +113,7 @@ class Arc {
114
113
  new ServerWorld({ networkComponents: [Position, Rotation, /* … */ Arc] });
115
114
  ```
116
115
 
117
- > **Decorator gotcha.** `@type` must work under both _legacy_ (`experimentalDecorators`, used by
116
+ > **Decorator gotcha.** `@wireType` must work under both _legacy_ (`experimentalDecorators`, used by
118
117
  > `tsc`) and _standard_ TC39 decorators (what esbuild — `tsx`/Vite — emits for source files).
119
118
  > If you load decorated component classes from **source** through an esbuild-based runtime and
120
119
  > their fields silently fail to register, that's the standard-vs-legacy mismatch. vecs-wire
@@ -179,8 +178,15 @@ entity.target(InCell); // the target entity
179
178
  world.query("ByCell").with(Networked, Position).groupBy(InCell); // index by target
180
179
  ```
181
180
 
182
- - **`Relationship.target` is an `Entity` — not serializable, so relationships are not
183
- networkable.** Don't try to replicate them.
181
+ - **`Relationship.target` is an `Entity` — not directly serialisable, but
182
+ relationships can be networked** when listed in `networkComponents`.
183
+ `vecs-protocol` registers `Relationship.prototype.target` as an `"entity"`
184
+ wire field (u32 eid), so subclasses serialise their target as a raw entity
185
+ id. The client resolves the id to a live entity (creating a ghost
186
+ placeholder if the parent hasn't arrived yet). Use a dedicated relationship
187
+ class per concern — e.g. `ChildOf` for server-local ownership/lifecycle,
188
+ `DrawIn` (in vecs-phaser) for networked display hierarchy — so the two
189
+ concerns don't overload a single relationship.
184
190
  - **Retargeting is a plain data change** (no archetype churn). Prefer retargeting a relationship
185
191
  over swapping marker components to express "which bucket this entity is in."
186
192
 
@@ -547,18 +553,18 @@ system after whatever produces the entity/companion.
547
553
 
548
554
  ## 8. Anti-patterns (keep ECS semantics)
549
555
 
550
- | Smell | Do instead |
551
- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
552
- | A side `Map<eid, resource>` | Store the resource in a **companion component**; let queries drive its lifecycle; release in `onRemove`. |
553
- | An imperative runtime object with `start()/stop()/track/enable` | A **module** + the world's own lifecycle (`clearAllEntities()`, closing the world). Systems live on the world; they don't need a wrapper to turn on. |
554
- | Builder/factory helpers that _create entities_ (`createArc(world, …)`) | Let callers `world.entity().add(Networked).set(Arc, …).set(FillStyle, …)`. Components are small and optional; the caller adds exactly what it wants. |
555
- | A god component with many optional fields | Split into small single-purpose components; absence encodes "not applicable." |
556
- | Duplicated `enter` + `update` doing the same thing | One `update({ watch: C, onEnter: true })` when update owns initialization. |
557
- | One system trying to act in two phases | Split into two systems, one per phase. |
558
- | Custom phases for app/render logic | Use the standard taxonomy; only engine modules insert phases. |
559
- | `.each` scanning every entity to detect change | Reactive `.update(C)` — fires only on change. |
560
- | Networking a relationship | You can't (target is an `Entity`); replicate a plain id/tag instead, or keep the relationship server-local. |
561
- | A `registerFooSystem(world)` wrapper per system | Declare the system inline in the module's `init`. |
556
+ | Smell | Do instead |
557
+ | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
558
+ | A side `Map<eid, resource>` | Store the resource in a **companion component**; let queries drive its lifecycle; release in `onRemove`. |
559
+ | An imperative runtime object with `start()/stop()/track/enable` | A **module** + the world's own lifecycle (`clearAllEntities()`, closing the world). Systems live on the world; they don't need a wrapper to turn on. |
560
+ | Builder/factory helpers that _create entities_ (`createArc(world, …)`) | Let callers `world.entity().add(Networked).set(Arc, …).set(FillStyle, …)`. Components are small and optional; the caller adds exactly what it wants. |
561
+ | A god component with many optional fields | Split into small single-purpose components; absence encodes "not applicable." |
562
+ | Duplicated `enter` + `update` doing the same thing | One `update({ watch: C, onEnter: true })` when update owns initialization. |
563
+ | One system trying to act in two phases | Split into two systems, one per phase. |
564
+ | Custom phases for app/render logic | Use the standard taxonomy; only engine modules insert phases. |
565
+ | `.each` scanning every entity to detect change | Reactive `.update(C)` — fires only on change. |
566
+ | Networking a relationship | Relationships can be networked when listed in `networkComponents`: `Relationship.target` serialises as a u32 entity id. List `ChildOf` (or custom subclasses) explicitly to replicate parent-child links. |
567
+ | A `registerFooSystem(world)` wrapper per system | Declare the system inline in the module's `init`. |
562
568
 
563
569
  ---
564
570
 
@@ -576,9 +582,12 @@ system after whatever produces the entity/companion.
576
582
  - **Within a phase, order = registration order;** across phases, the spine guarantees order. Don't
577
583
  use registration order for cross-module correctness — use phases.
578
584
  - **Networked component order = wire protocol.** Reordering breaks the protocol.
579
- - **`@type`/`@wireType` must survive esbuild's standard decorators** when components load from
585
+ - **`@wireType` must survive esbuild's standard decorators** when components load from
580
586
  source (tsx/Vite). Otherwise fields silently don't register.
581
- - **`Relationship.target` is an `Entity`** — not wire-encodable.
587
+ - **`Relationship.target` is an `Entity`** — not directly wire-encodable, but
588
+ `vecs-protocol` registers it as an `"entity"` wire field (u32 eid) at module
589
+ load, so `Relationship` subclasses listed in `networkComponents` serialise
590
+ correctly.
582
591
  - **`world.module(M)` runs `init` once.** Re-registering with a different config throws.
583
592
 
584
593
  ---
package/docs/glossary.md CHANGED
@@ -105,8 +105,7 @@ how often a system fires. [Systems](./systems.md#cadence-interval-rate-and-tick-
105
105
  **Watchlist** — the set of component types a query or system reacts to via `update`, stored as a
106
106
  bitmask. [Systems](./systems.md#reactive-callbacks-enter-update-exit).
107
107
 
108
- **Wire type** — a field-level encoding declaration (`@type` from `@vworlds/vecs-wire`, commonly
109
- imported as `wireType`) used by the networking packages to serialize components. Networking is
108
+ **Wire type** — a field-level encoding declaration (`@wireType` from `@vworlds/vecs-wire`) used by the networking packages to serialize components. Networking is
110
109
  documented with `@vworlds/vecs-wire` / `@vworlds/vecs-protocol`, not here.
111
110
 
112
111
  **World** — the central container owning entities, component registrations, queries, systems, and
@@ -26,8 +26,12 @@ with `children(ref)` through a reverse index the world maintains automatically.
26
26
  most one instance of a given relationship class (like any component), so multi-target links are
27
27
  modeled with one relationship class per meaning, or child entities.
28
28
 
29
- Because `target` is an `Entity` reference, **relationships are not wire-serializable — don't try
30
- to network them**. Replicate a plain id or tag instead, or keep the relationship server-local.
29
+ Because `target` is an `Entity` reference, relationships are not wire-serializable by default.
30
+ However, `vecs-protocol` registers `Relationship.prototype.target` as an `"entity"` wire field
31
+ (u32 eid) at module load, so **relationship subclasses listed in `networkComponents` are
32
+ networkable**: the target serialises as a raw entity id, and the client resolves it to a live
33
+ entity (creating a ghost placeholder if the parent hasn't arrived yet). List `ChildOf` (or custom
34
+ subclasses) explicitly in your `networkComponents` array to replicate parent-child links.
31
35
 
32
36
  ## Built-in relationships
33
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vworlds/vecs",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",