@oasys/oecs 0.5.0 → 0.5.2

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 (80) hide show
  1. package/CHANGELOG.md +108 -1
  2. package/README.md +20 -8
  3. package/dist/core/ecs/archetype.d.cts +3 -3
  4. package/dist/core/ecs/archetype.d.ts +3 -3
  5. package/dist/core/ecs/component.d.cts +25 -0
  6. package/dist/core/ecs/component.d.ts +25 -0
  7. package/dist/core/ecs/component.d.ts.map +1 -1
  8. package/dist/core/ecs/ecs.d.cts +67 -49
  9. package/dist/core/ecs/ecs.d.ts +67 -49
  10. package/dist/core/ecs/ecs.d.ts.map +1 -1
  11. package/dist/core/ecs/entity.d.cts +1 -1
  12. package/dist/core/ecs/entity.d.ts +1 -1
  13. package/dist/core/ecs/facades.d.cts +1 -1
  14. package/dist/core/ecs/facades.d.ts +1 -1
  15. package/dist/core/ecs/host_commands.d.cts +30 -20
  16. package/dist/core/ecs/host_commands.d.ts +30 -20
  17. package/dist/core/ecs/host_commands.d.ts.map +1 -1
  18. package/dist/core/ecs/index.d.cts +3 -3
  19. package/dist/core/ecs/index.d.ts +3 -3
  20. package/dist/core/ecs/index.d.ts.map +1 -1
  21. package/dist/core/ecs/observer.d.cts +1 -1
  22. package/dist/core/ecs/observer.d.ts +1 -1
  23. package/dist/core/ecs/observer.d.ts.map +1 -1
  24. package/dist/core/ecs/query.d.cts +22 -6
  25. package/dist/core/ecs/query.d.ts +22 -6
  26. package/dist/core/ecs/query.d.ts.map +1 -1
  27. package/dist/core/ecs/ref.d.ts.map +1 -1
  28. package/dist/core/ecs/run_condition.d.cts +1 -1
  29. package/dist/core/ecs/run_condition.d.ts +1 -1
  30. package/dist/core/ecs/run_condition.d.ts.map +1 -1
  31. package/dist/core/ecs/store.d.cts +10 -23
  32. package/dist/core/ecs/store.d.ts +10 -23
  33. package/dist/core/ecs/store.d.ts.map +1 -1
  34. package/dist/dev_flag.d.cts +0 -15
  35. package/dist/dev_flag.d.ts +0 -15
  36. package/dist/dev_flag.d.ts.map +1 -1
  37. package/dist/extensions/editor/editor.d.cts +31 -29
  38. package/dist/extensions/editor/editor.d.ts +31 -29
  39. package/dist/extensions/editor/editor.d.ts.map +1 -1
  40. package/dist/extensions/editor/field_handle.d.cts +3 -3
  41. package/dist/extensions/editor/field_handle.d.ts +3 -3
  42. package/dist/extensions/editor/field_handle.d.ts.map +1 -1
  43. package/dist/extensions/editor/index.cjs +1 -1
  44. package/dist/extensions/editor/index.development.cjs +1 -0
  45. package/dist/extensions/editor/index.development.js +239 -0
  46. package/dist/extensions/editor/index.js +53 -51
  47. package/dist/extensions/reactive/index.development.cjs +1 -0
  48. package/dist/extensions/reactive/index.development.js +215 -0
  49. package/dist/extensions/solid/index.development.cjs +1 -0
  50. package/dist/extensions/solid/index.development.js +40 -0
  51. package/dist/{host_commands-i4cAeyL5.js → host_commands-BI8pEmjH.js} +26 -18
  52. package/dist/{host_commands-BF8QMi3c.cjs → host_commands-CxhpzMx9.cjs} +1 -1
  53. package/dist/index.cjs +1 -1
  54. package/dist/index.d.cts +3 -3
  55. package/dist/index.d.ts +3 -3
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.development.cjs +1 -0
  58. package/dist/index.development.js +8116 -0
  59. package/dist/index.js +269 -214
  60. package/dist/internal-BMDgWZbt.js +2485 -0
  61. package/dist/internal-LiTSB_tG.cjs +2 -0
  62. package/dist/internal.cjs +1 -1
  63. package/dist/internal.development.cjs +1 -0
  64. package/dist/internal.development.js +24 -0
  65. package/dist/internal.js +1 -1
  66. package/dist/interop-DqxleBo7.js +18 -0
  67. package/dist/interop-T1HZ-Dpa.cjs +1 -0
  68. package/dist/kernel-Cz2Kxlwl.js +240 -0
  69. package/dist/kernel-sw7ScKWu.cjs +1 -0
  70. package/dist/primitives.development.cjs +1 -0
  71. package/dist/primitives.development.js +45 -0
  72. package/dist/reactive/index.development.cjs +1 -0
  73. package/dist/reactive/index.development.js +18 -0
  74. package/dist/shallow-9S1pY_Iw.js +159 -0
  75. package/dist/shallow-C5YmTbtb.cjs +1 -0
  76. package/dist/shared.development.cjs +1 -0
  77. package/dist/shared.development.js +7 -0
  78. package/dist/version.d.cts +1 -1
  79. package/dist/version.d.ts +1 -1
  80. package/package.json +31 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,114 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.2] — 2026-07-08
9
+
10
+ ### Added — a guards-on build and an explicit dev entry
11
+
12
+ - **`@oasys/oecs/dev`** — the same public API as `@oasys/oecs` with the `__DEV__`
13
+ guards left **on**, for a direct guards-on import (browser/CDN, quick debugging,
14
+ or bundlers that don't auto-select conditions).
15
+ - **`development` export condition** — dev-mode bundlers (`vite dev`,
16
+ `webpack --mode development`) now resolve `@oasys/oecs` (and every subpath) to a
17
+ guards-on build automatically; production-mode builds resolve to the stripped
18
+ build as before.
19
+ - npm now ships a **dual build** (`scripts/build.mjs`): the default `*.js`/`*.cjs`
20
+ are the stripped production artifacts (unchanged), alongside new guards-on
21
+ `*.development.js`/`*.development.cjs`.
22
+ - New guide: [Development guards & production builds](docs/PRODUCTION.md).
23
+
24
+ ### Changed — dev-guard default is now production on JSR/Deno
25
+
26
+ - Raw-source (JSR/Deno) consumers now default to `__DEV__ = false` (**production —
27
+ guards off, no per-frame tax**), matching npm's default. Previously the raw path
28
+ defaulted to guards-on. To enable the guards while developing on Deno, set
29
+ `globalThis.__DEV__ = true` **before the first import**. The `globalThis.__DEV__`
30
+ override is unchanged; only the default flipped. (`dev_flag.ts`)
31
+
32
+ ## [0.5.1] — 2026-07-06
33
+
34
+ ### Changed (breaking) — one attach grammar
35
+
36
+ `addComponents` and `template` now take the same callable-bundle varargs as `spawnBundle`
37
+ and `ctx.commands.spawn` / `add`, replacing the `{ def, values }[]` entry-object array — one
38
+ grammar across every authoring surface:
39
+
40
+ ```ts
41
+ // before
42
+ ecs.addComponents(e, [{ def: Pos, values: { x, y } }, { def: Vel, values: { vx } }]);
43
+ const Bullet = ecs.template([{ def: Pos, values: { x: 0, y: 0 } }]);
44
+ // after
45
+ ecs.addComponents(e, Pos({ x, y }), Vel({ vx }));
46
+ const Bullet = ecs.template(Pos({ x: 0, y: 0 }));
47
+ ```
48
+
49
+ To migrate: drop the array brackets, wrap a valued entry in its def's call
50
+ (`{ def: X, values: V }` → `X(V)`), and leave a bare entry bare (`{ def: X }` → `X`).
51
+
52
+ - Each item is schema-checked against its **own** def via the `StrictBundles` mapped tuple
53
+ (`{ [K in keyof Items]: … }`) — a misspelled or cross-component field, including a
54
+ hand-written raw `{ def, values }` literal, is a compile error. `spawnBundle` gains this
55
+ per-item checking (it previously had none).
56
+ - `ctx.commands.spawn` / `ctx.commands.add` now schema-check their bundle values in
57
+ declared-access systems as well (the `DeclaredBundleOrDef` type distributes over the
58
+ declared add set); a permissive / `exclusive` context stays loose, as before.
59
+ - The `TemplateEntry` / `TemplateEntries` public types are removed (they encoded the retired
60
+ entry-object grammar). The host command seam (`HostCommandQueue.spawn` — the record/replay
61
+ and editor-undo transport) deliberately keeps its entry-object + complete-values shape.
62
+
63
+ ### Changed — API vocabulary consistency
64
+
65
+ Cheap alignments from a public-API vocabulary audit that followed the grammar unification:
66
+
67
+ - `removeComponents(e, ...defs)` is now varargs, mirroring `addComponents` (was
68
+ `removeComponents(e, defs[])`).
69
+ - `HostCommandQueue.pending()` is now a `pending` getter (matching every other count accessor).
70
+ - `ReadonlyEntityIdArray` → `ReadonlyEntityIDArray` (acronym casing, matching `EntityID`).
71
+ - The entity-id parameter is now uniformly `entityId` across the core surface (ECS lifecycle,
72
+ host-command queue, `ObserverFn`); the `HostCommand` wire-format field stays `eid`.
73
+ - Source-compatible widenings: `ecs.despawn`, `ecs.removeSystem`, and the `HostCommandQueue`
74
+ mutators now return `this` for chaining.
75
+
76
+ The `ref` / `refRead` argument order was reviewed and **deliberately kept** def-first
77
+ (`ctx.ref(def, entityId)`): these are the outside-iteration members of the `cols.mut` /
78
+ `cols.read` column-cursor family, so def-first is the cursor convention, not an inconsistency
79
+ to fix — flipping it would align with `getField` while breaking alignment with `cols.mut`.
80
+ Documented as such (`refs.md`, `queries.md`) rather than flipped.
81
+
82
+ ### Changed (breaking) — host-write-seam verb grammar
83
+
84
+ The host-write-seam handles are namespaced command buffers, so they drop the component noun
85
+ to match `ctx.commands.add` / `remove` — and their own already-bare
86
+ `spawn`/`despawn`/`disable`/`enable`/`setField`:
87
+
88
+ - `HostCommandQueue.addComponent` / `removeComponent` → `add` / `remove`.
89
+ - `Editor` and `TransactionBuilder` `.addComponent` / `.removeComponent` → `add` / `remove`
90
+ (the two surfaces are designed to match, so they move together).
91
+ - The editor extension's entity-id parameters and its `FieldReader` type now read `entityId`,
92
+ completing the core's `eid` → `entityId` pass.
93
+
94
+ The wire-format `kind` discriminants (`"add_component"` / `"remove_component"`), the ring
95
+ codecs, and the `HostCommand` record's `eid` field are unchanged — transport vocabulary.
96
+
97
+ ### Changed (breaking) — `ctx.getResource`
98
+
99
+ The in-system resource getter is now `ctx.getResource(key)` (was the verb-less `ctx.resource(key)`),
100
+ matching its flat-surface siblings `setResource` / `removeResource` / `hasResource` and the
101
+ `getField` / `setField` / `hasComponent` convention. The rule is now explicit: the flat `ctx`
102
+ surface verbs every accessor; the grouped `ecs.resources` facade drops the noun (`get` / `set` /
103
+ `remove` / `has`) because its receiver already names it. `ConditionContext` (run-condition
104
+ predicates) moves in lockstep.
105
+
106
+ ### Fixed
107
+
108
+ - The immediate host spawn family (`spawn` / `spawnBundle` / `spawnMany`) now throws in DEV
109
+ when called from inside a system body — redirecting to `ctx.commands.spawn` — like every
110
+ other immediate host structural mutator. Previously it was silently unguarded (the archetype
111
+ iteration guard does not cover the append path), a live mid-iteration footgun; its guard
112
+ docstring's "one rule for every host mutator" claim is now true.
113
+ - Added explicit public `QueryCache` cache-map type annotations so JSR publish passes
114
+ slow-type validation and can generate package declarations cleanly.
115
+
8
116
  ## [0.5.0] — 2026-07-06
9
117
 
10
118
  ### Changed (breaking) — lifecycle & naming unification
@@ -27,7 +135,6 @@ rename/removal map:
27
135
  | `sourcesOf(def, tgt)` | `sourcesOf(tgt, def)` — matches `targetOf` / `targetsOf` |
28
136
  | `query.count()` | `query.entityCount` (getter, beside `archetypeCount`) |
29
137
  | `WorldRestoreError` / `WORLD_SNAPSHOT_VERSION` | `ECSRestoreError` / `ECS_SNAPSHOT_VERSION` |
30
- | `DestroyEntityArg` (type) | `DespawnArg` |
31
138
 
32
139
  - **Host `despawn` is immediate** — `ecs.despawn(e); ecs.isAlive(e)` is `false` on the next
33
140
  line, closing the audit's M1 finding (host `addComponent` immediate but destroy buffered).
package/README.md CHANGED
@@ -114,13 +114,14 @@ ecs.getField(e, Pos, "x"); // ≈ 1.667
114
114
  **Structural changes**
115
115
 
116
116
  - **Deferred inside systems, immediate on the host** — `ctx.commands` (a Bevy-`Commands`-style
117
- facade) buffers spawn / add / remove / despawn / enable / disable until the phase flush, so
118
- iterators stay valid. Every host-side mutation (`ecs.addComponent` / `removeComponent` /
117
+ facade) buffers add / remove / despawn / enable / disable until the phase flush, so iterators
118
+ stay valid (`commands.spawn` returns the id immediately; its component attaches are deferred). Every host-side mutation (`ecs.addComponent` / `removeComponent` /
119
119
  `despawn` / `disable` / `enable`) applies immediately.
120
120
  - **Entity enable/disable** — `disable` / `enable` / `isDisabled`; disabled rows sit in a partitioned
121
121
  tail and are skipped by default queries.
122
- - **Templates & bundles** — `ecs.template([...])` blueprints consumed by `spawn` /
123
- `spawnMany` for zero-transition spawns; `bundle(...)` + `spawnBundle(...)`.
122
+ - **Templates & bundles** — `ecs.template(Pos({ x, y }), …)` blueprints consumed by `spawn` /
123
+ `spawnMany` for zero-transition spawns; the same callable-bundle varargs drive `spawnBundle(...)`
124
+ and `addComponents(...)`.
124
125
 
125
126
  **Reactivity & relationships**
126
127
 
@@ -139,8 +140,8 @@ ecs.getField(e, Pos, "x"); // ≈ 1.667
139
140
 
140
141
  **Determinism, persistence & integration**
141
142
 
142
- - **Determinism** (opt-in) — `new ECS({ deterministic: true })`, then `ecs.snapshots.stateHash()` (FNV-1a over
143
- live dense bytes, sparse stores, and multi-relation target sets), `ecs.snapshots.capture()` /
143
+ - **Determinism** (opt-in) — `new ECS({ deterministic: true })`, then `ecs.snapshots.stateHash()` (an FNV-1a-style
144
+ 32-bit digest over live dense bytes, sparse stores, and multi-relation target sets), `ecs.snapshots.capture()` /
144
145
  `ecs.snapshots.restore(...)`, plus sparse variants. Backing-agnostic: a heap world and a shared world with identical history produce
145
146
  identical hashes.
146
147
  - **Host → ECS write seam** — `installHostCommandSeam(ecs)` applies typed `HostCommand`s off-schedule
@@ -167,20 +168,31 @@ The core is `@oasys/oecs`; everything else is opt-in and costs nothing until imp
167
168
 
168
169
  | Import | What it is |
169
170
  | --- | --- |
170
- | `@oasys/oecs` | the ECS — pure-TS heap profile by default |
171
+ | `@oasys/oecs` | the ECS — pure-TS heap profile by default (production build; dev guards stripped) |
172
+ | `@oasys/oecs/dev` | the same ECS with the dev guards **on** — for a direct guards-on import; see [Dev vs prod](#dev-vs-prod) |
171
173
  | `@oasys/oecs/shared` | opt-in `SharedArrayBuffer` allocators for worker offload / a WASM backend (needs COOP/COEP) |
172
174
  | `@oasys/oecs/reactive` | zero-dependency reactive kernel (`signal`/`computed`/`effect`, reactive collections) |
173
175
  | `@oasys/oecs/reactive-sync` | ECS→reactive bridge — publishes only dirty entities/columns |
174
176
  | `@oasys/oecs/editor` | undo/redo + field-handle layer over the host-write seam |
175
177
  | `@oasys/oecs/solid` | SolidJS adapter (`solid-js` is an **optional** peer dependency) |
176
178
  | `@oasys/oecs/primitives` | the standalone data structures oecs is built on |
179
+ | `@oasys/oecs/internal` | unstable internals (codecs, ABI constants, access checker) — **no semver guarantees** |
177
180
 
178
181
  ## Dev vs prod
179
182
 
180
183
  A compile-time `__DEV__` flag gates every runtime check — bounds and liveness checks, duplicate-system
181
184
  detection, registration validation, and the system access checker (`reads`/`writes`). These are
182
185
  **tree-shaken out of production builds**, so treat "throws in dev" as a development tripwire, not a
183
- production guarantee. The scheduler's cycle detection is the one check that is always active.
186
+ production guarantee. The scheduler's cycle detection and constructor-option validation (timestep,
187
+ memory options, relation cardinality) are always active.
188
+
189
+ **Production is the default on both channels; you opt *into* the guards.** On **npm**, `@oasys/oecs`
190
+ is the stripped production build — dev-mode bundlers (`vite dev`, `webpack --mode development`) pick
191
+ the guards-on build automatically via the `development` export condition, or import `@oasys/oecs/dev`
192
+ for it directly. On **JSR/Deno** (raw source, no bundler) the default is also production
193
+ (`__DEV__ = false`); set `globalThis.__DEV__ = true` before the first import to turn the guards on
194
+ while developing. Full details — including the browser/CDN and manual-override paths — are in the
195
+ [**Development guards & production builds**](docs/PRODUCTION.md) guide.
184
196
 
185
197
  ## Documentation
186
198
 
@@ -1,7 +1,7 @@
1
1
  import { Brand, AnyTypedArray, ColumnBacking, TypedArrayTag, BitSet } from '../../type_primitives/index.cjs';
2
2
  import { ColumnStore } from '../store/column_store.cjs';
3
3
  import { ComponentID, ComponentDef, ComponentSchema, SchemaOf, DeclaredQueryTerm, TagToTypedArray, ColumnsForSchema, MutableColumnsForSchema, ReadonlyColumn } from './component.cjs';
4
- import { EntityID, ReadonlyEntityIdArray } from './entity.cjs';
4
+ import { EntityID, ReadonlyEntityIDArray } from './entity.cjs';
5
5
  export type ArchetypeID = Brand<number, "archetype_id">;
6
6
  export declare const asArchetypeId: (value: number) => ArchetypeID;
7
7
  export interface ArchetypeEdge {
@@ -86,7 +86,7 @@ export interface ArchetypeView<out Defs extends readonly ComponentDef<any>[] = r
86
86
  readonly disabledCount: number;
87
87
  /** Raw entity ID buffer (packed `EntityID`s). Valid data at indices
88
88
  * 0..totalCount-1 (enabled rows first, then disabled). */
89
- readonly entityIds: ReadonlyEntityIdArray;
89
+ readonly entityIds: ReadonlyEntityIDArray;
90
90
  /** True if this archetype's mask includes the given component. */
91
91
  hasComponent(id: ComponentID): boolean;
92
92
  /** Get a single field's column (read-only). Valid data: indices
@@ -243,7 +243,7 @@ export declare class Archetype implements ArchetypeView {
243
243
  get disabledCount(): number;
244
244
  /** Raw entity ID buffer (packed `EntityID`s). Valid data at indices
245
245
  * 0..totalCount-1 (enabled rows first, then disabled). */
246
- get entityIds(): ReadonlyEntityIdArray;
246
+ get entityIds(): ReadonlyEntityIDArray;
247
247
  /**
248
248
  * Swap the two rows `a` and `b` — entity ids and every column. Does NOT touch
249
249
  * `entityRow`; the caller updates the entity→row map for whichever rows it
@@ -1,7 +1,7 @@
1
1
  import { Brand, AnyTypedArray, ColumnBacking, TypedArrayTag, BitSet } from '../../type_primitives/index.js';
2
2
  import { ColumnStore } from '../store/column_store.js';
3
3
  import { ComponentID, ComponentDef, ComponentSchema, SchemaOf, DeclaredQueryTerm, TagToTypedArray, ColumnsForSchema, MutableColumnsForSchema, ReadonlyColumn } from './component.js';
4
- import { EntityID, ReadonlyEntityIdArray } from './entity.js';
4
+ import { EntityID, ReadonlyEntityIDArray } from './entity.js';
5
5
  export type ArchetypeID = Brand<number, "archetype_id">;
6
6
  export declare const asArchetypeId: (value: number) => ArchetypeID;
7
7
  export interface ArchetypeEdge {
@@ -86,7 +86,7 @@ export interface ArchetypeView<out Defs extends readonly ComponentDef<any>[] = r
86
86
  readonly disabledCount: number;
87
87
  /** Raw entity ID buffer (packed `EntityID`s). Valid data at indices
88
88
  * 0..totalCount-1 (enabled rows first, then disabled). */
89
- readonly entityIds: ReadonlyEntityIdArray;
89
+ readonly entityIds: ReadonlyEntityIDArray;
90
90
  /** True if this archetype's mask includes the given component. */
91
91
  hasComponent(id: ComponentID): boolean;
92
92
  /** Get a single field's column (read-only). Valid data: indices
@@ -243,7 +243,7 @@ export declare class Archetype implements ArchetypeView {
243
243
  get disabledCount(): number;
244
244
  /** Raw entity ID buffer (packed `EntityID`s). Valid data at indices
245
245
  * 0..totalCount-1 (enabled rows first, then disabled). */
246
- get entityIds(): ReadonlyEntityIdArray;
246
+ get entityIds(): ReadonlyEntityIDArray;
247
247
  /**
248
248
  * Swap the two rows `a` and `b` — entity ids and every column. Does NOT touch
249
249
  * `entityRow`; the caller updates the entity→row map for whichever rows it
@@ -142,6 +142,31 @@ export interface Bundle<S extends ComponentSchema = ComponentSchema> {
142
142
  }
143
143
  /** Either a populated bundle or a bare def (tag / all-fields-zero). */
144
144
  export type BundleOrDef<S extends ComponentSchema = ComponentSchema> = Bundle<S> | ComponentDef<S>;
145
+ /** Re-validate one bundle-or-def item against its OWN def's schema. A bare def
146
+ * (the callable) passes as-is; a bundle is re-stated as `Bundle<S>` for its
147
+ * def's `S`, so a hand-written `{ def, values }` literal whose fields don't
148
+ * match the def is rejected — closing the raw-literal leak that `bundle(Pos,…)`
149
+ * / `Pos(…)` never had (those validate at their own call site). Per-element
150
+ * mapper for `StrictBundles`. */
151
+ export type StrictBundle<T> = T extends ComponentDef ? T : T extends {
152
+ def: ComponentDef<infer S>;
153
+ } ? Bundle<S> : never;
154
+ /** Mapped tuple over a bundle-or-def varargs list — each element re-checked
155
+ * against its own def's schema. The SolidJS-`on` per-element pattern (pull the
156
+ * tuple apart, map every element), over callable bundles instead of
157
+ * `{ def, values }` entry-objects. The one strictness mechanism shared by
158
+ * `spawnBundle`, `addComponents`, and `template`. */
159
+ export type StrictBundles<Items extends readonly BundleOrDef[]> = {
160
+ [K in keyof Items]: StrictBundle<Items[K]>;
161
+ };
162
+ /** Recover the def tuple from a bundle-or-def varargs list, so `template(...)`
163
+ * still returns `Template<[Pos, Vel]>` — the typed key set that `spawn`'s
164
+ * `overrides` (`TemplateOverrides`) maps over. Also a per-element tuple map. */
165
+ export type DefsOf<Items extends readonly BundleOrDef[]> = {
166
+ [K in keyof Items]: Items[K] extends {
167
+ def: infer D extends ComponentDef;
168
+ } ? D : Items[K] extends ComponentDef ? Items[K] : never;
169
+ };
145
170
  /** Pair a component def with field values to attach. Omitted fields zero-fill;
146
171
  * a tag def takes no values (see `ValuesArg`). */
147
172
  export declare function bundle<S extends ComponentSchema>(def: ComponentDef<S>, ...values: ValuesArg<S>): Bundle<S>;
@@ -142,6 +142,31 @@ export interface Bundle<S extends ComponentSchema = ComponentSchema> {
142
142
  }
143
143
  /** Either a populated bundle or a bare def (tag / all-fields-zero). */
144
144
  export type BundleOrDef<S extends ComponentSchema = ComponentSchema> = Bundle<S> | ComponentDef<S>;
145
+ /** Re-validate one bundle-or-def item against its OWN def's schema. A bare def
146
+ * (the callable) passes as-is; a bundle is re-stated as `Bundle<S>` for its
147
+ * def's `S`, so a hand-written `{ def, values }` literal whose fields don't
148
+ * match the def is rejected — closing the raw-literal leak that `bundle(Pos,…)`
149
+ * / `Pos(…)` never had (those validate at their own call site). Per-element
150
+ * mapper for `StrictBundles`. */
151
+ export type StrictBundle<T> = T extends ComponentDef ? T : T extends {
152
+ def: ComponentDef<infer S>;
153
+ } ? Bundle<S> : never;
154
+ /** Mapped tuple over a bundle-or-def varargs list — each element re-checked
155
+ * against its own def's schema. The SolidJS-`on` per-element pattern (pull the
156
+ * tuple apart, map every element), over callable bundles instead of
157
+ * `{ def, values }` entry-objects. The one strictness mechanism shared by
158
+ * `spawnBundle`, `addComponents`, and `template`. */
159
+ export type StrictBundles<Items extends readonly BundleOrDef[]> = {
160
+ [K in keyof Items]: StrictBundle<Items[K]>;
161
+ };
162
+ /** Recover the def tuple from a bundle-or-def varargs list, so `template(...)`
163
+ * still returns `Template<[Pos, Vel]>` — the typed key set that `spawn`'s
164
+ * `overrides` (`TemplateOverrides`) maps over. Also a per-element tuple map. */
165
+ export type DefsOf<Items extends readonly BundleOrDef[]> = {
166
+ [K in keyof Items]: Items[K] extends {
167
+ def: infer D extends ComponentDef;
168
+ } ? D : Items[K] extends ComponentDef ? Items[K] : never;
169
+ };
145
170
  /** Pair a component def with field values to attach. Omitted fields zero-fill;
146
171
  * a tag def takes no values (see `ValuesArg`). */
147
172
  export declare function bundle<S extends ComponentSchema>(def: ComponentDef<S>, ...values: ValuesArg<S>): Bundle<S>;
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/component.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Template, TemplateEntries, TemplateOverrides } from './store.cjs';
1
+ import { Template, TemplateOverrides } from './store.cjs';
2
2
  import { FrameTraceSink } from './frame_trace.cjs';
3
3
  import { ObserverHandle, EntitySetObserverConfig, ArchetypeSetObserverConfig, StructuralObserverConfig } from './observer.cjs';
4
4
  import { ColumnStore, ColumnStoreRegionHandle, StoreRegionSpec } from '../store/index.cjs';
@@ -8,7 +8,7 @@ import { Archetype, ArchetypeID } from './archetype.cjs';
8
8
  import { SystemContext, Query, QueryBuilder, QueryCache, QueryResolver } from './query.cjs';
9
9
  import { EntityID } from './entity.cjs';
10
10
  import { ReadonlyComponentRef } from './ref.cjs';
11
- import { ComponentDef, ComponentHandle, ComponentRegisterOptions, ComponentSchema, CompleteFieldValues, Bundle, BundleOrDef } from './component.cjs';
11
+ import { ComponentDef, ComponentHandle, ComponentRegisterOptions, ComponentSchema, CompleteFieldValues, Bundle, BundleOrDef, StrictBundles, DefsOf } from './component.cjs';
12
12
  import { SparseComponentDef, SparseComponentID } from './sparse_store.cjs';
13
13
  import { RelationDef } from './relation.cjs';
14
14
  import { SystemFn, SystemConfig, SystemDescriptor, TypedSystemConfig, DenseAccessDecl, SpawnsAccessDecl, DespawnsAccessDecl, TransitionsAccessDecl, SparseAccessDecl, RelationsAccessDecl, ResourcesAccessDecl } from './system.cjs';
@@ -193,22 +193,40 @@ export declare class ECS implements QueryResolver {
193
193
  spawn<Defs extends readonly ComponentDef[]>(template: Template<Defs>, overrides?: TemplateOverrides<Defs>): EntityID;
194
194
  /**
195
195
  * Spawn an entity from varargs bundles (§bundles) — the immediate
196
- * host-side analog of `ctx.commands.spawn`. `ecs.spawnBundle(bundle(Pos,{x,y}),
197
- * bundle(Vel,{vx:1}), IsEnemy)` collapses the five attach shapes into one. Each
198
- * bundle is applied immediately; a single combined-archetype insertion (one
199
- * transition instead of one-per-component) is a later optimization for the
200
- * prototype this mirrors the existing per-component `addComponent` path.
196
+ * host-side analog of `ctx.commands.spawn`, and the same callable-bundle
197
+ * grammar as `addComponents` / `template`. `ecs.spawnBundle(Pos({x,y}),
198
+ * Vel({vx:1}), IsEnemy)` collapses the attach shapes into one; each item is
199
+ * checked against its own def's schema (`StrictBundles`). Bundles are applied
200
+ * immediately; a single combined-archetype insertion (one transition instead
201
+ * of one-per-component) is a later optimization — for now this mirrors the
202
+ * per-component `addComponent` path (unlike `addComponents`, which batches).
203
+ *
204
+ * Immediate — inside a system use the deferred `ctx.commands.spawn(...)`
205
+ * (calling this from a system body throws in DEV). Note the redirect trades
206
+ * timing: `commands.spawn` returns the id now but defers the attaches to the
207
+ * phase flush, so the entity sits in its empty/partial archetype until then —
208
+ * unlike `spawnBundle`'s immediate, fully-populated archetype.
201
209
  */
202
- spawnBundle(...items: BundleOrDef[]): EntityID;
210
+ spawnBundle<Items extends readonly BundleOrDef[]>(...items: StrictBundles<Items>): EntityID;
211
+ /** Bulk-spawn `count` entities from `template`, optionally applying one
212
+ * shared `overrides` object to every spawned row (same typed keys as
213
+ * `spawn`). Field writes are O(columns) (one `TypedArray.fill` per
214
+ * column), not O(count×columns). Returns the new ids in spawn order.
215
+ * Immediate — inside a system use `ctx.commands.spawn` per entity (calling
216
+ * this from a system body throws in DEV). */
217
+ spawnMany<Defs extends readonly ComponentDef[]>(template: Template<Defs>, count: number, overrides?: TemplateOverrides<Defs>): EntityID[];
203
218
  /** DEV-only: throw when an *immediate* host structural mutator is called
204
219
  * from inside one of THIS world's system bodies (or an observer / onAdded
205
220
  * hook — they run in the same access spans). One rule for every host
206
- * mutator, not just despawn: an immediate structural op mid-schedule can
207
- * move or swap rows a running query is walking, and it is invisible to
208
- * observers. The archetype-level `_iterDepth` guard only catches mutations
209
- * touching the archetype currently being iterated an op landing elsewhere
210
- * would silently skip observers, so the receiver rule ("inside a system,
211
- * use ctx.commands") is enforced wholesale here.
221
+ * structural mutator despawn, add/remove(Components), batchAdd/Remove,
222
+ * disable/enable, AND the spawn family (spawn/spawnBundle/spawnMany): an
223
+ * immediate structural op mid-schedule can move or swap rows a running query
224
+ * is walking or, for a spawn-append into that archetype, trip a column
225
+ * realloc under it and it is invisible to observers. The archetype-level
226
+ * `_iterDepth` guard only catches mutations touching the archetype currently
227
+ * being iterated (and the append paths skip even that), so an op landing
228
+ * elsewhere would silently skip observers; the receiver rule ("inside a
229
+ * system, use ctx.commands") is enforced wholesale here.
212
230
  *
213
231
  * `_updating` scopes the guard to THIS world: the accessCheck slot is
214
232
  * process-global, so without it a system of world A mutating world B (a
@@ -220,11 +238,11 @@ export declare class ECS implements QueryResolver {
220
238
  * `ctx.commands.despawn` (applied at the phase flush); calling this from
221
239
  * a system body throws in DEV, since an immediate destroy mid-iteration
222
240
  * can invalidate rows the running query is walking. */
223
- despawn(id: EntityID): void;
224
- /** Disable `id` (idempotent). Excluded from default queries until re-enabled. */
225
- disable(id: EntityID): this;
226
- /** Re-enable a disabled `id` (idempotent). */
227
- enable(id: EntityID): this;
241
+ despawn(entityId: EntityID): this;
242
+ /** Disable `entityId` (idempotent). Excluded from default queries until re-enabled. */
243
+ disable(entityId: EntityID): this;
244
+ /** Re-enable a disabled `entityId` (idempotent). */
245
+ enable(entityId: EntityID): this;
228
246
  /**
229
247
  * Attach a component to an entity, immediately (inside a system, use the
230
248
  * deferred `ctx.commands.add`). Three shapes: a bare def attaches a tag; a
@@ -240,13 +258,16 @@ export declare class ECS implements QueryResolver {
240
258
  addComponent(entityId: EntityID, def: ComponentDef<Record<string, never>>): this;
241
259
  addComponent<S extends ComponentSchema>(entityId: EntityID, bundle: Bundle<S>): this;
242
260
  addComponent<S extends ComponentSchema>(entityId: EntityID, def: ComponentDef<S>, values: CompleteFieldValues<S>): this;
243
- /** Batch-attach several components in one archetype transition. Each
244
- * entry's `values` is checked against its own def's schema (a misspelled
245
- * field is a compile error; tags refuse `values`)same typing as
246
- * `ECS.template` entries. Omitted fields zero-fill. */
247
- addComponents<Defs extends readonly ComponentDef[]>(entityId: EntityID, entries: TemplateEntries<Defs>): this;
261
+ /** Batch-attach several components in one archetype transition. Takes the
262
+ * same callable-bundle varargs as `spawnBundle` `world.addComponents(e,
263
+ * Pos({ x, y }), Vel({ vx }), Frozen)` — each item checked against its own
264
+ * def's schema (a misspelled or cross-component field is a compile error;
265
+ * tags refuse values). Omitted fields zero-fill. */
266
+ addComponents<Items extends readonly BundleOrDef[]>(entityId: EntityID, ...items: StrictBundles<Items>): this;
248
267
  removeComponent(entityId: EntityID, def: ComponentDef): this;
249
- removeComponents(entityId: EntityID, defs: ComponentDef[]): this;
268
+ /** Detach several components in one archetype transition — the varargs
269
+ * mirror of `addComponents` (bare defs; removing needs no values). */
270
+ removeComponents(entityId: EntityID, ...defs: ComponentDef[]): this;
250
271
  /**
251
272
  * Bulk add a component to ALL entities in the given archetype.
252
273
  * O(columns) via TypedArray.set() instead of O(N×columns).
@@ -351,7 +372,7 @@ export declare class ECS implements QueryResolver {
351
372
  * access record. Escape hatch: annotate `fn(ctx: SystemContext, dt)`
352
373
  * explicitly to keep a system permissive at compile time. */
353
374
  registerSystem<R extends DenseAccessDecl, W extends DenseAccessDecl, Sp extends SpawnsAccessDecl = readonly never[], De extends DespawnsAccessDecl = readonly never[], Tr extends TransitionsAccessDecl = readonly never[], SR extends SparseAccessDecl = readonly never[], SW extends SparseAccessDecl = readonly never[], RR extends RelationsAccessDecl = readonly never[], RW extends RelationsAccessDecl = readonly never[], QR extends ResourcesAccessDecl = readonly never[], QW extends ResourcesAccessDecl = readonly never[]>(config: TypedSystemConfig<R, W, Sp, De, Tr, SR, SW, RR, RW, QR, QW>): SystemDescriptor;
354
- removeSystem(system: SystemDescriptor): void;
375
+ removeSystem(system: SystemDescriptor): this;
355
376
  get systemCount(): number;
356
377
  /**
357
378
  * Run the startup phases, once, before the first `update()`. Prewarms
@@ -393,6 +414,23 @@ export declare class ECS implements QueryResolver {
393
414
  */
394
415
  update(dt: number): void;
395
416
  dispose(): void;
417
+ /** Register an archetype template (#462). Resolves the component set +
418
+ * default field values to a target archetype once (creating it if absent —
419
+ * fits the prewarm model), so later `spawn` / `spawnMany` calls land
420
+ * entities directly in that archetype with **zero archetype transitions**.
421
+ *
422
+ * const Bullet = ecs.template(Position({ x: 0, y: 0 }), Velocity({ vx: 0, vy: 0 }));
423
+ *
424
+ * Takes the same callable-bundle varargs as `spawnBundle` / `addComponents`
425
+ * (each item schema-checked against its own def); the resulting
426
+ * `Template<[Position, Velocity]>` keeps the typed key set that `spawn`'s
427
+ * `overrides` map over. Not a pass-through — it normalizes bundles to the
428
+ * store's entry shape, so it lives here with the other real logic, not in the
429
+ * delegation band. The big win is multi-component entities and bulk spawns; a
430
+ * single-component spawn is no faster than `spawn` + `addComponent`, which
431
+ * already bump-allocates a fresh entity into the target archetype. See
432
+ * ADR-0010. */
433
+ template<Items extends readonly BundleOrDef[]>(...items: StrictBundles<Items>): Template<DefsOf<Items>>;
396
434
  /** Resolve a consumer-declared SAB region's byte offset by `region_id`, or
397
435
  * 0 when absent. Generic, de-gamed replacement (#623) for the removed
398
436
  * game-named accessors; pair with the consumer's own region module to
@@ -430,32 +468,12 @@ export declare class ECS implements QueryResolver {
430
468
  registerTag(): ComponentDef<Record<string, never>>;
431
469
  /** Register a sparse tag (empty schema) — membership only, no data. */
432
470
  registerSparseTag(): SparseComponentDef<Record<string, never>>;
433
- /** Register an archetype template (#462). Resolves the component set +
434
- * default field values to a target archetype once (creating it if absent —
435
- * fits the prewarm model), so later `spawn` / `spawnMany` calls land
436
- * entities directly in that archetype with **zero archetype transitions**.
437
- *
438
- * const Bullet = ecs.template([
439
- * { def: Position, values: { x: 0, y: 0 } },
440
- * { def: Velocity, values: { vx: 0, vy: 0 } },
441
- * ]);
442
- *
443
- * The big win is multi-component entities and bulk spawns; a single-
444
- * component spawn is no faster than `spawn` + `addComponent`, which
445
- * already bump-allocates a fresh entity into the target archetype. See
446
- * ADR-0010. */
447
- template<Defs extends readonly ComponentDef[]>(entries: TemplateEntries<Defs>): Template<Defs>;
448
- /** Bulk-spawn `count` entities from `template`, optionally applying one
449
- * shared `overrides` object to every spawned row (same typed keys as
450
- * `spawn`). Field writes are O(columns) (one `TypedArray.fill` per
451
- * column), not O(count×columns). Returns the new ids in spawn order. */
452
- spawnMany<Defs extends readonly ComponentDef[]>(template: Template<Defs>, count: number, overrides?: TemplateOverrides<Defs>): EntityID[];
453
- isAlive(id: EntityID): boolean;
471
+ isAlive(entityId: EntityID): boolean;
454
472
  get entityCount(): number;
455
473
  hasComponent(entityId: EntityID, def: ComponentDef): boolean;
456
- /** Whether `id` is currently disabled. Toggle via `disable` / `enable`
474
+ /** Whether `entityId` is currently disabled. Toggle via `disable` / `enable`
457
475
  * (immediate, above the band — they carry the in-system dev guard). */
458
- isDisabled(id: EntityID): boolean;
476
+ isDisabled(entityId: EntityID): boolean;
459
477
  addSparse(entityId: EntityID, def: SparseComponentDef<Record<string, never>>): this;
460
478
  addSparse<S extends ComponentSchema>(entityId: EntityID, def: SparseComponentDef<S>, values: CompleteFieldValues<S>): this;
461
479
  removeSparse(entityId: EntityID, def: SparseComponentDef): this;